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

namespace StorageService.API.Controllers;

/// <summary>
/// EN: Controller for file operations.
/// VI: Controller cho các thao tác file.
/// </summary>
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/files")]
[SwaggerTag("File Management - Upload, download, and manage files")]
public class FilesController : ControllerBase
{
    private readonly IMediator _mediator;
    private readonly ILogger<FilesController> _logger;

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

    /// <summary>
    /// EN: Upload a file.
    /// VI: Upload file.
    /// </summary>
    [HttpPost("upload")]
    [Authorize]
    [RequestSizeLimit(104857600)] // 100MB
    [SwaggerOperation(Summary = "Upload a file", Description = "Upload a file to storage")]
    [SwaggerResponse(200, "File uploaded successfully")]
    [SwaggerResponse(400, "Invalid request")]
    [SwaggerResponse(401, "Unauthorized")]
    public async Task<ActionResult<ApiResponse<UploadFileResult>>> Upload(
        IFormFile file,
        [FromQuery] string? accessLevel = "private",
        CancellationToken cancellationToken = default)
    {
        var userId = GetUserId();
        if (string.IsNullOrEmpty(userId))
            return Unauthorized(new ApiResponse<UploadFileResult> { Success = false, Error = "User ID not found" });

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

        using var stream = file.OpenReadStream();
        var fileAccessLevel = accessLevel?.ToLowerInvariant() switch
        {
            "public" => Domain.AggregatesModel.FileAggregate.FileAccessLevel.Public,
            "shared" => Domain.AggregatesModel.FileAggregate.FileAccessLevel.Shared,
            _ => Domain.AggregatesModel.FileAggregate.FileAccessLevel.Private
        };

        var command = new UploadFileCommand(
            stream,
            file.FileName,
            file.ContentType,
            file.Length,
            userId,
            null,
            fileAccessLevel);

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

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

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

    /// <summary>
    /// EN: Get user files.
    /// VI: Lấy danh sách files của user.
    /// </summary>
    [HttpGet]
    [Authorize]
    [SwaggerOperation(Summary = "Get user files", Description = "Get list of files uploaded by the current user")]
    [SwaggerResponse(200, "Files retrieved successfully")]
    public async Task<ActionResult<ApiResponse<UserFilesResult>>> GetFiles(
        [FromQuery] int skip = 0,
        [FromQuery] int take = 20,
        [FromQuery] string? search = null,
        CancellationToken cancellationToken = default)
    {
        var userId = GetUserId();
        if (string.IsNullOrEmpty(userId))
            return Unauthorized(new ApiResponse<UserFilesResult> { Success = false, Error = "User ID not found" });

        var query = new GetUserFilesQuery(userId, skip, take, search);
        var result = await _mediator.Send(query, cancellationToken);

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

    /// <summary>
    /// EN: Get file by ID.
    /// VI: Lấy thông tin file theo ID.
    /// </summary>
    [HttpGet("{fileId:guid}")]
    [Authorize]
    [SwaggerOperation(Summary = "Get file by ID", Description = "Get file metadata by ID")]
    [SwaggerResponse(200, "File retrieved successfully")]
    [SwaggerResponse(404, "File not found")]
    public async Task<ActionResult<ApiResponse<FileDto>>> GetFile(
        Guid fileId,
        CancellationToken cancellationToken = default)
    {
        var userId = GetUserId();
        if (string.IsNullOrEmpty(userId))
            return Unauthorized(new ApiResponse<FileDto> { Success = false, Error = "User ID not found" });

        var query = new GetFileQuery(fileId, userId);
        var result = await _mediator.Send(query, cancellationToken);

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

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

    /// <summary>
    /// EN: Get pre-signed download URL.
    /// VI: Lấy URL download có chữ ký.
    /// </summary>
    [HttpGet("{fileId:guid}/download-url")]
    [Authorize]
    [SwaggerOperation(Summary = "Get download URL", Description = "Get pre-signed URL for file download")]
    [SwaggerResponse(200, "URL generated successfully")]
    [SwaggerResponse(404, "File not found")]
    public async Task<ActionResult<ApiResponse<DownloadUrlResult>>> GetDownloadUrl(
        Guid fileId,
        [FromQuery] int expirationSeconds = 3600,
        CancellationToken cancellationToken = default)
    {
        var userId = GetUserId();
        if (string.IsNullOrEmpty(userId))
            return Unauthorized(new ApiResponse<DownloadUrlResult> { Success = false, Error = "User ID not found" });

        var query = new GetDownloadUrlQuery(fileId, userId, expirationSeconds);
        var result = await _mediator.Send(query, cancellationToken);

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

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

    /// <summary>
    /// EN: Delete a file.
    /// VI: Xóa file.
    /// </summary>
    [HttpDelete("{fileId:guid}")]
    [Authorize]
    [SwaggerOperation(Summary = "Delete file", Description = "Delete a file by ID")]
    [SwaggerResponse(200, "File deleted successfully")]
    [SwaggerResponse(404, "File not found")]
    public async Task<ActionResult<ApiResponse<DeleteFileResult>>> Delete(
        Guid fileId,
        CancellationToken cancellationToken = default)
    {
        var userId = GetUserId();
        if (string.IsNullOrEmpty(userId))
            return Unauthorized(new ApiResponse<DeleteFileResult> { Success = false, Error = "User ID not found" });

        var command = new DeleteFileCommand(fileId, userId);
        var result = await _mediator.Send(command, cancellationToken);

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

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

    /// <summary>
    /// EN: Get CDN URL for public files.
    /// VI: Lấy URL CDN cho public files.
    /// </summary>
    /// <remarks>
    /// EN: Returns CDN URL if file is public and CDN is enabled, otherwise returns pre-signed URL.
    /// VI: Trả về CDN URL nếu file là public và CDN đang bật, ngược lại trả về pre-signed URL.
    /// </remarks>
    [HttpGet("{fileId:guid}/cdn-url")]
    [Authorize]
    [SwaggerOperation(Summary = "Get CDN URL", Description = "Get CDN URL for public file or fallback to pre-signed URL")]
    [SwaggerResponse(200, "URL generated successfully")]
    [SwaggerResponse(404, "File not found")]
    public async Task<ActionResult<ApiResponse<CDNUrlResponse>>> GetCDNUrl(
        Guid fileId,
        CancellationToken cancellationToken = default)
    {
        var userId = GetUserId();
        if (string.IsNullOrEmpty(userId))
            return Unauthorized(new ApiResponse<CDNUrlResponse> { Success = false, Error = "User ID not found" });

        // EN: Get file metadata / VI: Lấy metadata file
        var query = new GetFileQuery(fileId, userId);
        var file = await _mediator.Send(query, cancellationToken);

        if (file == null)
            return NotFound(new ApiResponse<CDNUrlResponse> { Success = false, Error = "File not found" });

        // EN: Try to get CDN URL for public files / VI: Thử lấy CDN URL cho public files
        var cdnService = HttpContext.RequestServices.GetService<Infrastructure.CDN.ICDNService>();
        
        if (cdnService?.IsEnabled == true && file.AccessLevel == "Public")
        {
            // EN: Need to get object key - for now use file ID pattern
            // VI: Cần lấy object key - tạm thời dùng pattern từ file ID
            // Note: In production, we should store object_key in FileDto
            var cdnUrl = cdnService.GetCDNUrl($"public/{file.UserId}/{file.Id}", 
                Domain.AggregatesModel.FileAggregate.FileAccessLevel.Public);
                
            if (cdnUrl != null)
            {
                return Ok(new ApiResponse<CDNUrlResponse> 
                { 
                    Success = true, 
                    Data = new CDNUrlResponse(cdnUrl, true, "CDN URL for public file") 
                });
            }
        }

        // EN: Fallback to pre-signed URL / VI: Fallback sang pre-signed URL
        var downloadQuery = new GetDownloadUrlQuery(fileId, userId, 3600);
        var downloadResult = await _mediator.Send(downloadQuery, cancellationToken);

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

        return Ok(new ApiResponse<CDNUrlResponse> 
        { 
            Success = true, 
            Data = new CDNUrlResponse(downloadResult.Url!, false, "Pre-signed URL (CDN not available or file not public)") 
        });
    }

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

/// <summary>
/// EN: Standard API response wrapper.
/// VI: Wrapper response API chuẩn.
/// </summary>
public class ApiResponse<T>
{
    public bool Success { get; set; }
    public T? Data { get; set; }
    public string? Error { get; set; }
}

/// <summary>
/// EN: CDN URL response.
/// VI: Response URL CDN.
/// </summary>
public record CDNUrlResponse(
    string Url,
    bool IsCDN,
    string Description);
