using Microsoft.AspNetCore.Mvc;
using Nalu.Web.Models;
using Nalu.Web.Services;
using Nalu.Web.Services.LlmRouter;

namespace Nalu.Web.Endpoints;

public static class ExtractEndpoints
{
    public static void MapExtractEndpoints(this WebApplication app)
    {
        var group = app.MapGroup("/v1/extract")
            .RequireAuthorization("ApiKey")
            .WithTags("Extract");

        // ── Deterministic (1 credit) ──────────────────────────────────────────

        group.MapPost("/cpf", async (HttpContext ctx,
            [FromBody] ExtractionRequest req,
            ExtractionPipeline pipeline, CreditService credits, CancellationToken ct) =>
        {
            var cr = await credits.TryConsumeAsync(ctx.User, "validate_cpf", ctx, ct);
            credits.ApplyHeaders(ctx, cr);
            if (!cr.Success) return Results.Json(cr.ErrorPayload, statusCode: 429);
            return Results.Ok(await pipeline.ExecuteAsync("validate_cpf", req, ct));
        })
        .WithName("ExtractCpf")
        .WithSummary("Extrai e valida CPF")
        .WithDescription("Extrai CPF, valida dígitos verificadores (mod 11) e formata XXX.XXX.XXX-XX. Custa 1 crédito.")
        .Produces<ExtractionResponse>().ProducesProblem(429).WithOpenApi();

        group.MapPost("/cep", async (HttpContext ctx,
            [FromBody] ExtractionRequest req,
            ExtractionPipeline pipeline, CreditService credits, CancellationToken ct) =>
        {
            var cr = await credits.TryConsumeAsync(ctx.User, "validate_cep", ctx, ct);
            credits.ApplyHeaders(ctx, cr);
            if (!cr.Success) return Results.Json(cr.ErrorPayload, statusCode: 429);
            return Results.Ok(await pipeline.ExecuteAsync("validate_cep", req, ct));
        })
        .WithName("ExtractCep")
        .WithSummary("Extrai CEP e retorna endereço")
        .WithDescription("Extrai CEP e retorna endereço enriquecido com logradouro, bairro, cidade e estado. Custa 3 créditos.")
        .Produces<ExtractionResponse>().ProducesProblem(429).WithOpenApi();

        group.MapPost("/phone", async (HttpContext ctx,
            [FromBody] ExtractionRequest req,
            ExtractionPipeline pipeline, CreditService credits, CancellationToken ct) =>
        {
            var cr = await credits.TryConsumeAsync(ctx.User, "validate_phone_br", ctx, ct);
            credits.ApplyHeaders(ctx, cr);
            if (!cr.Success) return Results.Json(cr.ErrorPayload, statusCode: 429);
            return Results.Ok(await pipeline.ExecuteAsync("validate_phone_br", req, ct));
        })
        .WithName("ExtractPhone")
        .WithSummary("Extrai telefone brasileiro com DDD")
        .WithDescription("Extrai telefone com DDD e normaliza para (XX) XXXXX-XXXX ou (XX) XXXX-XXXX. Custa 1 crédito.")
        .Produces<ExtractionResponse>().ProducesProblem(429).WithOpenApi();

        group.MapPost("/email", async (HttpContext ctx,
            [FromBody] ExtractionRequest req,
            ExtractionPipeline pipeline, CreditService credits, CancellationToken ct) =>
        {
            var cr = await credits.TryConsumeAsync(ctx.User, "validate_email", ctx, ct);
            credits.ApplyHeaders(ctx, cr);
            if (!cr.Success) return Results.Json(cr.ErrorPayload, statusCode: 429);
            return Results.Ok(await pipeline.ExecuteAsync("validate_email", req, ct));
        })
        .WithName("ExtractEmail")
        .WithSummary("Extrai email com correção de typos")
        .WithDescription("Extrai email e corrige typos comuns em domínios (gmail, hotmail, outlook). Custa 1 crédito.")
        .Produces<ExtractionResponse>().ProducesProblem(429).WithOpenApi();

        group.MapPost("/postal-code", async (HttpContext ctx,
            [FromBody] ExtractionRequest req,
            ExtractionPipeline pipeline, CreditService credits, CancellationToken ct) =>
        {
            var cr = await credits.TryConsumeAsync(ctx.User, "validate_postal_code", ctx, ct);
            credits.ApplyHeaders(ctx, cr);
            if (!cr.Success) return Results.Json(cr.ErrorPayload, statusCode: 429);
            return Results.Ok(await pipeline.ExecuteAsync("validate_postal_code", req, ct));
        })
        .WithName("ExtractPostalCode")
        .WithSummary("Extrai código postal internacional")
        .WithDescription("Extrai e normaliza código postal de qualquer país exceto Brasil (use /cep para CEPs). Custa 1 crédito.")
        .Produces<ExtractionResponse>().ProducesProblem(429).WithOpenApi();

        group.MapPost("/cnpj", async (HttpContext ctx,
            [FromBody] ExtractionRequest req,
            ExtractionPipeline pipeline, CreditService credits, CancellationToken ct) =>
        {
            var cr = await credits.TryConsumeAsync(ctx.User, "validate_cnpj", ctx, ct);
            credits.ApplyHeaders(ctx, cr);
            if (!cr.Success) return Results.Json(cr.ErrorPayload, statusCode: 429);
            return Results.Ok(await pipeline.ExecuteAsync("validate_cnpj", req, ct));
        })
        .WithName("ExtractCnpj")
        .WithSummary("Extrai e valida CNPJ")
        .WithDescription("Extrai CNPJ, valida dígitos verificadores (mod 11) e formata XX.XXX.XXX/XXXX-XX. Custa 1 crédito.")
        .Produces<ExtractionResponse>().ProducesProblem(429).WithOpenApi();

        group.MapPost("/plate-br", async (HttpContext ctx,
            [FromBody] ExtractionRequest req,
            ExtractionPipeline pipeline, CreditService credits, CancellationToken ct) =>
        {
            var cr = await credits.TryConsumeAsync(ctx.User, "validate_plate_br", ctx, ct);
            credits.ApplyHeaders(ctx, cr);
            if (!cr.Success) return Results.Json(cr.ErrorPayload, statusCode: 429);
            return Results.Ok(await pipeline.ExecuteAsync("validate_plate_br", req, ct));
        })
        .WithName("ExtractPlateBr")
        .WithSummary("Extrai placa brasileira")
        .WithDescription("Suporta Mercosul (ABC1D23) e formato antigo (ABC-1234). Aceita entrada por extenso. Custa 1 crédito.")
        .Produces<ExtractionResponse>().ProducesProblem(429).WithOpenApi();

        // ── Light LLM (2 credits) ─────────────────────────────────────────────

        group.MapPost("/name", async (HttpContext ctx,
            [FromBody] ExtractionRequest req,
            ExtractionPipeline pipeline, CreditService credits, CancellationToken ct) =>
        {
            var cr = await credits.TryConsumeAsync(ctx.User, "validate_name", ctx, ct);
            credits.ApplyHeaders(ctx, cr);
            if (!cr.Success) return Results.Json(cr.ErrorPayload, statusCode: 429);
            return Results.Ok(await pipeline.ExecuteAsync("validate_name", req, ct));
        })
        .WithName("ExtractName")
        .WithSummary("Extrai nome ou apelido")
        .WithDescription("Extrai o nome ou apelido do usuário. Aceita primeiro nome sem sobrenome. Custa 3 créditos.")
        .Produces<ExtractionResponse>().ProducesProblem(429).WithOpenApi();

        group.MapPost("/full-name", async (HttpContext ctx,
            [FromBody] ExtractionRequest req,
            ExtractionPipeline pipeline, CreditService credits, CancellationToken ct) =>
        {
            var cr = await credits.TryConsumeAsync(ctx.User, "validate_full_name", ctx, ct);
            credits.ApplyHeaders(ctx, cr);
            if (!cr.Success) return Results.Json(cr.ErrorPayload, statusCode: 429);
            return Results.Ok(await pipeline.ExecuteAsync("validate_full_name", req, ct));
        })
        .WithName("ExtractFullName")
        .WithSummary("Extrai nome completo com sobrenome")
        .WithDescription("Extrai o nome completo do usuário. Exige sobrenome — retorna certain: false se apenas o primeiro nome for informado. Custa 3 créditos.")
        .Produces<ExtractionResponse>().ProducesProblem(429).WithOpenApi();

        group.MapPost("/yes-no", async (HttpContext ctx,
            [FromBody] ExtractionRequest req,
            ExtractionPipeline pipeline, CreditService credits, CancellationToken ct) =>
        {
            var cr = await credits.TryConsumeAsync(ctx.User, "validate_yes_no", ctx, ct);
            credits.ApplyHeaders(ctx, cr);
            if (!cr.Success) return Results.Json(cr.ErrorPayload, statusCode: 429);
            return Results.Ok(await pipeline.ExecuteAsync("validate_yes_no", req, ct));
        })
        .WithName("ExtractYesNo")
        .WithSummary("Detecta sim ou não")
        .WithDescription("Retorna extracted_value='true' (sim), 'false' (não) ou null (ambíguo). Custa 2 créditos.")
        .Produces<ExtractionResponse>().ProducesProblem(429).WithOpenApi();

        group.MapPost("/birthdate", async (HttpContext ctx,
            [FromBody] ExtractionRequest req,
            ExtractionPipeline pipeline, CreditService credits, CancellationToken ct) =>
        {
            var cr = await credits.TryConsumeAsync(ctx.User, "validate_birthdate", ctx, ct);
            credits.ApplyHeaders(ctx, cr);
            if (!cr.Success) return Results.Json(cr.ErrorPayload, statusCode: 429);
            return Results.Ok(await pipeline.ExecuteAsync("validate_birthdate", req, ct));
        })
        .WithName("ExtractBirthdate")
        .WithSummary("Extrai data de nascimento")
        .WithDescription("Extrai data de nascimento em múltiplos formatos e idiomas, calcula idade atual. Custa 2 créditos.")
        .Produces<ExtractionResponse>().ProducesProblem(429).WithOpenApi();

        group.MapPost("/handoff", async (HttpContext ctx,
            [FromBody] ExtractionRequest req,
            ExtractionPipeline pipeline, CreditService credits, CancellationToken ct) =>
        {
            var cr = await credits.TryConsumeAsync(ctx.User, "validate_handoff", ctx, ct);
            credits.ApplyHeaders(ctx, cr);
            if (!cr.Success) return Results.Json(cr.ErrorPayload, statusCode: 429);
            return Results.Ok(await pipeline.ExecuteAsync("validate_handoff", req, ct));
        })
        .WithName("ExtractHandoff")
        .WithSummary("Detecta intenção de falar com humano")
        .WithDescription("Classifica wants_human (true/false), urgência (low/medium/high) e frustração. Custa 2 créditos.")
        .Produces<ExtractionResponse>().ProducesProblem(429).WithOpenApi();

        group.MapPost("/cancel-intent", async (HttpContext ctx,
            [FromBody] ExtractionRequest req,
            ExtractionPipeline pipeline, CreditService credits, CancellationToken ct) =>
        {
            var cr = await credits.TryConsumeAsync(ctx.User, "validate_cancel_intent", ctx, ct);
            credits.ApplyHeaders(ctx, cr);
            if (!cr.Success) return Results.Json(cr.ErrorPayload, statusCode: 429);
            return Results.Ok(await pipeline.ExecuteAsync("validate_cancel_intent", req, ct));
        })
        .WithName("ExtractCancelIntent")
        .WithSummary("Detecta intenção de cancelamento")
        .WithDescription("Diferencia cancelamento de serviço, operação atual ou frustração momentânea. Custa 2 créditos.")
        .Produces<ExtractionResponse>().ProducesProblem(429).WithOpenApi();

        group.MapPost("/company-name", async (HttpContext ctx,
            [FromBody] ExtractionRequest req,
            ExtractionPipeline pipeline, CreditService credits, CancellationToken ct) =>
        {
            var cr = await credits.TryConsumeAsync(ctx.User, "validate_company_name", ctx, ct);
            credits.ApplyHeaders(ctx, cr);
            if (!cr.Success) return Results.Json(cr.ErrorPayload, statusCode: 429);
            return Results.Ok(await pipeline.ExecuteAsync("validate_company_name", req, ct));
        })
        .WithName("ExtractCompanyName")
        .WithSummary("Extrai nome de empresa")
        .WithDescription("Detecta sufixos legais (LTDA, ME, S/A, LLC, Inc, GmbH). Custa 2 créditos.")
        .Produces<ExtractionResponse>().ProducesProblem(429).WithOpenApi();

        // ── Heavy LLM (5 credits) — validate_reply ────────────────────────────

        group.MapPost("/reply", async (HttpContext ctx,
            [FromBody] ReplyRequest req,
            ReplyService replyService, CreditService credits, CancellationToken ct) =>
        {
            var cr = await credits.TryConsumeAsync(ctx.User, "validate_reply", ctx, ct);
            credits.ApplyHeaders(ctx, cr);
            if (!cr.Success) return Results.Json(cr.ErrorPayload, statusCode: 429);

            try
            {
                var response = await replyService.AnalyzeAsync(req, ct);
                return Results.Ok(response);
            }
            catch (ServiceUnavailableException)
            {
                ctx.Response.Headers["Retry-After"] = "30";
                return Results.StatusCode(503);
            }
        })
        .WithName("ExtractReply")
        .WithSummary("Analisa contexto conversacional")
        .WithDescription(
            "Analisa a relação entre a mensagem do agente e a resposta do usuário. " +
            "Classifica o tipo (answer, question, counter_proposal, confirmation, rejection, " +
            "off_topic, greeting, handoff, cancel, unclear), extrai o significado real e sugere " +
            "a próxima fala. Resolve o bug das 48 parcelas vs R$48. Custa 5 créditos.")
        .Produces<ReplyResponse>().ProducesProblem(429).ProducesProblem(503).WithOpenApi();
    }
}
