using System;
using System.Collections.Generic;
using UnityEngine;

namespace Auth {
    /// <summary>
    /// Represents a stored OAuth account with tokens.
    /// </summary>
    [Serializable]
    public class StoredAccount {
        public string Provider;     // "discord" or "google"
        public string UserId;       // OAuth provider user ID
        public string DisplayName;  // User's display name
        public string AccessToken;
        public string RefreshToken;
        public long ExpiresAt;  // Unix timestamp
        public bool IsAdmin;

        public string AccountKey => $"{Provider}:{UserId}";

        public bool HasValidToken => !string.IsNullOrEmpty(AccessToken) &&
                                     ExpiresAt > DateTimeOffset.UtcNow.ToUnixTimeSeconds();

        public bool HasRefreshToken => !string.IsNullOrEmpty(RefreshToken);

        public bool NeedsRefresh {
            get {
                var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
                return ExpiresAt > 0 && ExpiresAt - now < 300;  // 5 minutes
            }
        }
    }

    /// <summary>
    /// Container for all stored accounts, serialized to JSON.
    /// </summary>
    [Serializable]
    public class StoredAccountsData {
        public List<StoredAccount> Accounts = new();
        public string CurrentAccountKey;  // Provider:UserId of active account
    }

    /// <summary>
    /// Secure storage for OAuth tokens using Unity's PlayerPrefs.
    /// Supports multiple accounts - tokens are preserved on logout.
    /// Values are cached in memory to allow access from background threads.
    /// In production, consider using platform-specific secure storage
    /// (Keychain on iOS, Keystore on Android).
    /// </summary>
    public static class TokenStorage {
        private const string AccountsDataKey = "eagle0_accounts_data";

        // Legacy keys for migration
        private const string LegacyAccessTokenKey = "eagle0_access_token";
        private const string LegacyRefreshTokenKey = "eagle0_refresh_token";
        private const string LegacyExpiresAtKey = "eagle0_expires_at";
        private const string LegacyUserIdKey = "eagle0_user_id";
        private const string LegacyDisplayNameKey = "eagle0_display_name";

        // In-memory cache
        private static StoredAccountsData _accountsData;
        private static bool _cacheInitialized = false;

        /// <summary>
        /// Initialize cache from PlayerPrefs. Must be called from main thread.
        /// Call this early in app startup (e.g., in Awake or Start).
        /// </summary>
        public static void InitializeCache() {
            var json = PlayerPrefs.GetString(AccountsDataKey, "");

            if (!string.IsNullOrEmpty(json)) {
                try {
                    _accountsData = JsonUtility.FromJson<StoredAccountsData>(json);
                } catch (Exception ex) {
                    Debug.LogWarning($"[TokenStorage] Failed to parse accounts data: {ex.Message}");
                    _accountsData = new StoredAccountsData();
                }
            } else {
                _accountsData = new StoredAccountsData();
                // Try to migrate legacy single-account data
                MigrateLegacyData();
            }

            _cacheInitialized = true;
        }

        private static void MigrateLegacyData() {
            var legacyAccessToken = PlayerPrefs.GetString(LegacyAccessTokenKey, "");
            if (string.IsNullOrEmpty(legacyAccessToken)) return;

            var legacyRefreshToken = PlayerPrefs.GetString(LegacyRefreshTokenKey, "");
            var legacyExpiresAt =
                    long.TryParse(PlayerPrefs.GetString(LegacyExpiresAtKey, "0"), out var val) ? val
                                                                                               : 0;
            var legacyUserId = PlayerPrefs.GetString(LegacyUserIdKey, "");
            var legacyDisplayName = PlayerPrefs.GetString(LegacyDisplayNameKey, "");

            if (!string.IsNullOrEmpty(legacyUserId)) {
                // Assume discord for legacy data (most common)
                var account = new StoredAccount {
                    Provider = "discord",
                    UserId = legacyUserId,
                    DisplayName = legacyDisplayName,
                    AccessToken = legacyAccessToken,
                    RefreshToken = legacyRefreshToken,
                    ExpiresAt = legacyExpiresAt
                };
                _accountsData.Accounts.Add(account);
                _accountsData.CurrentAccountKey = account.AccountKey;
                SaveAccountsData();

                // Clear legacy keys
                PlayerPrefs.DeleteKey(LegacyAccessTokenKey);
                PlayerPrefs.DeleteKey(LegacyRefreshTokenKey);
                PlayerPrefs.DeleteKey(LegacyExpiresAtKey);
                PlayerPrefs.DeleteKey(LegacyUserIdKey);
                PlayerPrefs.DeleteKey(LegacyDisplayNameKey);
                PlayerPrefs.Save();

                Debug.Log($"[TokenStorage] Migrated legacy account: {account.DisplayName}");
            }
        }

        private static void SaveAccountsData() {
            var json = JsonUtility.ToJson(_accountsData);
            PlayerPrefs.SetString(AccountsDataKey, json);
            PlayerPrefs.Save();
        }

        /// <summary>
        /// Get all stored accounts.
        /// </summary>
        public static IReadOnlyList<StoredAccount> GetAllAccounts() {
            EnsureInitialized();
            return _accountsData.Accounts.AsReadOnly();
        }

        /// <summary>
        /// Get the currently active account, or null if none selected.
        /// </summary>
        public static StoredAccount CurrentAccount {
            get {
                EnsureInitialized();
                if (string.IsNullOrEmpty(_accountsData.CurrentAccountKey)) return null;
                return _accountsData.Accounts.Find(
                        a => a.AccountKey == _accountsData.CurrentAccountKey);
            }
        }

