using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization;
using TaxBaik.Application.Services;

namespace TaxBaik.Web.Services;

public class CustomAuthenticationStateProvider : AuthenticationStateProvider
{
    private readonly ILocalStorageService _localStorage;
    private readonly ITokenStore _tokenStore;
    private readonly IApiClient _apiClient;
    private readonly ILogger<CustomAuthenticationStateProvider> _logger;

    public CustomAuthenticationStateProvider(
        ILocalStorageService localStorage,
        ITokenStore tokenStore,
        IApiClient apiClient,
        ILogger<CustomAuthenticationStateProvider> logger)
    {
        _localStorage = localStorage;
        _tokenStore = tokenStore;
        _apiClient = apiClient;
        _logger = logger;
    }

    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        try
        {
            var accessToken = _tokenStore.AccessToken;

            // TokenStore가 비어있으면 localStorage에서 복원 (페이지 리로드 후)
            if (string.IsNullOrEmpty(accessToken))
            {
                var storedToken = await _localStorage.GetItemAsStringAsync("accessToken");
                if (!string.IsNullOrEmpty(storedToken))
                {
                    var refreshToken = await _localStorage.GetItemAsStringAsync("refreshToken");
                    var ticksStr = await _localStorage.GetItemAsStringAsync("tokenExpiry");
                    if (TryNormalizeExpiryTicks(ticksStr, out var ticks))
                    {
                        _tokenStore.AccessToken = storedToken;
                        _tokenStore.RefreshToken = refreshToken;
                        _tokenStore.TokenExpiryTicks = ticks;
                        accessToken = storedToken;
                    }
                }
            }

            if (string.IsNullOrEmpty(_tokenStore.AccessToken))
            {
                return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
            }

            // 토큰이 만료되면 로그아웃
            if (_tokenStore.IsAccessTokenExpired())
            {
                _logger.LogWarning("Access token 만료됨 - 자동 로그아웃");
                await LogoutAsync();
                return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
            }

            // 토큰이 5분 이내로 만료되면 자동 갱신 시도 (사용자 경험 향상)
            if (!string.IsNullOrEmpty(_tokenStore.RefreshToken) && ShouldRefreshToken())
            {
                _logger.LogInformation("토큰 만료 5분 전 - 자동 갱신 시작");
                var request = new { RefreshToken = _tokenStore.RefreshToken };
                var newTokenPair = await _apiClient.PostAsync<WasmAuthTokenPair>("auth/refresh", request);
                if (newTokenPair != null && !string.IsNullOrEmpty(newTokenPair.AccessToken))
                {
                    await LoginAsync(newTokenPair.AccessToken, newTokenPair.RefreshToken, newTokenPair.ExpiresIn);
                    _logger.LogInformation("토큰 자동 갱신 성공");
                    accessToken = newTokenPair.AccessToken;
                }
                else
                {
                    _logger.LogWarning("토큰 자동 갱신 실패 - 로그아웃");
                    await LogoutAsync();
                    return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
                }
            }

            var principal = ValidateTokenWithoutDb(accessToken ?? string.Empty);
            if (principal == null)
            {
                await LogoutAsync();
                return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
            }

            return new AuthenticationState(principal);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "인증 상태 조회 중 오류 발생");
            return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
        }
    }

    private ClaimsPrincipal? ValidateTokenWithoutDb(string token)
    {
        try
        {
            var handler = new JwtSecurityTokenHandler();
            var jwtToken = handler.ReadJwtToken(token);
            var identity = new ClaimsIdentity(jwtToken.Claims, "jwt");
            return new ClaimsPrincipal(identity);
        }
        catch
        {
            return null;
        }
    }


    public async Task LoginAsync(string accessToken, string refreshToken, int expiresIn)
    {
        var tokenExpiryTicks = DateTime.UtcNow.AddSeconds(expiresIn).Ticks;

        // TokenStore에 저장 (DelegatingHandler에서 사용)
        _tokenStore.AccessToken = accessToken;
        _tokenStore.RefreshToken = refreshToken;
        _tokenStore.TokenExpiryTicks = tokenExpiryTicks;

        // localStorage에도 저장 (페이지 리로드 후 복원)
        await _localStorage.SetItemAsStringAsync("accessToken", accessToken);
        await _localStorage.SetItemAsStringAsync("refreshToken", refreshToken);
        await _localStorage.SetItemAsStringAsync("tokenExpiry", tokenExpiryTicks.ToString());

        NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
    }

    private static bool TryNormalizeExpiryTicks(string? rawValue, out long ticks)
    {
        ticks = 0;
        if (!long.TryParse(rawValue, out var parsed))
        {
            return false;
        }

        // Support both legacy Unix-millisecond storage and .NET ticks.
        if (parsed > 10_000_000_000_000L && parsed < 100_000_000_000_000_000L)
        {
            ticks = parsed;
            return true;
        }

        if (parsed > 1_000_000_000_000L && parsed < 100_000_000_000_000L)
        {
            ticks = DateTimeOffset.FromUnixTimeMilliseconds(parsed).UtcDateTime.Ticks;
            return true;
        }

        return false;
    }

    private bool ShouldRefreshToken()
    {
        // 토큰이 5분 이내로 만료되면 갱신 (300초 = 5분)
        if (!_tokenStore.TokenExpiryTicks.HasValue || _tokenStore.TokenExpiryTicks.Value <= 0)
            return false;

        const int refreshThresholdSeconds = 300;
        try
        {
            var expiryTime = new DateTime(_tokenStore.TokenExpiryTicks.Value, DateTimeKind.Utc);
            var timeUntilExpiry = expiryTime - DateTime.UtcNow;
            return timeUntilExpiry.TotalSeconds <= refreshThresholdSeconds && timeUntilExpiry.TotalSeconds > 0;
        }
        catch
        {
            return false;
        }
    }

    public async Task LogoutAsync()
    {
        // TokenStore 초기화
        _tokenStore.Clear();

        // localStorage 초기화
        await _localStorage.RemoveItemAsync("accessToken");
        await _localStorage.RemoveItemAsync("refreshToken");
        await _localStorage.RemoveItemAsync("tokenExpiry");

        NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
    }

    private bool IsTokenExpired(string token)
    {
        try
        {
            var handler = new JwtSecurityTokenHandler();
            var jwtToken = handler.ReadJwtToken(token);
            return jwtToken.ValidTo < DateTime.UtcNow;
        }
        catch
        {
            return true;
        }
    }
}

public class WasmAuthTokenPair
{
    public WasmAuthTokenPair() { }
    public WasmAuthTokenPair(string accessToken, string refreshToken, int expiresIn)
    {
        AccessToken = accessToken;
        RefreshToken = refreshToken;
        ExpiresIn = expiresIn;
    }
    public string AccessToken { get; set; } = "";
    public string RefreshToken { get; set; } = "";
    public int ExpiresIn { get; set; }
}
