using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using Managing.Application.Abstractions.Services;
using Managing.Core;
using Managing.Domain.Users;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Managing.Infrastructure.Evm.Services;

/// <summary>
/// Configuration settings for Kaigen credit management service.
/// The SecretKey should be set via the KAIGEN_SECRET_KEY environment variable for security.
/// </summary>
public class KaigenSettings
{
    public string BaseUrl { get; set; } = "https://api.kaigen.managing.live";
    public string DebitEndpoint { get; set; } = "/api/credits/debit";
    public string RefundEndpoint { get; set; } = "/api/credits/refund";
    public string SecretKey { get; set; } = string.Empty;
}

public class KaigenService : IKaigenService
{
    private readonly HttpClient _httpClient;
    private readonly KaigenSettings _settings;
    private readonly ILogger<KaigenService> _logger;
    private readonly JsonSerializerOptions _jsonOptions;
    private readonly bool _creditsEnabled;

    public KaigenService(IOptions<KaigenSettings> options, ILogger<KaigenService> logger)
    {
        _httpClient = new HttpClient();
        _settings = options.Value;
        _logger = logger;
        _jsonOptions = new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        };

        // Check if credits are enabled via environment variable
        var creditsEnabledEnv = Environment.GetEnvironmentVariable("KAIGEN_CREDITS_ENABLED");
        _creditsEnabled = !string.IsNullOrEmpty(creditsEnabledEnv) && creditsEnabledEnv.ToLower() == "true";

        if (!_creditsEnabled)
        {
            _logger.LogInformation("Kaigen credits are disabled via KAIGEN_CREDITS_ENABLED environment variable");
            return; // Skip secret key validation if credits are disabled
        }

        // Always read from environment variable for security
        var envSecretKey = Environment.GetEnvironmentVariable("KAIGEN_SECRET_KEY");
        if (!string.IsNullOrEmpty(envSecretKey))
        {
            _settings.SecretKey = envSecretKey;
            _logger.LogInformation("Using KAIGEN_SECRET_KEY from environment variable");
        }

        // Validate required settings only if credits are enabled
        if (string.IsNullOrEmpty(_settings.SecretKey))
        {
            throw new InvalidOperationException(
                "Kaigen SecretKey is not configured. Please set the KAIGEN_SECRET_KEY environment variable.");
        }

        if (string.IsNullOrEmpty(_settings.BaseUrl))
        {
            throw new InvalidOperationException("Kaigen BaseUrl is not configured.");
        }
    }

    public async Task<string> DebitUserCreditsAsync(User user, decimal debitAmount)
    {
        // If credits are disabled, return a dummy request ID
        if (!_creditsEnabled)
        {
            var dummyRequestId = Guid.NewGuid().ToString();
            _logger.LogInformation(
                "Credits disabled - skipping debit for user {UserName}. Returning dummy request ID {RequestId}",
                user.Name, dummyRequestId);
            return dummyRequestId;
        }

        try
        {
            var walletAddress = GetUserWalletAddress(user);
            var requestId = Guid.NewGuid().ToString();

            // Create the request payload
            var requestPayload = new
            {
                requestId = requestId,
                debitAmount = debitAmount
            };

            _logger.LogInformation(
                "Debiting {Amount} credits for user {UserName} (wallet: {WalletAddress}) with request ID {RequestId}",
                debitAmount, user.Name, walletAddress, requestId);

            var result = await SendAuthenticatedRequestAsync(
                $"{_settings.BaseUrl}{_settings.DebitEndpoint}",
                requestPayload,
                user);

            if (!result.Success)
            {
                _logger.LogError("Debit request failed: {Message}", result.Message);
                throw new Exception($"Debit request failed: {result.Message}");
            }

            _logger.LogInformation(
                "Successfully debited {Amount} credits for user {UserName} (wallet: {WalletAddress})",
                debitAmount, user.Name, walletAddress);

            return requestId;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error debiting credits for user {UserName}", user.Name);
            throw;
        }
    }

    public async Task<bool> RefundUserCreditsAsync(string requestId, User user)
    {
        // If credits are disabled, return true (success) immediately
        if (!_creditsEnabled)
        {
            _logger.LogInformation("Credits disabled - skipping refund for user {UserName} with request ID {RequestId}",
                user.Name, requestId);
            return true;
        }

        try
        {
            var walletAddress = GetUserWalletAddress(user);

            // Create the request payload
            var requestPayload = new
            {
                requestId = requestId,
            };

            _logger.LogInformation(
                "Refunding credits for user {UserName} (wallet: {WalletAddress}) with request ID {RequestId}",
                user.Name, walletAddress, requestId);

            var result = await SendAuthenticatedRequestAsync(
                $"{_settings.BaseUrl}{_settings.RefundEndpoint}",
                requestPayload,
                user);

            if (!result.Success)
            {
                _logger.LogError("Failed to refund credits: {Message}", result.Message);
                return false;
            }

            _logger.LogInformation("Successfully refunded credits for user {UserName} (wallet: {WalletAddress})",
                user.Name, walletAddress);
            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error refunding credits for user {UserName}", user.Name);
            return false;
        }
    }

    private async Task<KaigenResponse> SendAuthenticatedRequestAsync(string url, object payload, User user)
    {
        // Create the auth token: "walletaddress-username"
        var authToken = $"{GetUserWalletAddress(user)}-{user.Name}";

        // Encrypt the auth token using AES-256-GCM
        var encryptedToken = CryptoHelpers.EncryptAesGcm(authToken, _settings.SecretKey);

        // Create Basic Auth header with the encrypted token
        var basicAuthString = $"{encryptedToken}:";
        var base64Auth = Convert.ToBase64String(Encoding.ASCII.GetBytes(basicAuthString));

        // Create a new request with the auth header
        var request = new HttpRequestMessage(HttpMethod.Put, url)
        {
            Content = JsonContent.Create(payload, options: _jsonOptions)
        };
        request.Headers.Authorization = new AuthenticationHeaderValue("Basic", base64Auth);

        var response = await _httpClient.SendAsync(request);

        if (!response.IsSuccessStatusCode)
        {
            var errorContent = await response.Content.ReadAsStringAsync();
            _logger.LogError("Request failed. Status: {StatusCode}, Error: {Error}",
                response.StatusCode, errorContent);
            return new KaigenResponse { Success = false, Message = $"HTTP {response.StatusCode}: {errorContent}" };
        }

        var result = await response.Content.ReadFromJsonAsync<KaigenResponse>(_jsonOptions);
        return result ?? new KaigenResponse { Success = false, Message = "Failed to parse response" };
    }

    private string GetUserWalletAddress(User user)
    {
        if (user?.Accounts == null || !user.Accounts.Any())
        {
            throw new InvalidOperationException($"No accounts found for user {user?.Name}");
        }

        // Use the first account's key as the wallet address
        var walletAddress = user.Accounts[0].Key;

        if (string.IsNullOrEmpty(walletAddress))
        {
            throw new InvalidOperationException($"No wallet address found for user {user.Name}");
        }

        return walletAddress;
    }

    private class KaigenResponse
    {
        public bool Success { get; set; }
        public string Message { get; set; }
    }
}