using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Tiku.Application.Assets;
using Tiku.Application.Catalog;
using Tiku.Application.Content;
using Tiku.Domain.Content;

namespace Tiku.Infrastructure.Assets;

public sealed partial class AssetManagementService
{
    public async Task<CatalogList<ContentAssetManagementItem>> GetAssetsAsync(
        AssetManagementActor actor,
        AssetManagementFilter filter,
        CancellationToken cancellationToken = default)
    {
        var query = dbContext.ContentAssets
            .AsNoTracking()
            .Where(asset => asset.TenantId == actor.TenantId);

        if (filter.RegionId.HasValue) query = query.Where(asset => asset.RegionId == filter.RegionId.Value);

        if (filter.SubjectId.HasValue) query = query.Where(asset => asset.SubjectId == filter.SubjectId.Value);

        if (filter.CategoryId.HasValue) query = query.Where(asset => asset.CategoryId == filter.CategoryId.Value);

        if (filter.ContentNodeId.HasValue)
            query = query.Where(asset => asset.ContentNodeId == filter.ContentNodeId.Value);

        if (!string.IsNullOrWhiteSpace(filter.AssetType) &&
            Enum.TryParse<ContentAssetType>(filter.AssetType, true, out var assetType))
            query = query.Where(asset => asset.AssetType == assetType);

        if (!string.IsNullOrWhiteSpace(filter.UploadStatus) &&
            Enum.TryParse<AssetUploadStatus>(filter.UploadStatus, true, out var uploadStatus))
            query = query.Where(asset => asset.UploadStatus == uploadStatus);

        if (!string.IsNullOrWhiteSpace(filter.SecurityScanStatus) &&
            Enum.TryParse<AssetSecurityScanStatus>(filter.SecurityScanStatus, true, out var securityScanStatus))
            query = query.Where(asset => asset.SecurityScanStatus == securityScanStatus);

        if (!string.IsNullOrWhiteSpace(filter.Category))
        {
            var category = filter.Category.Trim();
            query = query.Where(asset => asset.Category == category);
        }

        if (!string.IsNullOrWhiteSpace(filter.Keyword))
        {
            var keyword = filter.Keyword.Trim();
            query = query.Where(asset =>
                (asset.Title != null && asset.Title.Contains(keyword)) ||
                (asset.FileName != null && asset.FileName.Contains(keyword)) ||
                (asset.Description != null && asset.Description.Contains(keyword)) ||
                (asset.AssetKey != null && asset.AssetKey.Contains(keyword)));
        }

        var items = await query
            .OrderBy(asset => asset.SortOrder)
            .ThenByDescending(asset => asset.CreatedAt)
            .Take(ResolveLimit(filter.Limit))
            .Select(asset => ToItem(asset))
            .ToArrayAsync(cancellationToken);

        return new CatalogList<ContentAssetManagementItem>(items);
    }

    public async Task<ContentManagementResult<ContentAssetManagementItem>> UpsertAssetAsync(
        AssetManagementActor actor,
        UpsertAssetCommand command,
        CancellationToken cancellationToken = default)
    {
        var asset = await ResolveManagementAssetAsync(actor, command, cancellationToken);
        var accountedBytesBefore = AccountedStorageBytes(asset);
        asset.RegionId = command.RegionId;
        asset.SubjectId = command.SubjectId;
        asset.CategoryId = command.CategoryId;
        asset.ContentNodeId = command.ContentNodeId;
        asset.LegacyId = NormalizeOptional(command.LegacyId);
        asset.AssetKey = NormalizeOptional(command.AssetKey);
        asset.Title = NormalizeOptional(command.Title) ?? NormalizeOptional(command.FileName) ?? asset.Title ?? "未命名资源";
        asset.Category = NormalizeOptional(command.Category);
        asset.Description = NormalizeOptional(command.Description);
        asset.FileName = NormalizeOptional(command.FileName);
        asset.CdnUrl = NormalizeOptional(command.CdnUrl);
        asset.IsPublic = command.IsPublic ?? asset.IsPublic;
        asset.AssetType = ParseEnum(command.AssetType, asset.AssetType);
        asset.Visibility = ResolveVisibility(command.Visibility, asset.IsPublic);
        asset.Status = ParseEnum(command.Status, asset.Status);
        asset.StorageProvider =
            ToAssetStorageProvider(objectStorageService.NormalizeProvider(command.Provider,
                ToObjectStorageProvider(asset.StorageProvider)));
        asset.Bucket = string.IsNullOrWhiteSpace(command.Bucket) ? asset.Bucket : command.Bucket.Trim();
        asset.ObjectKey = string.IsNullOrWhiteSpace(command.ObjectKey)
            ? asset.ObjectKey
            : objectStorageService.ValidateObjectKey(actor.TenantId, command.ObjectKey.Trim());
        asset.MimeType = string.IsNullOrWhiteSpace(command.MimeType)
            ? asset.MimeType
            : objectStorageService.ValidateMimeType(command.MimeType.Trim());
        asset.FileSizeBytes = objectStorageService.ValidateFileSize(command.FileSizeBytes ?? asset.FileSizeBytes);
        asset.ChecksumSha256 = NormalizeChecksum(command.ChecksumSha256) ?? asset.ChecksumSha256;
        asset.PreviewUrl = NormalizeOptional(command.PreviewUrl);
        asset.PreviewObjectKey = NormalizeOptional(command.PreviewObjectKey) ?? asset.PreviewObjectKey;
        asset.SortOrder = command.Order ?? asset.SortOrder;
        asset.AccessRules = command.AccessRules.ValueKind == JsonValueKind.Undefined
            ? asset.AccessRules
            : command.AccessRules;
        asset.Metadata = command.Metadata.ValueKind == JsonValueKind.Undefined ? asset.Metadata : command.Metadata;
        asset.UpdatedBy = actor.UserId;

        var accountedBytesAfter = AccountedStorageBytes(asset);
        await SaveWithStorageQuotaAdjustmentAsync(
            actor.TenantId,
            accountedBytesAfter - accountedBytesBefore,
            cancellationToken);
        return new ContentManagementResult<ContentAssetManagementItem>(ToItem(asset));
    }
}