using Eis.Application.Candidate;
using Eis.Infrastructure.Candidate;

namespace Eis.Web.Candidate;

public static class NativeCandidateEndpoints
{
    public static IEndpointRouteBuilder MapNativeCandidateEndpoints(
        this IEndpointRouteBuilder endpoints,
        CandidateMigrationOptions options)
    {
        if (!options.NativeEnabled)
        {
            return endpoints;
        }

        endpoints.MapGet("/api/candidate/dashboard", async (
            HttpContext context,
            ICandidateService service,
            CancellationToken cancellationToken) => ToResult(
                context,
                await service.GetDashboardAsync(SessionToken(context), cancellationToken)));
        endpoints.MapGet("/api/candidate/notices", async (
            HttpContext context,
            ICandidateService service,
            CancellationToken cancellationToken) => ToResult(
                context,
                await service.GetNoticesAsync(SessionToken(context), cancellationToken)));
        endpoints.MapGet("/api/candidate/profile", async (
            HttpContext context,
            ICandidateService service,
            CancellationToken cancellationToken) => ToResult(
                context,
                await service.GetProfileAsync(SessionToken(context), cancellationToken)));
        endpoints.MapPut("/api/candidate/profile", async (
            HttpContext context,
            System.Text.Json.Nodes.JsonObject request,
            ICandidateService service,
            CancellationToken cancellationToken) => ToResult(
                context,
                await service.UpdateProfileAsync(SessionToken(context), request, cancellationToken)));
        endpoints.MapGet("/api/candidate/exams", async (
            HttpContext context,
            ICandidateService service,
            CancellationToken cancellationToken) => ToResult(
                context,
                await service.GetExamsAsync(SessionToken(context), cancellationToken)));
        endpoints.MapGet("/api/candidate/registrations", async (
            HttpContext context,
            ICandidateService service,
            CancellationToken cancellationToken) => ToResult(
                context,
                await service.GetRegistrationsAsync(SessionToken(context), cancellationToken)));
        endpoints.MapPost("/api/candidate/registrations", async (
            HttpContext context,
            System.Text.Json.Nodes.JsonObject request,
            ICandidateService service,
            CancellationToken cancellationToken) => ToResult(
                context,
                await service.CreateRegistrationAsync(SessionToken(context), request, cancellationToken)));
        endpoints.MapGet("/api/candidate/results", async (
            HttpContext context,
            ICandidateService service,
            CancellationToken cancellationToken) => ToResult(
                context,
                await service.GetResultsAsync(
                    SessionToken(context),
                    VerificationBaseUrl(context),
                    cancellationToken)));
        endpoints.MapGet("/api/candidate/admissions", async (
            HttpContext context,
            ICandidateService service,
            CancellationToken cancellationToken) => ToResult(
                context,
                await service.GetAdmissionsAsync(
                    SessionToken(context),
                    VerificationBaseUrl(context),
                    cancellationToken)));
        endpoints.MapPut("/api/candidate/admissions/{examId}/preferences", async (
            HttpContext context,
            string examId,
            System.Text.Json.Nodes.JsonObject request,
            ICandidateService service,
            CancellationToken cancellationToken) => ToResult(
                context,
                await service.UpdateAdmissionPreferencesAsync(
                    SessionToken(context),
                    examId,
                    request,
                    cancellationToken)));
        endpoints.MapPost("/api/candidate/results/{resultId}/appeals", async (
            HttpContext context,
            string resultId,
            System.Text.Json.Nodes.JsonObject request,
            ICandidateService service,
            CancellationToken cancellationToken) => ToResult(
                context,
                await service.CreateScoreAppealAsync(
                    SessionToken(context),
                    resultId,
                    request,
                    cancellationToken)));
        endpoints.MapGet("/api/candidate/registrations/{registrationId}/admit-card", async (
            HttpContext context,
            string registrationId,
            ICandidateService service,
            CancellationToken cancellationToken) => ToDocumentResult(
                context,
                await service.GetAdmitCardAsync(SessionToken(context), registrationId, cancellationToken)));

        return endpoints;
    }

    private static string SessionToken(HttpContext context) =>
        context.Request.Cookies.TryGetValue("hz_session", out var token) ? token : string.Empty;

    private static string VerificationBaseUrl(HttpContext context)
    {
        var forwarded = context.Request.Headers["X-Forwarded-Proto"].ToString();
        var protocol = forwarded.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).FirstOrDefault()
            ?? context.Request.Scheme;
        return $"{protocol}://{context.Request.Host.Value}";
    }

    private static IResult ToResult(HttpContext context, CandidateEndpointResult result)
    {
        context.Response.Headers.CacheControl = "no-store";
        context.Response.Headers["X-EIS-Implementation"] = "aspnet-core";
        return Results.Json(result.Body, statusCode: result.StatusCode);
    }

    private static IResult ToDocumentResult(HttpContext context, CandidateDocumentResult result)
    {
        context.Response.Headers.CacheControl = "no-store";
        context.Response.Headers["X-EIS-Implementation"] = "aspnet-core";
        if (result.Error is not null)
        {
            return Results.Json(result.Error, statusCode: result.StatusCode);
        }

        context.Response.Headers.ContentDisposition =
            $"attachment; filename*=UTF-8''{Uri.EscapeDataString(result.FileName ?? "admit-card.html")}";
        return Results.Content(result.Content ?? string.Empty, "text/html; charset=utf-8", statusCode: result.StatusCode);
    }
}
