using ColaFlow.API.Models;
using ColaFlow.Modules.Identity.Application.Commands.ForgotPassword;
using ColaFlow.Modules.Identity.Application.Commands.Login;
using ColaFlow.Modules.Identity.Application.Commands.ResetPassword;
using ColaFlow.Modules.Identity.Application.Commands.VerifyEmail;
using ColaFlow.Modules.Identity.Application.Commands.ResendVerificationEmail;
using ColaFlow.Modules.Identity.Application.Commands.AcceptInvitation;
using ColaFlow.Modules.Identity.Application.Services;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.Security.Claims;

namespace ColaFlow.API.Controllers;

[ApiController]
[Route("api/[controller]")]
public class AuthController(
    IMediator mediator,
    IRefreshTokenService refreshTokenService,
    ILogger<AuthController> logger)
    : ControllerBase
{
    /// <summary>
    /// Login with email and password
    /// </summary>
    [HttpPost("login")]
    public async Task<IActionResult> Login([FromBody] LoginRequest request)
    {
        var ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString();
        var userAgent = HttpContext.Request.Headers["User-Agent"].ToString();

        var command = new LoginCommand(
            request.TenantSlug,
            request.Email,
            request.Password,
            ipAddress,
            userAgent
        );

        var result = await mediator.Send(command);
        return Ok(result);
    }

    /// <summary>
    /// Get current user (requires authentication)
    /// </summary>
    [HttpGet("me")]
    [Authorize]
    public IActionResult GetCurrentUser()
    {
        // Extract user information from JWT Claims
        var userId = User.FindFirst("user_id")?.Value;
        var tenantId = User.FindFirst("tenant_id")?.Value;
        var email = User.FindFirst(ClaimTypes.Email)?.Value;
        var fullName = User.FindFirst("full_name")?.Value;
        var tenantSlug = User.FindFirst("tenant_slug")?.Value;
        var tenantRole = User.FindFirst("tenant_role")?.Value; // NEW: Role claim
        var role = User.FindFirst(ClaimTypes.Role)?.Value;

        return Ok(new
        {
            userId,
            tenantId,
            email,
            fullName,
            tenantSlug,
            tenantRole, // NEW: Role information
            role, // NEW: Standard role claim
            claims = User.Claims.Select(c => new { c.Type, c.Value })
        });
    }

    /// <summary>
    /// Refresh access token using refresh token
    /// </summary>
    [HttpPost("refresh")]
    [AllowAnonymous]
    public async Task<IActionResult> RefreshToken([FromBody] RefreshTokenRequest request)
    {
        try
        {
            var ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString();
            var userAgent = HttpContext.Request.Headers["User-Agent"].ToString();

            var (accessToken, newRefreshToken) = await refreshTokenService.RefreshTokenAsync(
                request.RefreshToken,
                ipAddress,
                userAgent,
                HttpContext.RequestAborted);

            return Ok(new
            {
                accessToken,
                refreshToken = newRefreshToken,
                expiresIn = 900, // 15 minutes in seconds
                tokenType = "Bearer"
            });
        }
        catch (UnauthorizedAccessException ex)
        {
            logger.LogWarning(ex, "Refresh token failed");
            return Unauthorized(new { message = "Invalid or expired refresh token" });
        }
    }

    /// <summary>
    /// Logout (revoke refresh token)
    /// </summary>
    [HttpPost("logout")]
    [AllowAnonymous]
    public async Task<IActionResult> Logout([FromBody] LogoutRequest request)
    {
        try
        {
            var ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString();

            await refreshTokenService.RevokeTokenAsync(
                request.RefreshToken,
                ipAddress,
                HttpContext.RequestAborted);

            return Ok(new { message = "Logged out successfully" });
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "Logout failed");
            return BadRequest(new { message = "Logout failed" });
        }
    }

    /// <summary>
    /// Logout from all devices (revoke all user refresh tokens)
    /// </summary>
    [HttpPost("logout-all")]
    [Authorize]
    public async Task<IActionResult> LogoutAllDevices()
    {
        try
        {
            var userId = Guid.Parse(User.FindFirstValue("user_id")!);

            await refreshTokenService.RevokeAllUserTokensAsync(
                userId,
                HttpContext.RequestAborted);

            return Ok(new { message = "Logged out from all devices successfully" });
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "Logout from all devices failed");
            return BadRequest(new { message = "Logout failed" });
        }
    }

    /// <summary>
    /// Verify email address using token
    /// </summary>
    [HttpPost("verify-email")]
    [AllowAnonymous]
    public async Task<IActionResult> VerifyEmail([FromBody] VerifyEmailRequest request)
    {
        var command = new VerifyEmailCommand(request.Token);
        var success = await mediator.Send(command);

        if (!success)
            return BadRequest(new { message = "Invalid or expired verification token" });

        return Ok(new { message = "Email verified successfully" });
    }

    /// <summary>
    /// Resend email verification link
    /// Always returns success to prevent email enumeration attacks
    /// </summary>
    [HttpPost("resend-verification")]
    [AllowAnonymous]
    [ProducesResponseType(typeof(ResendVerificationResponse), 200)]
    public async Task<IActionResult> ResendVerification([FromBody] ResendVerificationRequest request)
    {
        var baseUrl = $"{Request.Scheme}://{Request.Host}";

        var command = new ResendVerificationEmailCommand(
            request.Email,
            request.TenantId,
            baseUrl);

        await mediator.Send(command);

        // Always return success to prevent email enumeration
        return Ok(new ResendVerificationResponse(
            Message: "If the email exists, a verification link has been sent.",
            Success: true));
    }

    /// <summary>
    /// Initiate password reset flow (sends email with reset link)
    /// Always returns success to prevent email enumeration attacks
    /// </summary>
    [HttpPost("forgot-password")]
    [AllowAnonymous]
    public async Task<IActionResult> ForgotPassword([FromBody] ForgotPasswordRequest request)
    {
        var ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown";
        var userAgent = HttpContext.Request.Headers["User-Agent"].ToString();
        var baseUrl = $"{Request.Scheme}://{Request.Host}";

        var command = new ForgotPasswordCommand(
            request.Email,
            request.TenantSlug,
            ipAddress,
            userAgent,
            baseUrl);

        await mediator.Send(command);

        // Always return success to prevent email enumeration
        return Ok(new { message = "If the email exists, a password reset link has been sent" });
    }

    /// <summary>
    /// Reset password using valid reset token
    /// </summary>
    [HttpPost("reset-password")]
    [AllowAnonymous]
    public async Task<IActionResult> ResetPassword([FromBody] ResetPasswordRequest request)
    {
        var command = new ResetPasswordCommand(request.Token, request.NewPassword);
        var success = await mediator.Send(command);

        if (!success)
            return BadRequest(new { message = "Invalid or expired reset token" });

        return Ok(new { message = "Password reset successfully. Please login with your new password." });
    }

    /// <summary>
    /// Accept an invitation and create/join tenant
    /// </summary>
    [HttpPost("invitations/accept")]
    [AllowAnonymous]
    public async Task<IActionResult> AcceptInvitation([FromBody] AcceptInvitationRequest request)
    {
        try
        {
            var command = new AcceptInvitationCommand(
                request.Token,
                request.FullName,
                request.Password);

            var userId = await mediator.Send(command);

            return Ok(new
            {
                userId,
                message = "Invitation accepted successfully. You can now log in."
            });
        }
        catch (InvalidOperationException ex)
        {
            return BadRequest(new { message = ex.Message });
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "Failed to accept invitation");
            return StatusCode(500, new { message = "Failed to accept invitation. Please try again." });
        }
    }
}

public record LoginRequest(
    string TenantSlug,
    string Email,
    string Password
);

public record VerifyEmailRequest(string Token);

public record ResendVerificationRequest(string Email, Guid TenantId);

public record ResendVerificationResponse(string Message, bool Success);

public record ForgotPasswordRequest(string Email, string TenantSlug);

public record ResetPasswordRequest(string Token, string NewPassword);

public record AcceptInvitationRequest(string Token, string FullName, string Password);
