using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

/// <summary>
/// Shared utility for loading Addressable assets with automatic retry on failure.
/// All Addressable loads go through remote CDN and can fail due to transient network issues.
/// Without retries a single failed download permanently breaks the affected feature for the
/// session.
/// </summary>
public static class AddressableLoader {
    private const int DefaultMaxRetries = 3;
    private static readonly float[] RetryDelays = { 2f, 5f, 10f };

    /// <summary>
    /// Loads all assets matching a label, retrying on transient failures.
    /// Calls onSuccess with the loaded assets on success, or onFailure (if provided) on final
    /// failure.
    /// </summary>
    public static IEnumerator LoadWithRetry<T>(
            string label,
            Action<IList<T>> onSuccess,
            Action onFailure = null,
            int maxRetries = DefaultMaxRetries) {
        for (int attempt = 0; attempt <= maxRetries; attempt++) {
            var handle = Addressables.LoadAssetsAsync<T>(label, null);
            yield return handle;

            if (handle.Status == AsyncOperationStatus.Succeeded) {
                onSuccess(handle.Result);
                yield break;
            }

            Addressables.Release(handle);
            var error = handle.OperationException?.Message ?? "Unknown error";

            if (attempt < maxRetries) {
                float delay = attempt < RetryDelays.Length ? RetryDelays[attempt] : RetryDelays[^1];
                Debug.LogWarning(
                        $"Failed to load '{label}' (attempt {attempt + 1}/{maxRetries + 1}): " +
                        $"{error}. Retrying in {delay}s...");
                yield return new WaitForSeconds(delay);
            } else {
                Debug.LogError(
                        $"Failed to load '{label}' after {maxRetries + 1} attempts: {error}");
                onFailure?.Invoke();
            }
        }
    }
}
