using System;
using System.Net;
using AiLogia.Auth.Data;
using AiLogia.Auth.Services;
using Microsoft.AspNetCore.Mvc;

namespace AiLogia.Auth.Features.MagicLinks;

public static class MagicLinkEndpoints
{
    public static void MapMagicLinks(this IEndpointRouteBuilder app)
    {
        var grp = app.MapGroup("/magic-links");

        // Create (bots) — protected by X-Api-Key
        grp.MapPost("", async (
            [FromHeader(Name = "X-Api-Key")] string apiKey, 
            [FromBody] CreateMagicLinkRequest req,
            IConfiguration cfg,
            AuthDb db,
            SimpleRateLimiter rl,
            HttpContext http,
            CancellationToken ct) =>
        {
            var opts = cfg.GetSection("Auth").Get<AuthOptions>()!;
            if (string.IsNullOrEmpty(apiKey) || apiKey != opts.ApiKeyForBots)
                return Results.Unauthorized();

            var ttlMin = req.ttl_minutes ?? opts.DefaultTtlMinutes;
            if (ttlMin < 1 || ttlMin > 120) ttlMin = opts.DefaultTtlMinutes;

            // rate limit (e.g., 1 per 30s)
            if (!rl.TryHit(req.telegram_user_id, out var retry))
                return Results.Problem($"Too many requests. Retry after {retry.TotalSeconds:F0}s", statusCode: 429);

            var userId = await db.UpsertUserAsync(req.telegram_user_id, req.username, req.full_name, ct);

            var token = Crypto.GenerateTokenBase64Url(32);
            var tokenHash = AuthDb.Sha256(token);
            var expires = DateTime.UtcNow.AddMinutes(ttlMin);

            var ipStr = http.Request.Headers["X-Forwarded-For"].FirstOrDefault()
            ?? http.Connection.RemoteIpAddress?.ToString();

            IPAddress? ipAddr = null;
            if (!string.IsNullOrWhiteSpace(ipStr) && IPAddress.TryParse(ipStr, out var parsed))
                ipAddr = parsed;

            var ua = http.Request.Headers.UserAgent.ToString();

            await db.InsertMagicLinkAsync(userId, tokenHash, expires, ipAddr, ua, ct);

            return Results.Ok(new CreateMagicLinkResponse(token, expires));
        });

        // Validate (sites)
        grp.MapGet("validate", async ([FromQuery] string token, AuthDb db, IConfiguration cfg, CancellationToken ct) =>
        {
            if (string.IsNullOrWhiteSpace(token))
                return Results.BadRequest("missing token");

            var tokenHash = AuthDb.Sha256(token);
            var (found, userId, exp, used, tg, uname, fname) = await db.GetLinkWithUserAsync(tokenHash, ct);
            if (!found) return Results.Ok(new ValidateMagicLinkResponse(false, "invalid", null, null));
            if (used != null) return Results.Ok(new ValidateMagicLinkResponse(false, "used", null, null));

            var skew = cfg.GetSection("Auth").Get<AuthOptions>()!.AllowedClockSkewSeconds;
            if (DateTime.UtcNow > exp.AddSeconds(skew))
                return Results.Ok(new ValidateMagicLinkResponse(false, "expired", null, exp));

            var user = new MagicUser(userId, tg, uname, fname);
            return Results.Ok(new ValidateMagicLinkResponse(true, null, user, exp));
        });

        // Consume (sites)
        grp.MapPost("consume", async ([FromBody] ConsumeMagicLinkRequest body, AuthDb db, CancellationToken ct) =>
        {
            if (string.IsNullOrWhiteSpace(body.token))
                return Results.BadRequest("missing token");

            var tokenHash = AuthDb.Sha256(body.token);
            var ok = await db.ConsumeAsync(tokenHash, ct);
            return Results.Ok(new ConsumeMagicLinkResponse(ok));
        });
    }
}