using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;
using Nexus.Api.Models;
using Nexus.Api.Services;
using System.Security.Claims;

namespace Nexus.Api.Controllers;

/// <summary>
/// Owner-only orchestration for the single OpenClaw Attach &amp; Adopt profile.
/// The browser never receives Gateway, bootstrap or provider credentials.
/// </summary>
[Authorize(Roles = "owner")]
[ApiController]
[Route("api/v1/openclaw/setup")]
public sealed class OpenClawSetupController(
    IOpenClawSetupService setup,
    IOpenClawWizardService wizard) : ControllerBase
{
    [HttpGet]
    public async Task<ActionResult<OpenClawSetupStatusDto>> GetStatus(
        CancellationToken cancellationToken)
        => Ok(await setup.GetStatusAsync(cancellationToken));

    [HttpPost("discover")]
    [EnableRateLimiting("agents")]
    public async Task<ActionResult<OpenClawDiscoveryDto>> Discover(
        [FromBody] OpenClawDiscoveryRequest? request,
        CancellationToken cancellationToken)
        => Ok(await setup.DiscoverAsync(
            request ?? new OpenClawDiscoveryRequest(),
            cancellationToken));

    [HttpPost("probe")]
    [EnableRateLimiting("agents")]
    public async Task<ActionResult<OpenClawSetupOperationDto<OpenClawProbeDto>>> Probe(
        [FromBody] ProbeOpenClawRequest request,
        CancellationToken cancellationToken)
        => Map(await setup.ProbeAsync(request, cancellationToken));

    [HttpPost("attach")]
    [EnableRateLimiting("agents")]
    public async Task<ActionResult<OpenClawSetupOperationDto<OpenClawSetupStatusDto>>> Attach(
        [FromBody] AttachOpenClawRequest request,
        CancellationToken cancellationToken)
        => Map(await setup.AttachAsync(request, cancellationToken));

    [HttpPost("verify")]
    [EnableRateLimiting("agents")]
    public async Task<ActionResult<OpenClawSetupOperationDto<OpenClawSetupStatusDto>>> Verify(
        [FromBody] VerifyOpenClawRequest request,
        CancellationToken cancellationToken)
        => Map(await setup.VerifyAsync(request, cancellationToken));

    [HttpPost("adopt")]
    [EnableRateLimiting("agents")]
    public async Task<ActionResult<OpenClawSetupOperationDto<OpenClawAdoptionInventoryDto>>> Adopt(
        [FromBody] AdoptOpenClawRequest request,
        CancellationToken cancellationToken)
        => Map(await setup.AdoptAsync(request, cancellationToken));

    [HttpPost("management")]
    [EnableRateLimiting("agents")]
    public async Task<ActionResult<OpenClawSetupOperationDto<OpenClawSetupStatusDto>>> SetManagement(
        [FromBody] SetOpenClawManagementRequest request,
        CancellationToken cancellationToken)
        => Map(await setup.SetManagementAsync(request, cancellationToken));

    [HttpDelete("connection")]
    [EnableRateLimiting("agents")]
    public async Task<ActionResult<OpenClawSetupOperationDto<OpenClawSetupStatusDto>>> DeleteConnection(
        [FromBody] DeleteOpenClawConnectionRequest request,
        CancellationToken cancellationToken)
        => Map(await setup.DeleteAsync(request, cancellationToken));

    [HttpPost("wizard/start")]
    [EnableRateLimiting("agents")]
    public async Task<ActionResult<OpenClawWizardResultDto>> StartWizard(
        [FromBody] StartOpenClawWizardRequest request,
        CancellationToken cancellationToken)
    {
        if (!TryBuildInvocation(out var invocation, out var validationError))
            return validationError!;
        return MapWizard(await wizard.StartAsync(
            request,
            invocation!,
            cancellationToken));
    }

    [HttpPost("wizard/next")]
    [EnableRateLimiting("agents")]
    public async Task<ActionResult<OpenClawWizardResultDto>> AdvanceWizard(
        [FromBody] AdvanceOpenClawWizardRequest request,
        CancellationToken cancellationToken)
    {
        if (!TryBuildInvocation(out var invocation, out var validationError))
            return validationError!;
        return MapWizard(await wizard.NextAsync(
            request,
            invocation!,
            cancellationToken));
    }

    [HttpGet("wizard/{sessionId}")]
    public async Task<ActionResult<OpenClawWizardResultDto>> GetWizardStatus(
        string sessionId,
        CancellationToken cancellationToken)
        => MapWizard(await wizard.GetStatusAsync(sessionId, cancellationToken));

    [HttpPost("wizard/{sessionId}/cancel")]
    [EnableRateLimiting("agents")]
    public async Task<ActionResult<OpenClawWizardResultDto>> CancelWizard(
        string sessionId,
        CancellationToken cancellationToken)
    {
        if (!TryBuildInvocation(out var invocation, out var validationError))
            return validationError!;
        return MapWizard(await wizard.CancelAsync(
            sessionId,
            invocation!,
            cancellationToken));
    }

    private ActionResult<OpenClawSetupOperationDto<T>> Map<T>(
        OpenClawSetupOperationDto<T> result)
    {
        if (result.Ok)
            return Ok(result);

        return StatusCode(StatusFor(result.State), result);
    }

    private ActionResult<OpenClawWizardResultDto> MapWizard(
        OpenClawWizardResultDto result)
    {
        if (result.Ok)
            return Ok(result);

        var status = result.State switch
        {
            "invalid" or "confirmation_required"
                => StatusCodes.Status400BadRequest,
            "not_found"
                => StatusCodes.Status404NotFound,
            "management_disabled" or "scope_upgrade_required"
                => StatusCodes.Status403Forbidden,
            "conflict" or "server_secret_required" or "unsupported"
                => StatusCodes.Status409Conflict,
            "disconnected" or "gateway_error"
                => StatusCodes.Status503ServiceUnavailable,
            _ => StatusCodes.Status502BadGateway
        };
        return StatusCode(status, result);
    }

    private bool TryBuildInvocation(
        out OpenClawInvocationContext? invocation,
        out BadRequestObjectResult? validationError)
    {
        invocation = null;
        validationError = null;
        var idempotencyKey = Request.Headers["Idempotency-Key"].FirstOrDefault()?.Trim();
        if (string.IsNullOrWhiteSpace(idempotencyKey) ||
            idempotencyKey.Length > 128 ||
            idempotencyKey.Any(char.IsControl))
        {
            validationError = BadRequest(new ValidationProblemDetails(
                new Dictionary<string, string[]>
                {
                    ["Idempotency-Key"] =
                    [
                        "A non-empty Idempotency-Key header with at most 128 non-control characters is required."
                    ]
                }));
            return false;
        }

        var actor = User.FindFirst("sub")?.Value
                    ?? User.FindFirst(ClaimTypes.NameIdentifier)?.Value
                    ?? User.FindFirst(ClaimTypes.Email)?.Value
                    ?? User.Identity?.Name
                    ?? "authenticated-owner";
        try
        {
            invocation = OpenClawInvocationContext.Create(
                actor,
                idempotencyKey,
                Request.Headers["X-Correlation-ID"].FirstOrDefault(),
                Request.Headers["traceparent"].FirstOrDefault(),
                includeIdempotencyParameter: false);
        }
        catch (ArgumentException exception)
        {
            validationError = BadRequest(new ValidationProblemDetails(
                new Dictionary<string, string[]>
                {
                    ["request"] = [exception.Message]
                }));
            return false;
        }

        Response.Headers["Idempotency-Key"] = invocation.IdempotencyKey;
        Response.Headers["X-Correlation-ID"] = invocation.CorrelationId;
        return true;
    }

    private static int StatusFor(string state)
        => state switch
        {
            OpenClawSetupStates.InvalidEndpoint
                or OpenClawSetupStates.InvalidRequest
                => StatusCodes.Status400BadRequest,
            OpenClawSetupStates.NotFound
                => StatusCodes.Status404NotFound,
            OpenClawSetupStates.ConcurrencyConflict
                or OpenClawSetupStates.DynamicEndpointUnsupported
                or OpenClawSetupStates.ExperimentalBlocked
                or OpenClawSetupStates.ExcessiveScope
                or OpenClawSetupStates.PairingRequired
                or OpenClawSetupStates.ScopeUpgradeRequired
                => StatusCodes.Status409Conflict,
            OpenClawSetupStates.Disconnected
                or OpenClawSetupStates.GatewayUnavailable
                => StatusCodes.Status503ServiceUnavailable,
            _ => StatusCodes.Status502BadGateway
        };
}
