using JobTrackerApi.Data;
using JobTrackerApi.Models;
using JobTrackerApi.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace JobTrackerApi.Controllers;

[ApiController]
[Route("api/jobapplications/{jobId:int}/focus-plan")]
[Authorize(AuthenticationSchemes = "local", Policy = ProEntitlement.Policy)]
public sealed class StrategySnapshotController(
    JobTrackerContext db,
    AiOperationAdmission admission,
    StrategySnapshotService snapshots) : ControllerBase
{
    [HttpGet]
    public async Task<ActionResult<FocusPlanDto>> Get(int jobId, [FromQuery] string? attachmentIds, CancellationToken cancellationToken)
    {
        IReadOnlyList<int> ids;
        try { ids = StrategySnapshotService.ParseAttachmentIds(attachmentIds); }
        catch (StrategySnapshotValidationException exception) { return Problem(exception); }
        var result = await snapshots.GetCachedAsync(jobId, StrategySnapshotService.NormalizeAttachmentIds(ids), cancellationToken);
        return result is null
            ? NotFound(new { code = "strategy_not_generated", message = "No strategy snapshot has been generated for this context." })
            : Ok(result);
    }

    [HttpPost("operations")]
    public async Task<IActionResult> Enqueue(int jobId, [FromBody] StrategySnapshotRequest? request, CancellationToken cancellationToken)
    {
        try
        {
            var ids = StrategySnapshotService.ParseAttachmentIds(request?.AttachmentIds);
            await snapshots.ValidateRequestAsync(jobId, ids, cancellationToken);
            var signature = StrategySnapshotService.NormalizeAttachmentIds(ids);
            var subject = StrategySnapshotService.EncodeSubject(jobId, ids);
            var activeStatuses = new[] { OperationStatuses.Queued, OperationStatuses.Running, OperationStatuses.WaitingForRetry, OperationStatuses.WaitingForExternalFallback };
            var active = await db.UserOperations.AsNoTracking()
                .Where(item => item.TaskType == StrategySnapshotService.TaskType && item.SubjectId == subject && activeStatuses.Contains(item.Status))
                .OrderByDescending(item => item.CreatedAtUtc)
                .FirstOrDefaultAsync(cancellationToken);
            if (active is not null)
            {
                var statusUrl = $"/api/operations/{active.Id:D}";
                return Accepted(statusUrl, new StrategySnapshotOperationResponse(OperationDto.From(active), statusUrl, false));
            }
            var key = await snapshots.BuildIdempotencyKeyAsync(jobId, signature, cancellationToken);
            var result = await admission.EnqueueAsync(
                StrategySnapshotService.TaskType,
                key,
                "job_strategy",
                subject,
                AiOperationPriorities.Interactive,
                cancellationToken);
            return Accepted(result.StatusUrl, new StrategySnapshotOperationResponse(OperationDto.From(result.Operation), result.StatusUrl, result.Created));
        }
        catch (StrategySnapshotValidationException exception) { return Problem(exception); }
        catch (AiOperationAdmissionException exception)
        {
            if (exception.RetryAfterSeconds is int seconds) Response.Headers.RetryAfter = seconds.ToString();
            return StatusCode(exception.StatusCode, new { code = exception.Code, message = exception.Message });
        }
    }

    [HttpGet("operation")]
    public async Task<ActionResult<OperationDto>> LatestOperation(int jobId, [FromQuery] string? attachmentIds, CancellationToken cancellationToken)
    {
        IReadOnlyList<int> ids;
        try { ids = StrategySnapshotService.ParseAttachmentIds(attachmentIds); }
        catch (StrategySnapshotValidationException exception) { return Problem(exception); }
        var subject = StrategySnapshotService.EncodeSubject(jobId, ids);
        var operation = await db.UserOperations.AsNoTracking()
            .Where(item => item.TaskType == StrategySnapshotService.TaskType && item.SubjectId == subject)
            .OrderByDescending(item => item.CreatedAtUtc)
            .FirstOrDefaultAsync(cancellationToken);
        return operation is null ? NotFound() : Ok(OperationDto.From(operation));
    }

    private ObjectResult Problem(StrategySnapshotValidationException exception) =>
        StatusCode(exception.StatusCode, new { code = exception.Code, message = exception.Message });
}

public sealed record StrategySnapshotRequest(string? AttachmentIds);
public sealed record StrategySnapshotOperationResponse(OperationDto Operation, string StatusUrl, bool Created);