        /// <summary>
        /// Select an account as the current active account.
        /// </summary>
        public static void SelectAccount(string accountKey) {
            EnsureInitialized();
            var account = _accountsData.Accounts.Find(a => a.AccountKey == accountKey);
            if (account != null) {
                _accountsData.CurrentAccountKey = accountKey;
                SaveAccountsData();
                Debug.Log($"[TokenStorage] Selected account: {account.DisplayName}");
            }
        }

        /// <summary>
        /// Clear the current account selection (logout without deleting tokens).
        /// </summary>
        public static void ClearCurrentAccount() {
            EnsureInitialized();
            _accountsData.CurrentAccountKey = null;
            SaveAccountsData();
            Debug.Log("[TokenStorage] Cleared current account selection");
        }

        /// <summary>
        /// Remove a specific account and its tokens.
        /// </summary>
        public static void RemoveAccount(string accountKey) {
            EnsureInitialized();
            var removed = _accountsData.Accounts.RemoveAll(a => a.AccountKey == accountKey);
            if (_accountsData.CurrentAccountKey == accountKey) {
                _accountsData.CurrentAccountKey = null;
            }
            if (removed > 0) {
                SaveAccountsData();
                Debug.Log($"[TokenStorage] Removed account: {accountKey}");
            }
        }

        // Compatibility properties that reference the current account

        public static bool HasValidToken => CurrentAccount?.HasValidToken ?? false;

        public static bool HasRefreshToken => CurrentAccount?.HasRefreshToken ?? false;

        public static string AccessToken => CurrentAccount?.AccessToken;

        public static string RefreshToken => CurrentAccount?.RefreshToken;

        public static long ExpiresAt => CurrentAccount?.ExpiresAt ?? 0;

        public static string UserId => CurrentAccount?.UserId;

        public static string DisplayName => CurrentAccount?.DisplayName;

        public static bool IsAdmin => CurrentAccount?.IsAdmin ?? false;

        public static bool NeedsRefresh => CurrentAccount?.NeedsRefresh ?? false;

        /// <summary>
        /// Store tokens for an account. Creates new or updates existing.
        /// </summary>
        public static void StoreTokens(
                string accessToken,
                string refreshToken,
                long expiresAt,
                string userId,
                string displayName,
                string provider = "discord",
                bool isAdmin = false) {
            EnsureInitialized();

            var accountKey = $"{provider}:{userId}";
            var existingAccount = _accountsData.Accounts.Find(a => a.AccountKey == accountKey);

            if (existingAccount != null) {
                existingAccount.AccessToken = accessToken;
                existingAccount.RefreshToken = refreshToken;
                existingAccount.ExpiresAt = expiresAt;
                existingAccount.DisplayName = displayName;
                existingAccount.IsAdmin = isAdmin;
            } else {
                var newAccount = new StoredAccount {
                    Provider = provider,
                    UserId = userId,
                    DisplayName = displayName,
                    AccessToken = accessToken,
                    RefreshToken = refreshToken,
                    ExpiresAt = expiresAt,
                    IsAdmin = isAdmin
                };
                _accountsData.Accounts.Add(newAccount);
            }

            // Set as current account
            _accountsData.CurrentAccountKey = accountKey;
            SaveAccountsData();

            Debug.Log($"[TokenStorage] Stored tokens for {displayName} ({provider})");
        }

        /// <summary>
        /// Update access token after refresh for the current account.
        /// </summary>
        public static void UpdateAccessToken(string accessToken, long expiresAt) {
            EnsureInitialized();
            var account = CurrentAccount;
            if (account != null) {
                account.AccessToken = accessToken;
                account.ExpiresAt = expiresAt;
                SaveAccountsData();
                Debug.Log($"[TokenStorage] Updated access token (expires at {expiresAt})");
            }
        }

        /// <summary>
        /// Update access token for a specific account (by key).
        /// </summary>
        public static void
        UpdateAccessTokenForAccount(string accountKey, string accessToken, long expiresAt) {
            EnsureInitialized();
            var account = _accountsData.Accounts.Find(a => a.AccountKey == accountKey);
            if (account != null) {
                account.AccessToken = accessToken;
                account.ExpiresAt = expiresAt;
                SaveAccountsData();
                Debug.Log(
                        $"[TokenStorage] Updated access token for {account.DisplayName} (expires at {expiresAt})");
            }
        }

        /// <summary>
        /// Update display name for the current account.
        /// </summary>
        public static void UpdateDisplayName(string displayName) {
            EnsureInitialized();
            var account = CurrentAccount;
            if (account != null) {
                account.DisplayName = displayName;
                SaveAccountsData();
            }
        }

        /// <summary>
        /// Clear all stored accounts (full reset).
        /// </summary>
        public static void ClearAll() {
            _accountsData = new StoredAccountsData();
            PlayerPrefs.DeleteKey(AccountsDataKey);
            PlayerPrefs.Save();
            Debug.Log("[TokenStorage] Cleared all accounts");
        }

        /// <summary>
        /// Legacy Clear method - now just clears current selection, not tokens.
        /// </summary>
        public static void Clear() { ClearCurrentAccount(); }

        private static void EnsureInitialized() {
            if (!_cacheInitialized) {
                Debug.LogWarning("[TokenStorage] Cache not initialized, initializing now");
                InitializeCache();
            }
        }
    }
}
