using ColaFlow.Modules.Mcp.Application.DTOs;
using ColaFlow.Modules.Mcp.Application.Services;
using ColaFlow.Modules.Mcp.Domain.ValueObjects;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;

namespace ColaFlow.API.Controllers;

/// <summary>
/// Controller for managing PendingChanges (AI-proposed changes awaiting approval)
/// Requires JWT authentication
/// </summary>
[ApiController]
[Route("api/mcp/pending-changes")]
[Authorize] // Requires JWT authentication
public class McpPendingChangesController : ControllerBase
{
    private readonly IPendingChangeService _pendingChangeService;
    private readonly ILogger<McpPendingChangesController> _logger;

    public McpPendingChangesController(
        IPendingChangeService pendingChangeService,
        ILogger<McpPendingChangesController> logger)
    {
        _pendingChangeService = pendingChangeService ?? throw new ArgumentNullException(nameof(pendingChangeService));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    /// <summary>
    /// Get list of pending changes with filtering and pagination
    /// </summary>
    [HttpGet]
    [ProducesResponseType(typeof(PendingChangeListResponse), 200)]
    [ProducesResponseType(401)]
    public async Task<IActionResult> GetPendingChanges(
        [FromQuery] string? status = null,
        [FromQuery] string? entityType = null,
        [FromQuery] Guid? entityId = null,
        [FromQuery] Guid? apiKeyId = null,
        [FromQuery] string? toolName = null,
        [FromQuery] bool? includeExpired = null,
        [FromQuery] int page = 1,
        [FromQuery] int pageSize = 20)
    {
        try
        {
            var filter = new PendingChangeFilterDto
            {
                Status = string.IsNullOrWhiteSpace(status) ? null : Enum.Parse<PendingChangeStatus>(status, true),
                EntityType = entityType,
                EntityId = entityId,
                ApiKeyId = apiKeyId,
                ToolName = toolName,
                IncludeExpired = includeExpired,
                Page = page,
                PageSize = Math.Min(pageSize, 100) // Max 100 items per page
            };

            var (items, totalCount) = await _pendingChangeService.GetPendingChangesAsync(filter, HttpContext.RequestAborted);

            var response = new PendingChangeListResponse
            {
                Items = items,
                TotalCount = totalCount,
                Page = page,
                PageSize = pageSize,
                TotalPages = (int)Math.Ceiling((double)totalCount / pageSize)
            };

            return Ok(response);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to get pending changes");
            return StatusCode(500, new { message = "Failed to retrieve pending changes" });
        }
    }

    /// <summary>
    /// Get a specific pending change by ID
    /// </summary>
    [HttpGet("{id}")]
    [ProducesResponseType(typeof(PendingChangeDto), 200)]
    [ProducesResponseType(404)]
    [ProducesResponseType(401)]
    public async Task<IActionResult> GetPendingChange(Guid id)
    {
        try
        {
            var pendingChange = await _pendingChangeService.GetByIdAsync(id, HttpContext.RequestAborted);
            if (pendingChange == null)
            {
                return NotFound(new { message = "PendingChange not found" });
            }

            return Ok(pendingChange);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to get pending change {Id}", id);
            return StatusCode(500, new { message = "Failed to retrieve pending change" });
        }
    }

    /// <summary>
    /// Approve a pending change (will trigger execution)
    /// </summary>
    [HttpPost("{id}/approve")]
    [ProducesResponseType(200)]
    [ProducesResponseType(400)]
    [ProducesResponseType(404)]
    [ProducesResponseType(401)]
    public async Task<IActionResult> ApprovePendingChange(Guid id)
    {
        try
        {
            // Extract user ID from JWT claims
            var userId = GetUserIdFromClaims();

            await _pendingChangeService.ApproveAsync(id, userId, HttpContext.RequestAborted);

            _logger.LogInformation("PendingChange {Id} approved by User {UserId}", id, userId);

            return Ok(new { message = "PendingChange approved successfully. Execution in progress." });
        }
        catch (InvalidOperationException ex)
        {
            _logger.LogWarning(ex, "Cannot approve PendingChange {Id}", id);
            return BadRequest(new { message = ex.Message });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to approve PendingChange {Id}", id);
            return StatusCode(500, new { message = "Failed to approve pending change" });
        }
    }

    /// <summary>
    /// Reject a pending change
    /// </summary>
    [HttpPost("{id}/reject")]
    [ProducesResponseType(200)]
    [ProducesResponseType(400)]
    [ProducesResponseType(404)]
    [ProducesResponseType(401)]
    public async Task<IActionResult> RejectPendingChange(Guid id, [FromBody] RejectChangeRequest request)
    {
        try
        {
            if (string.IsNullOrWhiteSpace(request.Reason))
            {
                return BadRequest(new { message = "Rejection reason is required" });
            }

            // Extract user ID from JWT claims
            var userId = GetUserIdFromClaims();

            await _pendingChangeService.RejectAsync(id, userId, request.Reason, HttpContext.RequestAborted);

            _logger.LogInformation("PendingChange {Id} rejected by User {UserId}", id, userId);

            return Ok(new { message = "PendingChange rejected successfully" });
        }
        catch (InvalidOperationException ex)
        {
            _logger.LogWarning(ex, "Cannot reject PendingChange {Id}", id);
            return BadRequest(new { message = ex.Message });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to reject PendingChange {Id}", id);
            return StatusCode(500, new { message = "Failed to reject pending change" });
        }
    }

    /// <summary>
    /// Delete a pending change (only allowed for Expired or Rejected status)
    /// </summary>
    [HttpDelete("{id}")]
    [ProducesResponseType(204)]
    [ProducesResponseType(400)]
    [ProducesResponseType(404)]
    [ProducesResponseType(401)]
    public async Task<IActionResult> DeletePendingChange(Guid id)
    {
        try
        {
            await _pendingChangeService.DeleteAsync(id, HttpContext.RequestAborted);

            _logger.LogInformation("PendingChange {Id} deleted", id);

            return NoContent();
        }
        catch (InvalidOperationException ex)
        {
            _logger.LogWarning(ex, "Cannot delete PendingChange {Id}", id);
            return BadRequest(new { message = ex.Message });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to delete PendingChange {Id}", id);
            return StatusCode(500, new { message = "Failed to delete pending change" });
        }
    }

    // Helper method to extract user ID from claims
    private Guid GetUserIdFromClaims()
    {
        var userIdClaim = User.FindFirstValue("user_id")
            ?? User.FindFirstValue(ClaimTypes.NameIdentifier)
            ?? User.FindFirstValue("sub")
            ?? throw new UnauthorizedAccessException("User ID not found in token");

        return Guid.Parse(userIdClaim);
    }
}

/// <summary>
/// Response for paginated list of pending changes
/// </summary>
public class PendingChangeListResponse
{
    public List<PendingChangeDto> Items { get; set; } = new();
    public int TotalCount { get; set; }
    public int Page { get; set; }
    public int PageSize { get; set; }
    public int TotalPages { get; set; }
}
