using MediatR;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StorageService.API.Application.Queries;
using StorageService.Domain.AggregatesModel.FileAggregate;
using StorageService.Domain.AggregatesModel.QuotaAggregate;
using StorageService.Infrastructure.Caching;
using StorageService.Infrastructure.Configuration;
using StorageService.Infrastructure.Storage;

namespace StorageService.API.Application.Commands;

/// <summary>
/// EN: Handler for ConfirmUploadCommand - confirms direct upload and saves metadata.
/// VI: Handler cho ConfirmUploadCommand - xác nhận upload trực tiếp và lưu metadata.
/// </summary>
/// <remarks>
/// EN: Called after client successfully uploads file using pre-signed URL.
///     Verifies file exists in storage, saves metadata to database, and updates quota.
/// VI: Được gọi sau khi client upload file thành công bằng pre-signed URL.
///     Xác minh file tồn tại trong storage, lưu metadata vào database, và cập nhật quota.
/// </remarks>
public class ConfirmUploadCommandHandler : IRequestHandler<ConfirmUploadCommand, ConfirmUploadResult>
{
    private readonly IFileRepository _fileRepository;
    private readonly IQuotaRepository _quotaRepository;
    private readonly IStorageProviderFactory _storageProviderFactory;
    private readonly StorageSettings _settings;
    private readonly IRedisCacheService _cache;
    private readonly ILogger<ConfirmUploadCommandHandler> _logger;

    public ConfirmUploadCommandHandler(
        IFileRepository fileRepository,
        IQuotaRepository quotaRepository,
        IStorageProviderFactory storageProviderFactory,
        IOptions<StorageSettings> settings,
        IRedisCacheService cache,
        ILogger<ConfirmUploadCommandHandler> logger)
    {
        _fileRepository = fileRepository;
        _quotaRepository = quotaRepository;
        _storageProviderFactory = storageProviderFactory;
        _settings = settings.Value;
        _cache = cache;
        _logger = logger;
    }

    public async Task<ConfirmUploadResult> Handle(ConfirmUploadCommand request, CancellationToken cancellationToken)
    {
        try
        {
            _logger.LogInformation(
                "Confirm upload for user {UserId}, objectKey: {ObjectKey}",
                request.UserId, request.ObjectKey);

            // EN: Validate object key belongs to user
            // VI: Kiểm tra object key thuộc về user
            if (!ValidateObjectKeyOwnership(request.ObjectKey, request.UserId))
            {
                _logger.LogWarning(
                    "Object key {ObjectKey} does not belong to user {UserId}",
                    request.ObjectKey, request.UserId);
                return ConfirmUploadResult.Fail("Invalid object key.");
            }

            var bucketName = _settings.DefaultBucket;
            var provider = _storageProviderFactory.GetProvider();

            // EN: Verify file exists in storage / VI: Xác minh file tồn tại trong storage
            var exists = await provider.ExistsAsync(bucketName, request.ObjectKey, cancellationToken);
            if (!exists)
            {
                _logger.LogWarning(
                    "File not found in storage: {Bucket}/{ObjectKey}",
                    bucketName, request.ObjectKey);
                return ConfirmUploadResult.Fail("File not found in storage. Upload may have failed.");
            }

            // EN: Check if already confirmed (idempotency)
            // VI: Kiểm tra đã xác nhận chưa (idempotency)
            var existingFile = await _fileRepository.GetByObjectKeyAsync(request.ObjectKey, cancellationToken);
            if (existingFile != null)
            {
                _logger.LogInformation(
                    "Upload already confirmed for objectKey: {ObjectKey}, fileId: {FileId}",
                    request.ObjectKey, existingFile.Id);
                return ConfirmUploadResult.Ok(existingFile.Id, existingFile.ToDto());
            }

            // EN: Get or create quota / VI: Lấy hoặc tạo quota
            var quota = await _quotaRepository.GetOrCreateAsync(request.UserId, cancellationToken);
            
            // EN: Double-check quota (in case of concurrent uploads)
            // VI: Kiểm tra lại quota (phòng trường hợp upload đồng thời)
            if (!quota.CanUpload(request.FileSizeBytes))
            {
                _logger.LogWarning(
                    "Quota exceeded for user {UserId} during confirmation",
                    request.UserId);
                    
                // EN: Delete the uploaded file since quota exceeded
                // VI: Xóa file đã upload vì vượt quota
                await provider.DeleteAsync(bucketName, request.ObjectKey, cancellationToken);
                
                return ConfirmUploadResult.Fail(
                    "Storage quota exceeded. The uploaded file has been removed.");
            }

            // EN: Create file metadata / VI: Tạo metadata file
            var storageFile = new StorageFile(
                request.FileName,
                bucketName,
                request.ObjectKey,
                request.ContentType,
                request.FileSizeBytes,
                request.UserId,
                provider.ProviderType,
                request.AccessLevel,
                request.TenantId,
                checksum: null);

            await _fileRepository.AddAsync(storageFile, cancellationToken);

            // EN: Update quota / VI: Cập nhật quota
            quota.AddUsage(request.FileSizeBytes);
            _quotaRepository.Update(quota);

            // EN: Save changes / VI: Lưu thay đổi
            await _fileRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);

            // EN: Invalidate quota cache so next query gets updated value
            // VI: Invalidate quota cache để query tiếp theo lấy giá trị mới
            if (Guid.TryParse(request.UserId, out var userId))
            {
                await _cache.DeleteAsync(CacheKeys.UserQuota(userId), cancellationToken);
                
                // EN: Invalidate user files list cache (for search results refresh)
                // VI: Invalidate cache danh sách file của user (để refresh kết quả search)
                await _cache.DeleteByPatternAsync(CacheKeys.UserFilesPattern(userId), cancellationToken);
            }

            _logger.LogInformation(
                "Upload confirmed successfully: fileId={FileId}, objectKey={ObjectKey}, user={UserId}",
                storageFile.Id, request.ObjectKey, request.UserId);

            return ConfirmUploadResult.Ok(storageFile.Id, storageFile.ToDto());
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, 
                "Error confirming upload for user {UserId}, objectKey: {ObjectKey}",
                request.UserId, request.ObjectKey);
            return ConfirmUploadResult.Fail("An error occurred while confirming upload.");
        }
    }

    /// <summary>
    /// EN: Validate that object key belongs to the specified user.
    /// VI: Xác minh object key thuộc về user được chỉ định.
    /// </summary>
    /// <remarks>
    /// EN: Object key format: {accessLevel}/{userId}/{date}/{fileId}_{filename}
    /// VI: Định dạng object key: {accessLevel}/{userId}/{date}/{fileId}_{filename}
    /// </remarks>
    private static bool ValidateObjectKeyOwnership(string objectKey, string userId)
    {
        if (string.IsNullOrEmpty(objectKey))
            return false;

        var parts = objectKey.Split('/');
        if (parts.Length < 3)
            return false;

        // EN: Format: {prefix}/{userId}/{date}/...
        // VI: Định dạng: {prefix}/{userId}/{date}/...
        var ownerUserId = parts[1];
        return ownerUserId == userId;
    }
}
