using System.Collections.Concurrent;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace MembershipService.Infrastructure.ExternalServices;

/// <summary>
/// EN: HTTP client for communicating with IAM Service with caching and health check.
/// VI: HTTP client để giao tiếp với IAM Service với caching và health check.
/// </summary>
public class HttpIamServiceClient : IIamServiceClient
{
    private readonly HttpClient _httpClient;
    private readonly IamServiceSettings _settings;
    private readonly ILogger<HttpIamServiceClient> _logger;
    
    // EN: In-memory cache for user info / VI: Cache in-memory cho user info
    private readonly ConcurrentDictionary<string, CachedItem<IamUserInfo>> _userCache = new();
    private readonly ConcurrentDictionary<string, CachedItem<IReadOnlyList<string>>> _rolesCache = new();
    private readonly ConcurrentDictionary<string, CachedItem<IReadOnlyList<string>>> _permissionsCache = new();
    
    // EN: Health check cache / VI: Cache health check
    private CachedItem<IamHealthStatus>? _healthCache;
    private readonly object _healthLock = new();

    public HttpIamServiceClient(
        HttpClient httpClient,
        IOptions<IamServiceSettings> settings,
        ILogger<HttpIamServiceClient> logger)
    {
        _httpClient = httpClient;
        _settings = settings.Value;
        _logger = logger;
        
        _httpClient.BaseAddress = new Uri(_settings.BaseUrl);
        _httpClient.Timeout = TimeSpan.FromSeconds(_settings.TimeoutSeconds);
    }

    // ============================================
    // EN: User Operations / VI: Thao tác User
    // ============================================

    /// <inheritdoc />
    public async Task<IamUserInfo?> ValidateUserAsync(
        string accessToken, 
        CancellationToken cancellationToken = default)
    {
        // EN: Check cache first / VI: Kiểm tra cache trước
        var cacheKey = $"token:{accessToken.GetHashCode()}";
        if (TryGetFromCache(_userCache, cacheKey, out var cached))
        {
            _logger.LogDebug("User info retrieved from cache");
            return cached;
        }

        try
        {
            var request = CreateRequest(HttpMethod.Get, "/api/v1/users/me", accessToken);
            var response = await _httpClient.SendAsync(request, cancellationToken);
            
            if (!response.IsSuccessStatusCode)
            {
                _logger.LogWarning("Failed to validate user token. Status: {StatusCode}", response.StatusCode);
                return null;
            }

            var userResponse = await response.Content.ReadFromJsonAsync<UserMeResponse>(cancellationToken);
            
            if (userResponse?.Data == null)
                return null;

            var userInfo = new IamUserInfo(
                userResponse.Data.Id,
                userResponse.Data.Email,
                userResponse.Data.DisplayName,
                true,
                userResponse.Data.Roles ?? new List<string>(),
                userResponse.Data.Permissions);

            // EN: Cache the result / VI: Cache kết quả
            AddToCache(_userCache, cacheKey, userInfo, _settings.CacheDurationSeconds);
            
            return userInfo;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error validating user with IAM Service");
            return null;
        }
    }

    /// <inheritdoc />
    public async Task<IamUserInfo?> GetUserByIdAsync(
        string userId, 
        string accessToken,
        CancellationToken cancellationToken = default)
    {
        // EN: Check cache first / VI: Kiểm tra cache trước
        var cacheKey = $"user:{userId}";
        if (TryGetFromCache(_userCache, cacheKey, out var cached))
        {
            _logger.LogDebug("User {UserId} retrieved from cache", userId);
            return cached;
        }

        try
        {
            var request = CreateRequest(HttpMethod.Get, $"/api/v1/users/{userId}", accessToken);
            var response = await _httpClient.SendAsync(request, cancellationToken);
            
            if (!response.IsSuccessStatusCode)
            {
                _logger.LogWarning("Failed to get user {UserId}. Status: {StatusCode}", userId, response.StatusCode);
                return null;
            }

            var userResponse = await response.Content.ReadFromJsonAsync<UserResponse>(cancellationToken);
            
            if (userResponse?.Data == null)
                return null;

            var userInfo = new IamUserInfo(
                userResponse.Data.Id,
                userResponse.Data.Email,
                userResponse.Data.DisplayName,
                userResponse.Data.IsActive,
                userResponse.Data.Roles ?? new List<string>(),
                userResponse.Data.Permissions);

            // EN: Cache the result / VI: Cache kết quả
            AddToCache(_userCache, cacheKey, userInfo, _settings.CacheDurationSeconds);
            
            return userInfo;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting user {UserId} from IAM Service", userId);
            return null;
        }
    }

