using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using StorageService.Domain.AggregatesModel.FileAggregate;
using StorageService.Domain.AggregatesModel.QuotaAggregate;
using StorageService.Infrastructure.Persistence;
using StorageService.Infrastructure.Storage;

namespace StorageService.API.Application.Commands.MultipartUpload;

/// <summary>
/// EN: Handler for CompleteMultipartUploadCommand.
/// VI: Handler cho CompleteMultipartUploadCommand.
/// </summary>
public class CompleteMultipartUploadCommandHandler 
    : IRequestHandler<CompleteMultipartUploadCommand, CompleteMultipartUploadResult>
{
    private readonly StorageServiceContext _context;
    private readonly IQuotaRepository _quotaRepository;
    private readonly IStorageProviderFactory _storageProviderFactory;
    private readonly ILogger<CompleteMultipartUploadCommandHandler> _logger;

    public CompleteMultipartUploadCommandHandler(
        StorageServiceContext context,
        IQuotaRepository quotaRepository,
        IStorageProviderFactory storageProviderFactory,
        ILogger<CompleteMultipartUploadCommandHandler> logger)
    {
        _context = context;
        _quotaRepository = quotaRepository;
        _storageProviderFactory = storageProviderFactory;
        _logger = logger;
    }

    public async Task<CompleteMultipartUploadResult> Handle(
        CompleteMultipartUploadCommand request, 
        CancellationToken cancellationToken)
    {
        try
        {
            _logger.LogInformation("Completing multipart upload {UploadId}", request.UploadId);

            // EN: Get multipart upload with parts / VI: Lấy multipart upload với parts
            var upload = await _context.MultipartUploads
                .Include(u => u.Parts)
                .FirstOrDefaultAsync(u => u.Id == request.UploadId, cancellationToken);

            if (upload == null)
            {
                _logger.LogWarning("Multipart upload not found: {UploadId}", request.UploadId);
                return CompleteMultipartUploadResult.Fail("Multipart upload not found");
            }

            // EN: Verify ownership / VI: Xác minh quyền sở hữu
            if (upload.UserId != request.UserId)
            {
                _logger.LogWarning("User {UserId} attempted to complete upload owned by {OwnerId}", 
                    request.UserId, upload.UserId);
                return CompleteMultipartUploadResult.Fail("Unauthorized");
            }

            // EN: Complete the upload (domain validation) / VI: Hoàn thành upload (domain validation)
            try
            {
                upload.Complete();
            }
            catch (InvalidOperationException ex)
            {
                _logger.LogWarning(ex, "Cannot complete upload {UploadId}: {Message}", request.UploadId, ex.Message);
                return CompleteMultipartUploadResult.Fail(ex.Message);
            }

            // EN: Complete multipart upload at provider / VI: Hoàn thành multipart upload tại provider
            var provider = _storageProviderFactory.GetProvider();
            var parts = upload.Parts
                .OrderBy(p => p.PartNumber)
                .Select(p => new PartETag(p.PartNumber, p.ETag))
                .ToList();

            await provider.CompleteMultipartUploadAsync(
                upload.BucketName,
                upload.ObjectKey,
                upload.ProviderUploadId,
                parts,
                cancellationToken);

            // EN: Create StorageFile entity / VI: Tạo StorageFile entity
            var storageFile = new StorageFile(
                fileName: upload.FileName,
                bucketName: upload.BucketName,
                objectKey: upload.ObjectKey,
                contentType: upload.ContentType,
                fileSizeBytes: upload.TotalSizeBytes,
                userId: upload.UserId,
                provider: provider.ProviderType,
                // EN: Multipart aggregate currently does not persist access-level metadata.
                // VI: Aggregate multipart hiện chưa lưu metadata access-level.
                accessLevel: FileAccessLevel.Private
            );

            await _context.StorageFiles.AddAsync(storageFile, cancellationToken);

            // EN: Update user quota / VI: Cập nhật quota của user
            var quota = await _quotaRepository.GetOrCreateAsync(upload.UserId, cancellationToken);
            quota.AddUsage(upload.TotalSizeBytes, 1);

            // EN: Save all changes / VI: Lưu tất cả thay đổi
            await _context.SaveEntitiesAsync(cancellationToken);

            _logger.LogInformation(
                "Multipart upload completed successfully. FileId: {FileId}, Size: {Size} bytes",
                storageFile.Id, upload.TotalSizeBytes);

            return CompleteMultipartUploadResult.Ok(storageFile.Id, upload.ObjectKey);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error completing multipart upload {UploadId}", request.UploadId);
            return CompleteMultipartUploadResult.Fail("An error occurred while completing multipart upload.");
        }
    }
}
