using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MediatR;
using ChatService.API.Application.Commands.Keys;
using ChatService.API.Application.Queries.Keys;

namespace ChatService.API.Controllers;

/// <summary>
/// EN: Controller for E2EE key management.
/// VI: Controller để quản lý E2EE keys.
/// </summary>
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class KeysController : ControllerBase
{
    private readonly IMediator _mediator;
    private readonly ILogger<KeysController> _logger;

    public KeysController(IMediator mediator, ILogger<KeysController> logger)
    {
        _mediator = mediator;
        _logger = logger;
    }

    /// <summary>
    /// EN: Register or update user's E2EE key bundle.
    /// VI: Đăng ký hoặc cập nhật E2EE key bundle của user.
    /// </summary>
    [HttpPost("register")]
    [ProducesResponseType(typeof(RegisterUserKeysResult), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task<IActionResult> RegisterKeys([FromBody] RegisterUserKeysRequest request)
    {
        var identityUserId = User.FindFirst("sub")?.Value 
            ?? User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;

        if (string.IsNullOrEmpty(identityUserId))
        {
            return Unauthorized(new { success = false, error = new { code = "UNAUTHORIZED", message = "User ID not found in token" } });
        }

        var command = new RegisterUserKeysCommand(
            identityUserId,
            request.DisplayName,
            request.AvatarUrl,
            request.IdentityPublicKey,
            request.SignedPreKey,
            request.SignedPreKeySignature,
            request.OneTimePreKeys?.Select(k => new OneTimePreKeyDto(k.KeyId, k.PublicKey))
        );

        var result = await _mediator.Send(command);
        return Ok(new { success = true, data = result });
    }

    /// <summary>
    /// EN: Rotate the signed pre-key.
    /// VI: Xoay vòng signed pre-key.
    /// </summary>
    [HttpPost("rotate")]
    [ProducesResponseType(typeof(RotatePreKeyResult), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> RotatePreKey([FromBody] RotatePreKeyRequest request)
    {
        var command = new RotatePreKeyCommand(
            request.ChatUserId,
            request.NewSignedPreKey,
            request.NewSignedPreKeySignature
        );

        try
        {
            var result = await _mediator.Send(command);
            return Ok(new { success = true, data = result });
        }
        catch (InvalidOperationException ex)
        {
            return NotFound(new { success = false, error = new { code = "NOT_FOUND", message = ex.Message } });
        }
    }

    /// <summary>
    /// EN: Upload one-time pre-keys.
    /// VI: Upload one-time pre-keys.
    /// </summary>
    [HttpPost("prekeys")]
    [ProducesResponseType(typeof(UploadOneTimeKeysResult), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> UploadPreKeys([FromBody] UploadPreKeysRequest request)
    {
        var command = new UploadOneTimeKeysCommand(
            request.ChatUserId,
            request.OneTimePreKeys.Select(k => new OneTimePreKeyDto(k.KeyId, k.PublicKey))
        );

        try
        {
            var result = await _mediator.Send(command);
            return Ok(new { success = true, data = result });
        }
        catch (InvalidOperationException ex)
        {
            return NotFound(new { success = false, error = new { code = "NOT_FOUND", message = ex.Message } });
        }
    }

    /// <summary>
    /// EN: Get a user's key bundle for initiating E2EE session.
    /// VI: Lấy key bundle của user để khởi tạo E2EE session.
    /// </summary>
    [HttpGet("bundle/{userId:guid}")]
    [ProducesResponseType(typeof(UserKeyBundleDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> GetUserKeyBundle(Guid userId)
    {
        var query = new GetUserKeyBundleQuery(userId);
        var result = await _mediator.Send(query);

        if (result == null)
        {
            return NotFound(new { success = false, error = new { code = "NOT_FOUND", message = $"Key bundle not found for user {userId}" } });
        }

        return Ok(new { success = true, data = result });
    }

    /// <summary>
    /// EN: Get current user's own key bundle status.
    /// VI: Lấy trạng thái key bundle của user hiện tại.
    /// </summary>
    [HttpGet("my-bundle")]
    [ProducesResponseType(typeof(MyKeyBundleDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> GetMyKeyBundle()
    {
        var identityUserId = User.FindFirst("sub")?.Value 
            ?? User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;

        if (string.IsNullOrEmpty(identityUserId))
        {
            return Unauthorized(new { success = false, error = new { code = "UNAUTHORIZED", message = "User ID not found in token" } });
        }

        var query = new GetMyKeyBundleQuery(identityUserId);
        var result = await _mediator.Send(query);

        if (result == null)
        {
            return NotFound(new { success = false, error = new { code = "NOT_FOUND", message = "Key bundle not registered. Please register your keys first." } });
        }

        return Ok(new { success = true, data = result });
    }
}

#region Request DTOs

public record RegisterUserKeysRequest(
    string DisplayName,
    string? AvatarUrl,
    string IdentityPublicKey,
    string SignedPreKey,
    string SignedPreKeySignature,
    List<PreKeyRequest>? OneTimePreKeys = null
);

public record RotatePreKeyRequest(
    Guid ChatUserId,
    string NewSignedPreKey,
    string NewSignedPreKeySignature
);

public record UploadPreKeysRequest(
    Guid ChatUserId,
    List<PreKeyRequest> OneTimePreKeys
);

public record PreKeyRequest(
    int KeyId,
    string PublicKey
);

#endregion
