using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using StorageService.API.Application.Commands;
using StorageService.API.Application.Queries;
using StorageService.Domain.AggregatesModel.FileAggregate;
using Swashbuckle.AspNetCore.Annotations;
using System.Security.Claims;
using Asp.Versioning;

namespace StorageService.API.Controllers;

/// <summary>
/// EN: Controller for direct upload using pre-signed URLs.
/// VI: Controller cho upload trực tiếp sử dụng pre-signed URLs.
/// </summary>
/// <remarks>
/// EN: This controller implements the Direct Client Upload pattern where files are uploaded
///     directly to MinIO, bypassing the backend to handle millions of concurrent uploads.
///     
///     Flow:
///     1. Client calls POST /sign-upload to get pre-signed PUT URL
///     2. Client uploads file directly to MinIO using the pre-signed URL
///     3. Client calls POST /confirm-upload to save metadata
/// 
/// VI: Controller này triển khai pattern Direct Client Upload, nơi files được upload
///     trực tiếp lên MinIO, bỏ qua backend để xử lý hàng triệu uploads đồng thời.
///     
///     Luồng:
///     1. Client gọi POST /sign-upload để lấy pre-signed PUT URL
///     2. Client upload file trực tiếp lên MinIO bằng pre-signed URL
///     3. Client gọi POST /confirm-upload để lưu metadata
/// </remarks>
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/storage")]
[SwaggerTag("Direct Upload - Pre-signed URL based upload for high scalability")]
public class SignedUrlController : ControllerBase
{
    private readonly IMediator _mediator;
    private readonly ILogger<SignedUrlController> _logger;

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

    /// <summary>
    /// EN: Request a pre-signed URL for direct upload.
    /// VI: Yêu cầu pre-signed URL để upload trực tiếp.
    /// </summary>
    /// <remarks>
    /// EN: Returns a pre-signed PUT URL that allows the client to upload directly to MinIO.
    ///     The URL is valid for a limited time (default: 1 hour).
    ///     
    /// VI: Trả về pre-signed PUT URL cho phép client upload trực tiếp lên MinIO.
    ///     URL có hiệu lực trong thời gian giới hạn (mặc định: 1 giờ).
    /// </remarks>
    [HttpPost("sign-upload")]
    [Authorize]
    [SwaggerOperation(
        Summary = "Get pre-signed upload URL",
        Description = "Request a pre-signed PUT URL for direct file upload to storage")]
    [SwaggerResponse(200, "Pre-signed URL generated successfully")]
    [SwaggerResponse(400, "Invalid request or quota exceeded")]
    [SwaggerResponse(401, "Unauthorized")]
    public async Task<ActionResult<ApiResponse<SignUploadResponse>>> SignUpload(
        [FromBody] SignUploadRequest request,
        CancellationToken cancellationToken = default)
    {
        var userId = GetUserId();
        if (string.IsNullOrEmpty(userId))
            return Unauthorized(new ApiResponse<SignUploadResponse> 
            { 
                Success = false, 
                Error = "User ID not found" 
            });

        // EN: Validate request / VI: Validate request
        if (string.IsNullOrWhiteSpace(request.FileName))
            return BadRequest(new ApiResponse<SignUploadResponse> 
            { 
                Success = false, 
                Error = "File name is required" 
            });

        if (request.FileSizeBytes <= 0)
            return BadRequest(new ApiResponse<SignUploadResponse> 
            { 
                Success = false, 
                Error = "File size must be greater than 0" 
            });

        var accessLevel = ParseAccessLevel(request.AccessLevel);

        var command = new SignUploadCommand(
            userId,
            request.FileName,
            request.ContentType ?? "application/octet-stream",
            request.FileSizeBytes,
            accessLevel);

        var result = await _mediator.Send(command, cancellationToken);

        if (!result.Success)
            return BadRequest(new ApiResponse<SignUploadResponse> 
            { 
                Success = false, 
                Error = result.Error 
            });

        return Ok(new ApiResponse<SignUploadResponse>
        {
            Success = true,
            Data = new SignUploadResponse(
                result.UploadUrl!,
                result.ObjectKey!,
                result.ExpiresAt!.Value)
        });
    }

