using System.Text.Json;
using Managing.Application.Abstractions.Services;
using Managing.Domain.Synth.Models;
using Microsoft.Extensions.Logging;

namespace Managing.Application.Synth;

/// <summary>
/// Client for communicating with the Synth API
/// </summary>
public class SynthApiClient : ISynthApiClient, IDisposable
{
    private readonly HttpClient _httpClient;
    private readonly ILogger<SynthApiClient> _logger;
    private readonly JsonSerializerOptions _jsonOptions;

    // Private configuration - should come from app settings or environment variables
    private readonly string _apiKey;
    private readonly string _baseUrl;

    public SynthApiClient(HttpClient httpClient, ILogger<SynthApiClient> logger)
    {
        _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));

        // TODO: These should come from IConfiguration or environment variables
        _apiKey = Environment.GetEnvironmentVariable("SYNTH_API_KEY") ??
                  "bfd2a078b412452af2e01ca74b2a7045d4ae411a85943342";
        _baseUrl = Environment.GetEnvironmentVariable("SYNTH_BASE_URL") ?? "https://api.synthdata.co";

        // Configure HttpClient once
        ConfigureHttpClient();

        // Configure JSON options
        _jsonOptions = new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true,
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        };
    }

    /// <summary>
    /// Configures the HTTP client with API settings
    /// </summary>
    private void ConfigureHttpClient()
    {
        // Validate API configuration
        if (string.IsNullOrEmpty(_apiKey) || string.IsNullOrEmpty(_baseUrl))
        {
            throw new InvalidOperationException(
                "Synth API configuration is missing. Please set SYNTH_API_KEY and SYNTH_BASE_URL environment variables.");
        }

        // Set base address and authorization
        _httpClient.BaseAddress = new Uri(_baseUrl);
        _httpClient.DefaultRequestHeaders.Clear();
        _httpClient.DefaultRequestHeaders.Add("Authorization", $"Apikey {_apiKey}");
        _httpClient.Timeout = TimeSpan.FromSeconds(30);
    }

    /// <summary>
    /// Fetches the current leaderboard from Synth API
    /// </summary>
    public async Task<List<MinerInfo>> GetLeaderboardAsync(SynthConfiguration config)
    {
        try
        {
            _logger.LogInformation("🔍 **Synth API** - Fetching leaderboard");

            var response = await _httpClient.GetAsync("/leaderboard/latest");

            if (!response.IsSuccessStatusCode)
            {
                _logger.LogWarning(
                    $"Synth API leaderboard request failed: {response.StatusCode} - {response.ReasonPhrase}");
                return new List<MinerInfo>();
            }

            var jsonContent = await response.Content.ReadAsStringAsync();
            var miners = JsonSerializer.Deserialize<List<MinerInfo>>(jsonContent, _jsonOptions);

            _logger.LogInformation($"📊 **Synth API** - Retrieved {miners?.Count ?? 0} miners from leaderboard");

            return miners ?? new List<MinerInfo>();
        }
        catch (HttpRequestException ex)
        {
            _logger.LogError(ex, "HTTP error while fetching Synth leaderboard");
            return new List<MinerInfo>();
        }
        catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
        {
            _logger.LogError(ex, "Timeout while fetching Synth leaderboard");
            return new List<MinerInfo>();
        }
        catch (JsonException ex)
        {
            _logger.LogError(ex, "JSON deserialization error while parsing Synth leaderboard");
            return new List<MinerInfo>();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unexpected error while fetching Synth leaderboard");
            return new List<MinerInfo>();
        }
    }

    /// <summary>
    /// Fetches historical leaderboard data from Synth API for a specific time range
    /// </summary>
    public async Task<List<MinerInfo>> GetHistoricalLeaderboardAsync(DateTime startTime, DateTime endTime, SynthConfiguration config)
    {
        try
        {
            // Format dates to ISO 8601 format as required by the API
            var startTimeStr = Uri.EscapeDataString(startTime.ToString("yyyy-MM-ddTHH:mm:ssZ"));
            var endTimeStr = Uri.EscapeDataString(endTime.ToString("yyyy-MM-ddTHH:mm:ssZ"));
            
            var url = $"/leaderboard/historical?start_time={startTimeStr}&end_time={endTimeStr}";

            _logger.LogInformation($"🔍 **Synth API** - Fetching historical leaderboard from {startTime:yyyy-MM-dd HH:mm} to {endTime:yyyy-MM-dd HH:mm}");

            var response = await _httpClient.GetAsync(url);

            if (!response.IsSuccessStatusCode)
            {
                _logger.LogWarning(
                    $"Synth API historical leaderboard request failed: {response.StatusCode} - {response.ReasonPhrase}");
                return new List<MinerInfo>();
            }

            var jsonContent = await response.Content.ReadAsStringAsync();
            var miners = JsonSerializer.Deserialize<List<MinerInfo>>(jsonContent, _jsonOptions);

            _logger.LogInformation($"📊 **Synth API** - Retrieved {miners?.Count ?? 0} miners from historical leaderboard");

            return miners ?? new List<MinerInfo>();
        }
        catch (HttpRequestException ex)
        {
            _logger.LogError(ex, "HTTP error while fetching Synth historical leaderboard");
            return new List<MinerInfo>();
        }
        catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
        {
            _logger.LogError(ex, "Timeout while fetching Synth historical leaderboard");
            return new List<MinerInfo>();
        }
        catch (JsonException ex)
        {
            _logger.LogError(ex, "JSON deserialization error while parsing Synth historical leaderboard");
            return new List<MinerInfo>();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unexpected error while fetching Synth historical leaderboard");
            return new List<MinerInfo>();
        }
    }

    /// <summary>
    /// Fetches latest predictions from specified miners
    /// </summary>
    public async Task<List<MinerPrediction>> GetMinerPredictionsAsync(
        List<int> minerUids,
        string asset,
        int timeIncrement,
        int timeLength,
        SynthConfiguration config)
    {
        if (minerUids == null || !minerUids.Any())
        {
            _logger.LogWarning("No miner UIDs provided for prediction request");
            return new List<MinerPrediction>();
        }

        try
        {
            // Build URL with proper array formatting for miner parameter
            var queryParams = new List<string>
            {
                $"asset={Uri.EscapeDataString(asset)}",
                $"time_increment={timeIncrement}",
                $"time_length={timeLength}"
            };
            
            // Add each miner UID as a separate parameter (standard array query parameter format)
            foreach (var minerUid in minerUids)
            {
                queryParams.Add($"miner={minerUid}");
            }
            
            var url = $"/prediction/latest?{string.Join("&", queryParams)}";

            _logger.LogInformation(
                $"🔮 **Synth API** - Fetching predictions for {minerUids.Count} miners, asset: {asset}, time: {timeLength}s");

            var response = await _httpClient.GetAsync(url);

            if (!response.IsSuccessStatusCode)
            {
                _logger.LogWarning(
                    $"Synth API predictions request failed: {response.StatusCode} - {response.ReasonPhrase}");
                return new List<MinerPrediction>();
            }

            var jsonContent = await response.Content.ReadAsStringAsync();
            var predictions = JsonSerializer.Deserialize<List<MinerPrediction>>(jsonContent, _jsonOptions);

            var totalPaths = predictions?.Sum(p => p.NumSimulations) ?? 0;
            _logger.LogInformation(
                $"📈 **Synth API** - Retrieved {predictions?.Count ?? 0} predictions with {totalPaths} total simulation paths");

            return predictions ?? new List<MinerPrediction>();
        }
        catch (HttpRequestException ex)
        {
            _logger.LogError(ex, $"HTTP error while fetching Synth predictions for {asset}");
            return new List<MinerPrediction>();
        }
        catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
        {
            _logger.LogError(ex, $"Timeout while fetching Synth predictions for {asset}");
            return new List<MinerPrediction>();
        }
        catch (JsonException ex)
        {
            _logger.LogError(ex, $"JSON deserialization error while parsing Synth predictions for {asset}");
            return new List<MinerPrediction>();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"Unexpected error while fetching Synth predictions for {asset}");
            return new List<MinerPrediction>();
        }
    }

    /// <summary>
    /// Fetches historical predictions from specified miners for a specific time point
    /// </summary>
    public async Task<List<MinerPrediction>> GetHistoricalMinerPredictionsAsync(
        List<int> minerUids,
        string asset,
        DateTime startTime,
        int timeIncrement,
        int timeLength,
        SynthConfiguration config)
    {
        if (minerUids == null || !minerUids.Any())
        {
            _logger.LogWarning("No miner UIDs provided for historical prediction request");
            return new List<MinerPrediction>();
        }

        try
        {
            // Format start time to ISO 8601 format as required by the API
            var startTimeStr = Uri.EscapeDataString(startTime.ToString("yyyy-MM-ddTHH:mm:ssZ"));
            
            // Build URL with proper array formatting for miner parameter
            var queryParams = new List<string>
            {
                $"asset={Uri.EscapeDataString(asset)}",
                $"start_time={startTimeStr}",
                $"time_increment={timeIncrement}",
                $"time_length={timeLength}"
            };
            
            // Add each miner UID as a separate parameter (standard array query parameter format)
            foreach (var minerUid in minerUids)
            {
                queryParams.Add($"miner={minerUid}");
            }
            
            var url = $"/prediction/historical?{string.Join("&", queryParams)}";

            _logger.LogInformation(
                $"🔮 **Synth API** - Fetching historical predictions for {minerUids.Count} miners, asset: {asset}, time: {startTime:yyyy-MM-dd HH:mm}, duration: {timeLength}s");

            var response = await _httpClient.GetAsync(url);

            if (!response.IsSuccessStatusCode)
            {
                _logger.LogWarning(
                    $"Synth API historical predictions request failed: {response.StatusCode} - {response.ReasonPhrase}");
                return new List<MinerPrediction>();
            }

            var jsonContent = await response.Content.ReadAsStringAsync();
            var predictions = JsonSerializer.Deserialize<List<MinerPrediction>>(jsonContent, _jsonOptions);

            var totalPaths = predictions?.Sum(p => p.NumSimulations) ?? 0;
            _logger.LogInformation(
                $"📈 **Synth API** - Retrieved {predictions?.Count ?? 0} historical predictions with {totalPaths} total simulation paths");

            return predictions ?? new List<MinerPrediction>();
        }
        catch (HttpRequestException ex)
        {
            _logger.LogError(ex, $"HTTP error while fetching Synth historical predictions for {asset}");
            return new List<MinerPrediction>();
        }
        catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
        {
            _logger.LogError(ex, $"Timeout while fetching Synth historical predictions for {asset}");
            return new List<MinerPrediction>();
        }
        catch (JsonException ex)
        {
            _logger.LogError(ex, $"JSON deserialization error while parsing Synth historical predictions for {asset}");
            return new List<MinerPrediction>();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"Unexpected error while fetching Synth historical predictions for {asset}");
            return new List<MinerPrediction>();
        }
    }

    public void Dispose()
    {
        _httpClient?.Dispose();
    }
}