using Hangfire;
using KArtSell.Modules.ModelOperations.TradeExecution;
using Microsoft.Extensions.Logging;

namespace KArtSell.Host.Jobs;

/// <summary>
/// Polls KIS for status on trades that have been submitted but haven't reached a terminal state,
/// and confirms settlement for trades that have fully filled.
///
/// DEBT-027: before this job existed, PollTradeStatusHandler and ConfirmSettlementHandler were
/// registered in DI and fully implemented, but nothing in the running application ever invoked
/// them — no endpoint, no scheduled job. A trade could reach Submitted via POST /trades and never
/// progress any further; the same class of gap DEBT-026 found and fixed for the approval
/// workflow's Draft-&gt;Proposed transition.
/// </summary>
public sealed class TradeStatusPollingJob(
    ITradeSql sql,
    PollTradeStatusHandler pollHandler,
    ConfirmSettlementHandler settlementHandler,
    ILogger<TradeStatusPollingJob> logger)
{
    private static readonly TradeStatus[] PollableStatuses =
        [TradeStatus.Submitted, TradeStatus.Accepted, TradeStatus.PartiallyFilled];

    private static readonly Action<ILogger, int, Exception?> LogPolled =
        LoggerMessage.Define<int>(
            LogLevel.Information,
            new EventId(1, nameof(LogPolled)),
            "Trade status polling processed {TradeCount} non-terminal trades.");

    private static readonly Action<ILogger, Guid, Exception?> LogPollError =
        LoggerMessage.Define<Guid>(
            LogLevel.Error,
            new EventId(2, nameof(LogPollError)),
            "Failed to poll trade status for {TradeId}");

    private static readonly Action<ILogger, int, Exception?> LogSettled =
        LoggerMessage.Define<int>(
            LogLevel.Information,
            new EventId(3, nameof(LogSettled)),
            "Trade settlement confirmation processed {TradeCount} fully-filled trades.");

    private static readonly Action<ILogger, Guid, Exception?> LogSettleError =
        LoggerMessage.Define<Guid>(
            LogLevel.Error,
            new EventId(4, nameof(LogSettleError)),
            "Failed to confirm settlement for {TradeId}");

    [Queue("q-customer-sla")]
    [DisableConcurrentExecution(timeoutInSeconds: 120)]
    [AutomaticRetry(Attempts = 3, OnAttemptsExceeded = AttemptsExceededAction.Fail)]
    public async Task ExecuteAsync(CancellationToken ct = default)
    {
        var polledCount = 0;
        foreach (var status in PollableStatuses)
        {
            var trades = await sql.GetTradesByStatusAsync(status, Guid.NewGuid(), ct);
            foreach (var trade in trades)
            {
                if (string.IsNullOrEmpty(trade.KisOrderId))
                {
                    continue;
                }

                try
                {
                    await pollHandler.HandleAsync(new PollTradeStatusCommand
                    {
                        TradeId = trade.Id,
                        KisOrderId = trade.KisOrderId,
                        CorrelationId = trade.CorrelationId
                    }, ct);
                    polledCount++;
                }
                catch (Exception ex)
                {
                    LogPollError(logger, trade.Id, ex);
                }
            }
        }

        LogPolled(logger, polledCount, null);

        var filledTrades = await sql.GetTradesByStatusAsync(TradeStatus.FullyFilled, Guid.NewGuid(), ct);
        var settledCount = 0;
        foreach (var trade in filledTrades)
        {
            if (string.IsNullOrEmpty(trade.KisOrderId))
            {
                continue;
            }

            try
            {
                await settlementHandler.HandleAsync(new ConfirmSettlementCommand
                {
                    TradeId = trade.Id,
                    KisOrderId = trade.KisOrderId,
                    CorrelationId = trade.CorrelationId
                }, ct);
                settledCount++;
            }
            catch (Exception ex)
            {
                LogSettleError(logger, trade.Id, ex);
            }
        }

        LogSettled(logger, settledCount, null);
    }
}
