using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using Dapper;
using KArtSell.BuildingBlocks.Time;
using KArtSell.Modules.ModelOperations.ShadowRun.Services;
using Microsoft.Extensions.Logging;
using Npgsql;

namespace KArtSell.Modules.ModelOperations.ShadowRun.Features.ImportMarketData;

/// <summary>
/// Handler for market data import from external APIs.
/// Implements idempotency via correlation_id + import_date.
/// Logs all imports (success/failure) for audit trail.
/// </summary>
public sealed class ImportMarketDataHandler
{
    private readonly string _connectionString;
    private readonly IKrxDataService? _krxService;
    private readonly IOpenDartDataService? _openDartService;
    private readonly IKisDataService? _kisService;
    private readonly IClock _clock;
    private readonly ILogger<ImportMarketDataHandler> _logger;

    public ImportMarketDataHandler(
        string connectionString,
        IKrxDataService? krxService,
        IOpenDartDataService? openDartService,
        IKisDataService? kisService,
        IClock clock,
        ILogger<ImportMarketDataHandler> logger)
    {
        _connectionString = connectionString;
        _krxService = krxService;
        _openDartService = openDartService;
        _kisService = kisService;
        _clock = clock;
        _logger = logger;
    }

    /// <summary>
    /// Execute import and log result to market_data.krx_imports / opendart_imports / kis_imports.
    /// </summary>
    public async Task<ImportMarketDataResult> HandleAsync(
        ImportMarketDataCommand command,
        CancellationToken cancellationToken)
    {
        var startTime = _clock.UtcNow.UtcDateTime;

        try
        {
            _logger.LogInformation(
                "Starting market data import: API={ApiName}, Date={ImportDate}, CorrelationId={CorrelationId}",
                command.ApiName, command.ImportDate, command.CorrelationId);

            // Check for duplicate (idempotency)
            using (var conn = new NpgsqlConnection(_connectionString))
            {
                await conn.OpenAsync(cancellationToken);

                var tableName = GetTableName(command.ApiName);
                var duplicate = await conn.QuerySingleOrDefaultAsync(
                    $"SELECT id FROM {tableName} WHERE correlation_id = @CorrelationId AND import_at = @ImportDate",
                    new { command.CorrelationId, ImportDate = command.ImportDate });

                if (duplicate != null)
                {
                    _logger.LogInformation("Duplicate import detected (idempotent replay): {CorrelationId}", command.CorrelationId);
                    return new ImportMarketDataResult(
                        success: true,
                        apiName: command.ApiName,
                        rowCount: 0,
                        checksum: "",
                        isDuplicate: true,
                        errorMessage: null);
                }
            }

            // Execute import based on API type
            var (success, rowCount, errorMessage) = command.ApiName switch
            {
                "krx" => await ImportKrxDataAsync(command, cancellationToken),
                "opendart" => await ImportOpenDartDataAsync(command, cancellationToken),
                "kis" => await ImportKisDataAsync(command, cancellationToken),
                _ => throw new InvalidOperationException($"Unknown API: {command.ApiName}")
            };

            // Log import result
            var checksum = ComputeChecksum($"{command.ApiName}:{command.ImportDate}:{rowCount}");
            await LogImportResultAsync(
                command,
                success ? "SUCCESS" : "FAILURE",
                rowCount,
                checksum,
                errorMessage,
                cancellationToken);

            var duration = _clock.UtcNow.UtcDateTime - startTime;
            _logger.LogInformation(
                "Market data import completed: API={ApiName}, Status={Status}, Rows={RowCount}, Duration={DurationMs}ms",
                command.ApiName, success ? "SUCCESS" : "FAILURE", rowCount, duration.TotalMilliseconds);

            return new ImportMarketDataResult(
                success: success,
                apiName: command.ApiName,
                rowCount: rowCount,
                checksum: checksum,
                isDuplicate: false,
                errorMessage: errorMessage);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Market data import failed: API={ApiName}", command.ApiName);

            // Log failure
            try
            {
                await LogImportResultAsync(
                    command,
                    "FAILURE",
                    0,
                    "",
                    ex.Message,
                    cancellationToken);
            }
            catch (Exception logEx)
            {
                _logger.LogError(logEx, "Failed to log import failure");
            }

            throw;
        }
    }