    /// <summary>
    /// EN: Confirm upload after client uploads to MinIO.
    /// VI: Xác nhận upload sau khi client upload lên MinIO.
    /// </summary>
    /// <remarks>
    /// EN: Call this endpoint after successfully uploading the file using the pre-signed URL.
    ///     This saves the file metadata and updates the user's quota.
    ///     
    /// VI: Gọi endpoint này sau khi upload file thành công bằng pre-signed URL.
    ///     Lưu metadata file và cập nhật quota của user.
    /// </remarks>
    [HttpPost("confirm-upload")]
    [Authorize]
    [SwaggerOperation(
        Summary = "Confirm file upload",
        Description = "Confirm upload completion and save file metadata")]
    [SwaggerResponse(200, "Upload confirmed successfully")]
    [SwaggerResponse(400, "File not found in storage or validation failed")]
    [SwaggerResponse(401, "Unauthorized")]
    public async Task<ActionResult<ApiResponse<ConfirmUploadResponse>>> ConfirmUpload(
        [FromBody] ConfirmUploadRequest request,
        CancellationToken cancellationToken = default)
    {
        var userId = GetUserId();
        if (string.IsNullOrEmpty(userId))
            return Unauthorized(new ApiResponse<ConfirmUploadResponse> 
            { 
                Success = false, 
                Error = "User ID not found" 
            });

        // EN: Validate request / VI: Validate request
        if (string.IsNullOrWhiteSpace(request.ObjectKey))
            return BadRequest(new ApiResponse<ConfirmUploadResponse> 
            { 
                Success = false, 
                Error = "Object key is required" 
            });

        if (string.IsNullOrWhiteSpace(request.FileName))
            return BadRequest(new ApiResponse<ConfirmUploadResponse> 
            { 
                Success = false, 
                Error = "File name is required" 
            });

        var accessLevel = ParseAccessLevel(request.AccessLevel);

        var command = new ConfirmUploadCommand(
            userId,
            request.ObjectKey,
            request.FileName,
            request.ContentType ?? "application/octet-stream",
            request.FileSizeBytes,
            accessLevel);

        var result = await _mediator.Send(command, cancellationToken);

        if (!result.Success)
            return BadRequest(new ApiResponse<ConfirmUploadResponse> 
            { 
                Success = false, 
                Error = result.Error 
            });

        return Ok(new ApiResponse<ConfirmUploadResponse>
        {
            Success = true,
            Data = new ConfirmUploadResponse(
                result.FileId!.Value,
                result.Metadata!)
        });
    }

    private string? GetUserId() => User.FindFirstValue(ClaimTypes.NameIdentifier);

    private static FileAccessLevel ParseAccessLevel(string? accessLevel) =>
        accessLevel?.ToLowerInvariant() switch
        {
            "public" => FileAccessLevel.Public,
            "shared" => FileAccessLevel.Shared,
            _ => FileAccessLevel.Private
        };
}

#region Request/Response DTOs

/// <summary>
/// EN: Request to sign upload URL.
/// VI: Request để ký URL upload.
/// </summary>
/// <param name="FileName">EN: Original file name / VI: Tên file gốc</param>
/// <param name="FileSizeBytes">EN: File size in bytes / VI: Kích thước file (bytes)</param>
/// <param name="ContentType">EN: MIME content type / VI: Content type MIME</param>
/// <param name="AccessLevel">EN: Access level: public, private, shared / VI: Mức truy cập: public, private, shared</param>
public record SignUploadRequest(
    string FileName,
    long FileSizeBytes,
    string? ContentType = null,
    string? AccessLevel = "private"
);

/// <summary>
/// EN: Response containing pre-signed upload URL.
/// VI: Response chứa pre-signed URL upload.
/// </summary>
/// <param name="UploadUrl">EN: Pre-signed PUT URL / VI: Pre-signed PUT URL</param>
/// <param name="ObjectKey">EN: Object key to use for confirm-upload / VI: Object key để sử dụng cho confirm-upload</param>
/// <param name="ExpiresAt">EN: URL expiration time / VI: Thời gian hết hạn URL</param>
public record SignUploadResponse(
    string UploadUrl,
    string ObjectKey,
    DateTime ExpiresAt
);

/// <summary>
/// EN: Request to confirm upload.
/// VI: Request để xác nhận upload.
/// </summary>
/// <param name="ObjectKey">EN: Object key from sign-upload response / VI: Object key từ response sign-upload</param>
/// <param name="FileName">EN: Original file name / VI: Tên file gốc</param>
/// <param name="FileSizeBytes">EN: File size in bytes / VI: Kích thước file (bytes)</param>
/// <param name="ContentType">EN: MIME content type / VI: Content type MIME</param>
/// <param name="AccessLevel">EN: Access level / VI: Mức truy cập</param>
public record ConfirmUploadRequest(
    string ObjectKey,
    string FileName,
    long FileSizeBytes,
    string? ContentType = null,
    string? AccessLevel = "private"
);

/// <summary>
/// EN: Response after confirming upload.
/// VI: Response sau khi xác nhận upload.
/// </summary>
/// <param name="FileId">EN: File ID in database / VI: ID file trong database</param>
/// <param name="Metadata">EN: File metadata / VI: Metadata file</param>
public record ConfirmUploadResponse(
    Guid FileId,
    FileDto Metadata
);

#endregion
