using Hangfire;
using KArtSell.BuildingBlocks.Time;
using KArtSell.Host.Jobs;
using KArtSell.Modules.ModelOperations.ShadowRun;
using Microsoft.Extensions.Logging;

namespace KArtSell.Host.Features.ShadowRun;

/// <summary>
/// Handles shadow run initiation: pre-insert shadow_run with "Queued" status, enqueue Hangfire job.
/// Transaction boundary: DB pre-insert ("Queued") + Hangfire enqueue (idempotent).
/// Polling endpoint works immediately after response (202 Accepted).
/// </summary>
public sealed class InitiateShadowRunHandler(
    IBackgroundJobClient backgroundJobClient,
    ShadowRunQueries queries,
    IClock clock,
    ILogger<InitiateShadowRunHandler> logger)
{
    private static readonly Action<ILogger, Guid, Guid, DateOnly, DateOnly, Exception?> LogInitiated =
        LoggerMessage.Define<Guid, Guid, DateOnly, DateOnly>(
            LogLevel.Information,
            new EventId(1, nameof(LogInitiated)),
            "Shadow run {RunId} initiated for model {ModelId} ({Start:yyyy-MM-dd} to {End:yyyy-MM-dd})");

    private static readonly Action<ILogger, Guid, Exception?> LogJobEnqueued =
        LoggerMessage.Define<Guid>(
            LogLevel.Information,
            new EventId(2, nameof(LogJobEnqueued)),
            "Hangfire job enqueued for shadow run {RunId}");

    public async Task<InitiateShadowRunResponse> HandleAsync(
        InitiateShadowRunRequest request,
        Guid correlationId,
        CancellationToken cancellationToken)
    {
        // Validate: Model exists (stub for now; would query DB in production)
        if (request.ModelId == Guid.Empty)
            throw new InvalidOperationException("ModelId cannot be empty");

        // Create shadow run command with idempotency key
        var idempotencyKey = Guid.NewGuid();
        var runId = Guid.NewGuid();

        var command = new ShadowRunCommand(
            ModelId: request.ModelId,
            CorrelationId: correlationId,
            IdempotencyKey: idempotencyKey,
            WindowStartDate: request.WindowStart,
            WindowEndDate: request.WindowEnd,
            PhaseFilter: ParsePhaseFilter(request.PhaseFilter));

        LogInitiated(logger, runId, request.ModelId, request.WindowStart, request.WindowEnd, null);

        // Pre-insert shadow_run with "Queued" status (enables immediate polling)
        var createdAt = clock.UtcNow;
        await queries.InsertShadowRunQueuedAsync(
            runId,
            request.ModelId,
            request.WindowStart,
            request.WindowEnd,
            createdAt,
            CancellationToken.None);

        // Enqueue Hangfire job to q-evaluation queue (Host listens to this queue)
        var jobId = backgroundJobClient.Enqueue<ShadowRunJob>(
            "q-evaluation",
            job => job.ExecuteAsync(command, CancellationToken.None));

        LogJobEnqueued(logger, runId, null);

        // Return response immediately (202 Accepted)
        return new InitiateShadowRunResponse(
            RunId: runId,
            Status: "Queued",
            JobId: jobId,
            EstimatedSeconds: 3600,  // 1 hour estimate
            CreatedAt: createdAt);
    }

    private static MarketPhaseFilter ParsePhaseFilter(string phase) =>
        phase switch
        {
            "BullMarket" => MarketPhaseFilter.BullMarket,
            "BearMarket" => MarketPhaseFilter.BearMarket,
            "Sideways" => MarketPhaseFilter.Sideways,
            "HighVolatility" => MarketPhaseFilter.HighVolatility,
            _ => MarketPhaseFilter.All
        };
}