    private async Task<(bool success, int rowCount, string? errorMessage)> ImportKrxDataAsync(
        ImportMarketDataCommand command,
        CancellationToken cancellationToken)
    {
        if (_krxService == null)
            return (false, 0, "KRX service not configured");

        try
        {
            // Fetch daily OHLCV for a sample ticker (in production: iterate over portfolio)
            var bars = await _krxService.GetDailyOhlcvAsync(
                ticker: "005930",  // Samsung
                startDate: command.ImportDate,
                endDate: command.ImportDate,
                cancellationToken: cancellationToken);

            return (true, bars.Count, null);
        }
        catch (Exception ex)
        {
            return (false, 0, ex.Message);
        }
    }

    private async Task<(bool success, int rowCount, string? errorMessage)> ImportOpenDartDataAsync(
        ImportMarketDataCommand command,
        CancellationToken cancellationToken)
    {
        if (_openDartService == null)
            return (false, 0, "OpenDart service not configured");

        try
        {
            // Fetch disclosures for a sample corporation (in production: iterate over watch list)
            var disclosures = await _openDartService.GetDisclosuresAsync(
                corpCode: "005930",
                startDate: command.ImportDate.AddMonths(-1),
                endDate: command.ImportDate,
                cancellationToken: cancellationToken);

            return (true, disclosures.Count, null);
        }
        catch (Exception ex)
        {
            return (false, 0, ex.Message);
        }
    }

    private async Task<(bool success, int rowCount, string? errorMessage)> ImportKisDataAsync(
        ImportMarketDataCommand command,
        CancellationToken cancellationToken)
    {
        if (_kisService == null)
            return (false, 0, "KIS service not configured");

        try
        {
            // Fetch trading orders for a sample account (in production: iterate over accounts)
            var orders = await _kisService.GetTradingOrdersAsync(
                accountNumber: "test-account",
                startDate: command.ImportDate.AddDays(-30),
                endDate: command.ImportDate,
                cancellationToken: cancellationToken);

            return (true, orders.Count, null);
        }
        catch (Exception ex)
        {
            return (false, 0, ex.Message);
        }
    }

    private async Task LogImportResultAsync(
        ImportMarketDataCommand command,
        string status,
        int rowCount,
        string checksum,
        string? errorMessage,
        CancellationToken cancellationToken)
    {
        using (var conn = new NpgsqlConnection(_connectionString))
        {
            await conn.OpenAsync(cancellationToken);

            var tableName = GetTableName(command.ApiName);
            var sql = $@"
                INSERT INTO {tableName}
                (id, import_at, row_count, checksum, status, error_message, published_at, correlation_id, revision)
                VALUES (@Id, @ImportAt, @RowCount, @Checksum, @Status, @ErrorMessage, @PublishedAt, @CorrelationId, 1)
            ";

            await conn.ExecuteAsync(sql, new
            {
                Id = Guid.NewGuid(),
                ImportAt = _clock.UtcNow.UtcDateTime,
                RowCount = rowCount,
                Checksum = checksum,
                Status = status,
                ErrorMessage = errorMessage,
                PublishedAt = _clock.UtcNow.UtcDateTime,
                command.CorrelationId
            });
        }
    }

    private static string GetTableName(string apiName) => apiName switch
    {
        "krx" => "market_data.krx_imports",
        "opendart" => "market_data.opendart_imports",
        "kis" => "market_data.kis_imports",
        _ => throw new InvalidOperationException($"Unknown API: {apiName}")
    };

    private static string ComputeChecksum(string data)
    {
        using var sha = SHA256.Create();
        var hash = sha.ComputeHash(Encoding.UTF8.GetBytes(data));
        return Convert.ToHexString(hash)[..16];
    }
}

public record ImportMarketDataResult(
    bool success,
    string apiName,
    int rowCount,
    string checksum,
    bool isDuplicate,
    string? errorMessage);