    /// <inheritdoc />
    public async Task<bool> UserExistsAsync(
        string userId, 
        string accessToken,
        CancellationToken cancellationToken = default)
    {
        var user = await GetUserByIdAsync(userId, accessToken, cancellationToken);
        return user != null;
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<string>> GetUserRolesAsync(
        string userId,
        string accessToken,
        CancellationToken cancellationToken = default)
    {
        // EN: Check cache first / VI: Kiểm tra cache trước
        var cacheKey = $"roles:{userId}";
        if (TryGetFromCache(_rolesCache, cacheKey, out var cached))
        {
            return cached;
        }

        try
        {
            var request = CreateRequest(HttpMethod.Get, $"/api/v1/users/{userId}/roles", accessToken);
            var response = await _httpClient.SendAsync(request, cancellationToken);
            
            if (!response.IsSuccessStatusCode)
            {
                _logger.LogWarning("Failed to get roles for user {UserId}. Status: {StatusCode}", userId, response.StatusCode);
                return Array.Empty<string>();
            }

            var rolesResponse = await response.Content.ReadFromJsonAsync<RolesResponse>(cancellationToken);
            var roles = rolesResponse?.Data ?? Array.Empty<string>();

            // EN: Cache the result / VI: Cache kết quả
            AddToCache(_rolesCache, cacheKey, roles, _settings.CacheDurationSeconds);
            
            return roles;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting roles for user {UserId}", userId);
            return Array.Empty<string>();
        }
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<string>> GetUserPermissionsAsync(
        string userId,
        string accessToken,
        CancellationToken cancellationToken = default)
    {
        // EN: Check cache first / VI: Kiểm tra cache trước
        var cacheKey = $"permissions:{userId}";
        if (TryGetFromCache(_permissionsCache, cacheKey, out var cached))
        {
            return cached;
        }

        try
        {
            var request = CreateRequest(HttpMethod.Get, $"/api/v1/users/{userId}/permissions", accessToken);
            var response = await _httpClient.SendAsync(request, cancellationToken);
            
            if (!response.IsSuccessStatusCode)
            {
                _logger.LogWarning("Failed to get permissions for user {UserId}. Status: {StatusCode}", userId, response.StatusCode);
                return Array.Empty<string>();
            }

            var permissionsResponse = await response.Content.ReadFromJsonAsync<PermissionsResponse>(cancellationToken);
            var permissions = permissionsResponse?.Data ?? Array.Empty<string>();

            // EN: Cache the result / VI: Cache kết quả
            AddToCache(_permissionsCache, cacheKey, permissions, _settings.CacheDurationSeconds);
            
            return permissions;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting permissions for user {UserId}", userId);
            return Array.Empty<string>();
        }
    }

    /// <inheritdoc />
    public async Task<bool> HasPermissionAsync(
        string userId,
        string permission,
        string accessToken,
        CancellationToken cancellationToken = default)
    {
        var permissions = await GetUserPermissionsAsync(userId, accessToken, cancellationToken);
        return permissions.Contains(permission, StringComparer.OrdinalIgnoreCase);
    }

    /// <inheritdoc />
    public async Task<bool> HasRoleAsync(
        string userId,
        string role,
        string accessToken,
        CancellationToken cancellationToken = default)
    {
        var roles = await GetUserRolesAsync(userId, accessToken, cancellationToken);
        return roles.Contains(role, StringComparer.OrdinalIgnoreCase);
    }

    // ============================================
    // EN: Health Check / VI: Kiểm tra Health
    // ============================================

    /// <inheritdoc />
    public async Task<IamHealthStatus> CheckHealthAsync(CancellationToken cancellationToken = default)
    {
        // EN: Check cache first / VI: Kiểm tra cache trước
        lock (_healthLock)
        {
            if (_healthCache != null && !_healthCache.IsExpired)
            {
                _logger.LogDebug("Health status retrieved from cache");
                return _healthCache.Value;
            }
        }

        try
        {
            var request = new HttpRequestMessage(HttpMethod.Get, "/health");
            var response = await _httpClient.SendAsync(request, cancellationToken);
            
            var healthStatus = new IamHealthStatus(
                response.IsSuccessStatusCode,
                response.IsSuccessStatusCode ? "Healthy" : $"Unhealthy ({response.StatusCode})",
                DateTime.UtcNow);

            // EN: Cache the result / VI: Cache kết quả
            lock (_healthLock)
            {
                _healthCache = new CachedItem<IamHealthStatus>(healthStatus, _settings.HealthCheckCacheDurationSeconds);
            }

            if (!response.IsSuccessStatusCode)
            {
                _logger.LogWarning("IAM Service health check failed. Status: {StatusCode}", response.StatusCode);
            }

            return healthStatus;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error checking IAM Service health");
            
            var unhealthyStatus = new IamHealthStatus(false, $"Error: {ex.Message}", DateTime.UtcNow);
            
            // EN: Cache unhealthy status for shorter duration / VI: Cache trạng thái unhealthy với thời gian ngắn hơn
            lock (_healthLock)
            {
                _healthCache = new CachedItem<IamHealthStatus>(unhealthyStatus, 10); // 10 seconds
            }
            
            return unhealthyStatus;
        }
    }

    /// <inheritdoc />
    public async Task<bool> IsAvailableAsync(CancellationToken cancellationToken = default)
    {
        var health = await CheckHealthAsync(cancellationToken);
        return health.IsHealthy;
    }

    // ============================================
    // EN: Cache Management / VI: Quản lý Cache
    // ============================================

    /// <inheritdoc />
    public void InvalidateUserCache(string userId)
    {
        var keysToRemove = new[]
        {
            $"user:{userId}",
            $"roles:{userId}",
            $"permissions:{userId}"
        };

        foreach (var key in keysToRemove)
        {
            _userCache.TryRemove(key, out _);
            _rolesCache.TryRemove(key, out _);
            _permissionsCache.TryRemove(key, out _);
        }

        _logger.LogDebug("Cache invalidated for user {UserId}", userId);
    }

    /// <inheritdoc />
    public void ClearCache()
    {
        _userCache.Clear();
        _rolesCache.Clear();
        _permissionsCache.Clear();
        
        lock (_healthLock)
        {
            _healthCache = null;
        }

        _logger.LogInformation("All IAM client caches cleared");
    }

    // ============================================
    // EN: Helper Methods / VI: Phương thức hỗ trợ
    // ============================================

    private HttpRequestMessage CreateRequest(HttpMethod method, string path, string accessToken)
    {
        var request = new HttpRequestMessage(method, path);
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        request.Headers.Add("X-Service-Name", _settings.ServiceName);
        return request;
    }

    private static bool TryGetFromCache<T>(
        ConcurrentDictionary<string, CachedItem<T>> cache, 
        string key, 
        out T value)
    {
        if (cache.TryGetValue(key, out var item) && !item.IsExpired)
        {
            value = item.Value;
            return true;
        }

        value = default!;
        return false;
    }

    private static void AddToCache<T>(
        ConcurrentDictionary<string, CachedItem<T>> cache,
        string key,
        T value,
        int durationSeconds)
    {
        cache[key] = new CachedItem<T>(value, durationSeconds);
    }

    // ============================================
    // EN: Internal DTOs / VI: Internal DTOs
    // ============================================

    private sealed record UserMeResponse(UserData? Data);
    private sealed record UserResponse(UserFullData? Data);
    private sealed record RolesResponse(IReadOnlyList<string>? Data);
    private sealed record PermissionsResponse(IReadOnlyList<string>? Data);
    
    private sealed record UserData(
        string Id, 
        string Email, 
        string? DisplayName, 
        List<string>? Roles,
        List<string>? Permissions);
    
    private sealed record UserFullData(
        string Id, 
        string Email, 
        string? DisplayName,
        bool IsActive,
        List<string>? Roles,
        List<string>? Permissions);

    /// <summary>
    /// EN: Generic cached item with expiration.
    /// VI: Item cache generic có thời hạn.
    /// </summary>
    private sealed class CachedItem<T>
    {
        public T Value { get; }
        private readonly DateTime _expiresAt;

        public CachedItem(T value, int durationSeconds)
        {
            Value = value;
            _expiresAt = DateTime.UtcNow.AddSeconds(durationSeconds);
        }

        public bool IsExpired => DateTime.UtcNow >= _expiresAt;
    }
}
