using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Hua.Todo.Application.CloudSync.Auth;
using Hua.Todo.Application.CloudSync.Models;
using Hua.Todo.Application.CloudSync.Services;

namespace Hua.Todo.Application.CloudSync;

/// <summary>
/// 云同步服务端 API 路由注册扩展。
/// </summary>
public static class CloudSyncEndpointExtensions
{
    /// <summary>
    /// 映射云同步相关 API 端点。
    /// </summary>
    /// <param name="app">Web 应用。</param>
    /// <returns>Web 应用。</returns>
    public static WebApplication MapCloudSyncEndpoints(this WebApplication app)
    {
        var auth = app.MapGroup("/auth").WithTags("CloudSync - Auth");
        auth.MapPost("/bootstrap", BootstrapAdminAsync).AllowAnonymous();
        auth.MapPost("/login", LoginAsync).AllowAnonymous();
        auth.MapPost("/step-up", StepUpAsync).AllowAnonymous();

        var tasks = app.MapGroup("/tasks").WithTags("CloudSync - Tasks");
        tasks.MapGet("/", GetTasksAsync).AllowAnonymous();

        var sync = app.MapGroup("/sync").WithTags("CloudSync - Sync");
        sync.MapPost("/", SyncAsync).AllowAnonymous();

        var security = app.MapGroup("/security").WithTags("CloudSync - Security");
        security.MapGet("/policy", GetPolicyAsync).AllowAnonymous();
        security.MapPut("/policy", UpdatePolicyAsync).AllowAnonymous();

        return app;
    }

    private static async Task<IResult> BootstrapAdminAsync(
        BootstrapAdminRequest request,
        CloudAuthService authService,
        CancellationToken cancellationToken)
    {
        if (request == null || string.IsNullOrWhiteSpace(request.UserName) || string.IsNullOrWhiteSpace(request.Password))
        {
            return CloudApiErrors.BadRequest("UserName and Password are required.");
        }

        var ok = await authService.BootstrapAdminAsync(request.UserName, request.Password, cancellationToken);
        if (!ok)
        {
            return CloudApiErrors.Forbidden("Bootstrap is not allowed (already initialized or invalid input).");
        }

        return Results.Ok();
    }

    private static async Task<IResult> LoginAsync(
        LoginRequest request,
        CloudAuthService authService,
        CancellationToken cancellationToken)
    {
        if (request == null || string.IsNullOrWhiteSpace(request.UserName) || string.IsNullOrWhiteSpace(request.Password))
        {
            return CloudApiErrors.BadRequest("UserName and Password are required.");
        }

        var response = await authService.LoginAsync(request.UserName, request.Password, TimeSpan.FromDays(7), cancellationToken);
        if (response == null)
        {
            return CloudApiErrors.Unauthorized("Invalid credentials.");
        }

        return Results.Json(response);
    }

    private static async Task<IResult> StepUpAsync(
        StepUpRequest request,
        CloudAuthService authService,
        HttpContext httpContext,
        CancellationToken cancellationToken)
    {
        var sessionId = httpContext.User.GetSessionId();
        if (sessionId == null)
        {
            return CloudApiErrors.Unauthorized();
        }

        if (request == null || string.IsNullOrWhiteSpace(request.Password))
        {
            return CloudApiErrors.BadRequest("Password is required.");
        }

        var expiresAt = await authService.StepUpAsync(sessionId.Value, request.Password, TimeSpan.FromMinutes(5), cancellationToken);
        if (!expiresAt.HasValue)
        {
            return CloudApiErrors.Unauthorized("Invalid credentials or session expired.");
        }

        return Results.Json(new StepUpResponse { StepUpExpiresAtUtc = expiresAt.Value });
    }

    private static async Task<IResult> GetTasksAsync(
        CloudTaskSyncService taskService,
        HttpContext httpContext,
        CancellationToken cancellationToken)
    {
        var userId = httpContext.User.GetUserId();
        if (userId == null)
        {
            return CloudApiErrors.Unauthorized();
        }

        if (!httpContext.User.HasPermission(CloudPermissions.TasksRead))
        {
            return CloudApiErrors.Forbidden();
        }

        var tasks = await taskService.GetTasksAsync(userId.Value, cancellationToken);
        return Results.Json(tasks);
    }

    private static async Task<IResult> SyncAsync(
        SyncRequest request,
        CloudTaskSyncService taskService,
        HttpContext httpContext,
        CancellationToken cancellationToken)
    {
        var userId = httpContext.User.GetUserId();
        if (userId == null)
        {
            return CloudApiErrors.Unauthorized();
        }

        if (!httpContext.User.HasPermission(CloudPermissions.SyncWrite))
        {
            return CloudApiErrors.Forbidden();
        }

        if (!httpContext.User.HasStepUp())
        {
            return CloudApiErrors.SecondFactorRequired();
        }

        var response = await taskService.SyncAsync(userId.Value, request, cancellationToken);
        return Results.Json(response);
    }

    private static async Task<IResult> GetPolicyAsync(
        SecurityPolicyService policyService,
        HttpContext httpContext,
        CancellationToken cancellationToken)
    {
        var userId = httpContext.User.GetUserId();
        if (userId == null)
        {
            return CloudApiErrors.Unauthorized();
        }

        if (!httpContext.User.HasPermission(CloudPermissions.PolicyRead))
        {
            return CloudApiErrors.Forbidden();
        }

        var policy = await policyService.GetPolicyAsync(userId.Value, cancellationToken);
        return Results.Json(policy);
    }

    private static async Task<IResult> UpdatePolicyAsync(
        UpdateSecurityPolicyRequest request,
        SecurityPolicyService policyService,
        HttpContext httpContext,
        CancellationToken cancellationToken)
    {
        var userId = httpContext.User.GetUserId();
        if (userId == null)
        {
            return CloudApiErrors.Unauthorized();
        }

        if (!httpContext.User.HasPermission(CloudPermissions.PolicyWrite))
        {
            return CloudApiErrors.Forbidden();
        }

        if (!httpContext.User.HasStepUp())
        {
            return CloudApiErrors.SecondFactorRequired();
        }

        var policy = await policyService.UpdatePolicyAsync(userId.Value, request.AllowPersist, request.AllowSync, cancellationToken);
        return Results.Json(policy);
    }
}
