using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using StorageService.API.Application.Commands.MultipartUpload;
using Swashbuckle.AspNetCore.Annotations;
using System.Security.Claims;
using Asp.Versioning;

namespace StorageService.API.Controllers;

/// <summary>
/// EN: Controller for multipart file upload operations.
/// VI: Controller cho các thao tác multipart upload file.
/// </summary>
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/files/multipart")]
[SwaggerTag("Multipart Upload - Upload large files in chunks")]
public class MultipartUploadController : ControllerBase
{
    private readonly IMediator _mediator;
    private readonly ILogger<MultipartUploadController> _logger;

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

    /// <summary>
    /// EN: Initiate a multipart upload session.
    /// VI: Khởi tạo phiên multipart upload.
    /// </summary>
    /// <remarks>
    /// EN: Start uploading a large file in chunks. Returns an upload ID to use for uploading parts.
    /// VI: Bắt đầu upload file lớn theo từng phần. Trả về upload ID để sử dụng cho việc upload các phần.
    /// </remarks>
    [HttpPost("initiate")]
    [Authorize]
    [SwaggerOperation(
        Summary = "Initiate multipart upload",
        Description = "Start a new multipart upload session for large files")]
    [SwaggerResponse(200, "Upload session initiated successfully", typeof(ApiResponse<InitiateMultipartUploadResult>))]
    [SwaggerResponse(400, "Invalid request")]
    [SwaggerResponse(401, "Unauthorized")]
    public async Task<ActionResult<ApiResponse<InitiateMultipartUploadResult>>> Initiate(
        [FromBody] InitiateMultipartUploadRequest request,
        CancellationToken cancellationToken = default)
    {
        var userId = GetUserId();
        if (string.IsNullOrEmpty(userId))
            return Unauthorized(new ApiResponse<InitiateMultipartUploadResult> 
            { 
                Success = false, 
                Error = "User ID not found" 
            });

        var command = new InitiateMultipartUploadCommand(
            request.FileName,
            request.TotalSizeBytes,
            request.ContentType ?? "application/octet-stream",
            userId,
            request.ChunkSizeBytes);

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

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

        return Ok(new ApiResponse<InitiateMultipartUploadResult> { Success = true, Data = result });
    }

    /// <summary>
    /// EN: Upload a single part of a multipart upload.
    /// VI: Upload một phần của multipart upload.
    /// </summary>
    /// <remarks>
    /// EN: Upload one chunk of the file. Part numbers must be between 1 and N (total chunks).
    /// VI: Upload một phần của file. Số thứ tự phần phải từ 1 đến N (tổng số chunks).
    /// </remarks>
    [HttpPost("upload-part")]
    [Authorize]
    [RequestSizeLimit(20971520)] // 20MB per part
    [SwaggerOperation(
        Summary = "Upload a part",
        Description = "Upload a single chunk of the file")]
    [SwaggerResponse(200, "Part uploaded successfully", typeof(ApiResponse<UploadPartResult>))]
    [SwaggerResponse(400, "Invalid request")]
    [SwaggerResponse(401, "Unauthorized")]
    [SwaggerResponse(404, "Upload session not found")]
    public async Task<ActionResult<ApiResponse<UploadPartResult>>> UploadPart(
        [FromForm] Guid uploadId,
        [FromForm] int partNumber,
        IFormFile file,
        CancellationToken cancellationToken = default)
    {
        var userId = GetUserId();
        if (string.IsNullOrEmpty(userId))
            return Unauthorized(new ApiResponse<UploadPartResult> 
            { 
                Success = false, 
                Error = "User ID not found" 
            });

        if (file == null || file.Length == 0)
            return BadRequest(new ApiResponse<UploadPartResult> 
            { 
                Success = false, 
                Error = "No file data provided" 
            });

        using var stream = file.OpenReadStream();
        var command = new UploadPartCommand(uploadId, partNumber, stream, userId);
        var result = await _mediator.Send(command, cancellationToken);

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

        return Ok(new ApiResponse<UploadPartResult> { Success = true, Data = result });
    }

    /// <summary>
    /// EN: Complete the multipart upload.
    /// VI: Hoàn thành multipart upload.
    /// </summary>
    /// <remarks>
    /// EN: Complete the upload by merging all parts into a single file.
    /// VI: Hoàn thành upload bằng cách merge tất cả các phần thành một file duy nhất.
    /// </remarks>
    [HttpPost("complete")]
    [Authorize]
    [SwaggerOperation(
        Summary = "Complete multipart upload",
        Description = "Finalize the upload and merge all parts")]
    [SwaggerResponse(200, "Upload completed successfully", typeof(ApiResponse<CompleteMultipartUploadResult>))]
    [SwaggerResponse(400, "Invalid request or missing parts")]
    [SwaggerResponse(401, "Unauthorized")]
    [SwaggerResponse(404, "Upload session not found")]
    public async Task<ActionResult<ApiResponse<CompleteMultipartUploadResult>>> Complete(
        [FromBody] CompleteMultipartUploadRequest request,
        CancellationToken cancellationToken = default)
    {
        var userId = GetUserId();
        if (string.IsNullOrEmpty(userId))
            return Unauthorized(new ApiResponse<CompleteMultipartUploadResult> 
            { 
                Success = false, 
                Error = "User ID not found" 
            });

        var command = new CompleteMultipartUploadCommand(request.UploadId, userId);
        var result = await _mediator.Send(command, cancellationToken);

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

        return Ok(new ApiResponse<CompleteMultipartUploadResult> { Success = true, Data = result });
    }

