using GerbilManagerWebAPI.Common;
using GerbilManagerWebAPI.Dtos;
using GerbilManagerWebAPI.Inbox;
using GerbilManagerWebAPI.Models;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.EntityFrameworkCore;

namespace GerbilManagerWebAPI.Endpoints
{
    /// <summary>
    /// INBOX-0: Gmail request inbox. Sync imports mail into Request rows; the list/detail/triage
    /// endpoints drive the in-app triage (frontend INBOX-1). LAN-only like the rest of the API.
    /// </summary>
    public static class RequestEndpoints
    {
        public static IEndpointRouteBuilder MapRequestEndpoints(this IEndpointRouteBuilder app)
        {
            var api = app.MapGroup("/api").WithTags("Inbox");

            // POST /api/requests/sync — fetch from Gmail + import (dedup on Message-Id)
            api.MapPost("/requests/sync", async (RequestSyncService sync) =>
                TypedResults.Ok(await sync.SyncAsync()));

            // GET /api/requests?filter=status==New&orderBy=receivedAt desc (Gridify paged)
            api.MapGet("/requests", async ([Microsoft.AspNetCore.Http.AsParameters] GridifyParams query, ApplicationContext db) =>
                TypedResults.Ok(await db.Requests.AsNoTracking()
                    .ToPagedResultAsync(query, ToDto)));

            api.MapGet("/requests/{id:guid}", async Task<Results<Ok<RequestDto>, NotFound>> (Guid id, ApplicationContext db) =>
            {
                var r = await db.Requests.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id);
                return r is null ? TypedResults.NotFound() : TypedResults.Ok(ToDto(r));
            });

            // PUT /api/requests/{id} — triage: status and/or assigned contact
            api.MapPut("/requests/{id:guid}", async Task<Results<NoContent, NotFound, BadRequest<string>>> (
                Guid id, RequestTriageInput input, ApplicationContext db) =>
            {
                var r = await db.Requests.FirstOrDefaultAsync(x => x.Id == id);
                if (r is null) return TypedResults.NotFound();
                if (input.AssignedContactId is Guid cid && !await db.Contacts.AnyAsync(c => c.Id == cid))
                    return TypedResults.BadRequest("Assigned contact does not exist.");

                r.AssignedContactId = input.AssignedContactId;
                if (input.Status is RequestStatus s)
                {
                    r.Status = s;
                    if (s == RequestStatus.Answered) r.AnsweredAt ??= DateTimeOffset.UtcNow;
                }
                await db.SaveChangesAsync();
                return TypedResults.NoContent();
            });

            // POST /api/requests/{id}/draft — INBOX-2: KI-Antwortentwurf. NUR Entwurf
            // (human-in-the-loop, Versand ist /send). 503 AiKeyMissing solange die
            // AI-Sektion unkonfiguriert ist (gleiches UI-Muster wie sale-ad).
            api.MapPost("/requests/{id:guid}/draft", async Task<IResult> (
                Guid id, DraftReplyService drafter, ApplicationContext db, CancellationToken ct) =>
            {
                var r = await db.Requests.FirstOrDefaultAsync(x => x.Id == id, ct);
                if (r is null) return TypedResults.NotFound();

                // Datenminimierung: NUR die ForSale-Liste (Name + Farbschlag) geht
                // zusätzlich zum Anfragetext an den Anbieter (arch §privacy).
                var forSale = await db.Gerbils.AsNoTracking()
                    .Where(g => g.Status == GerbilStatus.ForSale)
                    .OrderBy(g => g.Name)
                    .Select(g => new DraftReplyService.ForSaleAnimal(g.Name, g.ColorVariety!.Name))
                    .ToListAsync(ct);

                var result = await drafter.DraftAsync(r, forSale, ct);
                if (result.Status == Ai.AiCallStatus.NotConfigured)
                    return Results.Json(new { code = "AiKeyMissing", message = result.Error }, statusCode: 503);
                if (result.Status != Ai.AiCallStatus.Ok)
                    return Results.Json(new { code = "AiUpstreamError", message = result.Error }, statusCode: 502);

                r.DraftReply = result.Text;
                await db.SaveChangesAsync(ct);
                return TypedResults.Ok(ToDto(r));
            });

            // POST /api/requests/{id}/send — send the (edited) reply, threaded, mark Answered
            api.MapPost("/requests/{id:guid}/send", async Task<Results<Ok<RequestDto>, NotFound, ProblemHttpResult>> (
                Guid id, SendReplyInput input, SendReplyService sender, ApplicationContext db) =>
            {
                var r = await db.Requests.FirstOrDefaultAsync(x => x.Id == id);
                if (r is null) return TypedResults.NotFound();

                var outcome = await sender.SendAsync(r, input.Body ?? "");
                return outcome.Error switch
                {
                    null => TypedResults.Ok(ToDto(r)),
                    "MailNotConfigured" => TypedResults.Problem(
                        "Gmail ist noch nicht konfiguriert.", statusCode: 503, title: "MailNotConfigured"),
                    "MailAuthFailed" => TypedResults.Problem(
                        "Gmail-Anmeldung fehlgeschlagen — App-Passwort erneut eingeben.", statusCode: 502, title: "MailAuthFailed"),
                    var code => TypedResults.Problem(code, statusCode: 500, title: code),
                };
            });

            // GET/PUT /api/mail-settings — App Password never leaves the server
            api.MapGet("/mail-settings", async (MailSettingsService svc) =>
            {
                var s = await svc.GetAsync();
                return TypedResults.Ok(new MailSettingsDto(
                    s.GmailAddress, s.PollIntervalMinutes, s.Folder, s.BackgroundPollEnabled, svc.HasAppPassword(s)));
            });

            api.MapPut("/mail-settings", async (MailSettingsInput input, MailSettingsService svc, ApplicationContext db) =>
            {
                var s = await svc.GetAsync();
                if (input.GmailAddress is not null) s.GmailAddress = string.IsNullOrWhiteSpace(input.GmailAddress) ? null : input.GmailAddress.Trim();
                if (input.PollIntervalMinutes is int m) s.PollIntervalMinutes = m;
                if (input.Folder is not null) s.Folder = string.IsNullOrWhiteSpace(input.Folder) ? "INBOX" : input.Folder.Trim();
                if (input.BackgroundPollEnabled is bool b) s.BackgroundPollEnabled = b;
                // AppPassword: null => leave unchanged; "" => clear; value => set (encrypted)
                if (input.AppPassword is not null) svc.SetPassword(s, input.AppPassword);
                await db.SaveChangesAsync();
                return TypedResults.NoContent();
            });

            return app;
        }

        private static RequestDto ToDto(Request r) => new(
            r.Id, r.GmailMessageId, r.ThreadId, r.FromAddress, r.FromName, r.Subject, r.BodyText,
            r.ReceivedAt, r.Status, r.AssignedContactId, r.DraftReply, r.AnsweredAt);
    }
}
