using ColaFlow.Modules.Identity.Application.Commands.AssignUserRole;
using ColaFlow.Modules.Identity.Application.Commands.RemoveUserFromTenant;
using ColaFlow.Modules.Identity.Application.Commands.UpdateUserRole;
using ColaFlow.Modules.Identity.Application.Queries.ListTenantUsers;
using ColaFlow.Modules.Identity.Application.Dtos;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace ColaFlow.API.Controllers;

[ApiController]
[Route("api/tenants/{tenantId}/users")]
[Authorize]
public class TenantUsersController(IMediator mediator) : ControllerBase
{
    /// <summary>
    /// List all users in a tenant with their roles
    /// </summary>
    [HttpGet]
    [Authorize(Policy = "RequireTenantAdmin")]
    public async Task<IActionResult> ListUsers(
        [FromRoute] Guid tenantId,
        [FromQuery] int pageNumber = 1,
        [FromQuery] int pageSize = 20,
        [FromQuery] string? search = null)
    {
        // SECURITY: Validate user belongs to target tenant
        var userTenantIdClaim = User.FindFirst("tenant_id")?.Value;
        if (userTenantIdClaim == null)
            return Unauthorized(new { error = "Tenant information not found in token" });

        var userTenantId = Guid.Parse(userTenantIdClaim);
        if (userTenantId != tenantId)
            return StatusCode(403, new { error = "Access denied: You can only manage users in your own tenant" });

        var query = new ListTenantUsersQuery(tenantId, pageNumber, pageSize, search);
        var result = await mediator.Send(query);
        return Ok(result);
    }

    /// <summary>
    /// Assign or update a user's role in the tenant
    /// </summary>
    [HttpPost("{userId}/role")]
    [Authorize(Policy = "RequireTenantOwner")]
    public async Task<IActionResult> AssignRole(
        [FromRoute] Guid tenantId,
        [FromRoute] Guid userId,
        [FromBody] AssignRoleRequest request)
    {
        // SECURITY: Validate user belongs to target tenant
        var userTenantIdClaim = User.FindFirst("tenant_id")?.Value;
        if (userTenantIdClaim == null)
            return Unauthorized(new { error = "Tenant information not found in token" });

        var userTenantId = Guid.Parse(userTenantIdClaim);
        if (userTenantId != tenantId)
            return StatusCode(403, new { error = "Access denied: You can only manage users in your own tenant" });

        // Extract current user ID from claims
        var currentUserIdClaim = User.FindFirst("user_id")?.Value;
        if (currentUserIdClaim == null)
            return Unauthorized(new { error = "User ID not found in token" });

        var currentUserId = Guid.Parse(currentUserIdClaim);

        var command = new AssignUserRoleCommand(tenantId, userId, request.Role, currentUserId);
        await mediator.Send(command);
        return Ok(new { Message = "Role assigned successfully" });
    }

    /// <summary>
    /// Update an existing user's role in the tenant (RESTful PUT endpoint)
    /// </summary>
    [HttpPut("{userId:guid}/role")]
    [Authorize(Policy = "RequireTenantOwner")]
    public async Task<ActionResult<UserWithRoleDto>> UpdateRole(
        [FromRoute] Guid tenantId,
        [FromRoute] Guid userId,
        [FromBody] AssignRoleRequest request)
    {
        // SECURITY: Validate user belongs to target tenant
        var userTenantIdClaim = User.FindFirst("tenant_id")?.Value;
        if (userTenantIdClaim == null)
            return Unauthorized(new { error = "Tenant information not found in token" });

        var userTenantId = Guid.Parse(userTenantIdClaim);
        if (userTenantId != tenantId)
            return StatusCode(403, new { error = "Access denied: You can only manage users in your own tenant" });

        // Extract current user ID from claims
        var currentUserIdClaim = User.FindFirst("user_id")?.Value;
        if (currentUserIdClaim == null)
            return Unauthorized(new { error = "User ID not found in token" });

        var currentUserId = Guid.Parse(currentUserIdClaim);

        try
        {
            var command = new UpdateUserRoleCommand(tenantId, userId, request.Role, currentUserId);
            var result = await mediator.Send(command);
            return Ok(result);
        }
        catch (InvalidOperationException ex)
        {
            return BadRequest(new { error = ex.Message });
        }
        catch (ArgumentException ex)
        {
            return BadRequest(new { error = ex.Message });
        }
    }

    /// <summary>
    /// Remove a user from the tenant
    /// </summary>
    [HttpDelete("{userId}")]
    [Authorize(Policy = "RequireTenantOwner")]
    public async Task<IActionResult> RemoveUser(
        [FromRoute] Guid tenantId,
        [FromRoute] Guid userId)
    {
        // SECURITY: Validate user belongs to target tenant
        var userTenantIdClaim = User.FindFirst("tenant_id")?.Value;
        if (userTenantIdClaim == null)
            return Unauthorized(new { error = "Tenant information not found in token" });

        var userTenantId = Guid.Parse(userTenantIdClaim);
        if (userTenantId != tenantId)
            return StatusCode(403, new { error = "Access denied: You can only manage users in your own tenant" });

        // Extract current user ID from claims
        var currentUserIdClaim = User.FindFirst("user_id")?.Value;
        if (currentUserIdClaim == null)
            return Unauthorized(new { error = "User ID not found in token" });

        var currentUserId = Guid.Parse(currentUserIdClaim);

        var command = new RemoveUserFromTenantCommand(tenantId, userId, currentUserId, null);
        await mediator.Send(command);
        return Ok(new { Message = "User removed from tenant successfully" });
    }

    /// <summary>
    /// Get available roles (Note: This endpoint doesn't use tenantId from route, so tenant validation is skipped.
    /// It only returns static role definitions, not tenant-specific data.)
    /// </summary>
    [HttpGet("../roles")]
    [Authorize(Policy = "RequireTenantAdmin")]
    public IActionResult GetAvailableRoles()
    {
        var roles = new[]
        {
            new { Name = "TenantOwner", Description = "Full control over the tenant" },
            new { Name = "TenantAdmin", Description = "Manage users and projects" },
            new { Name = "TenantMember", Description = "Create and edit tasks" },
            new { Name = "TenantGuest", Description = "Read-only access" }
        };

        return Ok(roles);
    }
}

public record AssignRoleRequest(string Role);