    /// <summary>
    /// EN: Abort the multipart upload.
    /// VI: Hủy multipart upload.
    /// </summary>
    /// <remarks>
    /// EN: Cancel the upload and delete all uploaded parts.
    /// VI: Hủy upload và xóa tất cả các phần đã upload.
    /// </remarks>
    [HttpDelete("abort")]
    [Authorize]
    [SwaggerOperation(
        Summary = "Abort multipart upload",
        Description = "Cancel the upload and cleanup resources")]
    [SwaggerResponse(200, "Upload aborted successfully", typeof(ApiResponse<AbortMultipartUploadResult>))]
    [SwaggerResponse(400, "Invalid request")]
    [SwaggerResponse(401, "Unauthorized")]
    [SwaggerResponse(404, "Upload session not found")]
    public async Task<ActionResult<ApiResponse<AbortMultipartUploadResult>>> Abort(
        [FromBody] AbortMultipartUploadRequest request,
        CancellationToken cancellationToken = default)
    {
        var userId = GetUserId();
        if (string.IsNullOrEmpty(userId))
            return Unauthorized(new ApiResponse<AbortMultipartUploadResult> 
            { 
                Success = false, 
                Error = "User ID not found" 
            });

        var command = new AbortMultipartUploadCommand(request.UploadId, userId);
        var result = await _mediator.Send(command, cancellationToken);

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

        return Ok(new ApiResponse<AbortMultipartUploadResult> { Success = true, Data = result });
    }

    /// <summary>
    /// EN: Get multipart upload progress.
    /// VI: Lấy tiến độ multipart upload.
    /// </summary>
    /// <remarks>
    /// EN: Get the current status and progress of an ongoing multipart upload.
    /// VI: Lấy trạng thái và tiến độ hiện tại của một multipart upload đang diễn ra.
    /// </remarks>
    [HttpGet("{uploadId:guid}")]
    [Authorize]
    [SwaggerOperation(
        Summary = "Get upload progress",
        Description = "Get the current status and progress of a multipart upload")]
    [SwaggerResponse(200, "Progress retrieved successfully", typeof(ApiResponse<MultipartUploadProgressResult>))]
    [SwaggerResponse(401, "Unauthorized")]
    [SwaggerResponse(404, "Upload session not found")]
    public async Task<ActionResult<ApiResponse<MultipartUploadProgressResult>>> GetProgress(
        Guid uploadId,
        CancellationToken cancellationToken = default)
    {
        var userId = GetUserId();
        if (string.IsNullOrEmpty(userId))
            return Unauthorized(new ApiResponse<MultipartUploadProgressResult> 
            { 
                Success = false, 
                Error = "User ID not found" 
            });

        var query = new GetMultipartUploadProgressQuery(uploadId, userId);
        var result = await _mediator.Send(query, cancellationToken);

        if (result == null)
            return NotFound(new ApiResponse<MultipartUploadProgressResult> 
            { 
                Success = false, 
                Error = "Upload session not found" 
            });

        return Ok(new ApiResponse<MultipartUploadProgressResult> { Success = true, Data = result });
    }

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

// ========== Request DTOs ==========

/// <summary>
/// EN: Request to initiate a multipart upload.
/// VI: Request để khởi tạo multipart upload.
/// </summary>
public record InitiateMultipartUploadRequest
{
    /// <summary>EN: File name / VI: Tên file</summary>
    public required string FileName { get; init; }
    
    /// <summary>EN: Total file size in bytes / VI: Tổng kích thước file (bytes)</summary>
    public required long TotalSizeBytes { get; init; }
    
    /// <summary>EN: Content type (MIME) / VI: Content type (MIME)</summary>
    public string? ContentType { get; init; }
    
    /// <summary>EN: Size of each chunk in bytes (default: 5MB) / VI: Kích thước mỗi chunk (bytes, mặc định: 5MB)</summary>
    public int? ChunkSizeBytes { get; init; }
}

/// <summary>
/// EN: Request to complete a multipart upload.
/// VI: Request để hoàn thành multipart upload.
/// </summary>
public record CompleteMultipartUploadRequest
{
    /// <summary>EN: Upload ID / VI: ID của upload</summary>
    public required Guid UploadId { get; init; }
}

/// <summary>
/// EN: Request to abort a multipart upload.
/// VI: Request để hủy multipart upload.
/// </summary>
public record AbortMultipartUploadRequest
{
    /// <summary>EN: Upload ID / VI: ID của upload</summary>
    public required Guid UploadId { get; init; }
}

// ========== Query for Progress ==========

/// <summary>
/// EN: Query to get multipart upload progress.
/// VI: Query để lấy tiến độ multipart upload.
/// </summary>
public record GetMultipartUploadProgressQuery(Guid UploadId, string UserId) : IRequest<MultipartUploadProgressResult?>;

/// <summary>
/// EN: Result of multipart upload progress query.
/// VI: Kết quả query tiến độ multipart upload.
/// </summary>
public record MultipartUploadProgressResult
{
    public Guid UploadId { get; init; }
    public string FileName { get; init; } = default!;
    public long TotalSizeBytes { get; init; }
    public long UploadedBytes { get; init; }
    public int TotalChunks { get; init; }
    public int UploadedChunks { get; init; }
    public double ProgressPercentage { get; init; }
    public string Status { get; init; } = default!;
    public DateTime CreatedAt { get; init; }
    public DateTime? ExpiresAt { get; init; }
}
