using Application.Abstractions.Messaging;
using Application.Abstractions.Data;
using Application.Abstractions.Cache;
using Domain.Entities.Crypto;
using SharedKernel.Storage;
using Microsoft.EntityFrameworkCore;

namespace Application.Features.Admin.Crypto.List.Update
{
    public sealed class Handler(IAppDbContext db, IFileStorage fileStorage, ICacheService cache) : ICommandHandler<Command>
    {
        public async Task Handle(Command request, CancellationToken ct)
        {
            var coin = await db.Coin.Include(c => c.CoinCategoryMap).FirstOrDefaultAsync(c => c.ID == request.ID, ct);
            if (coin is null)
            {
                throw new KeyNotFoundException("코인을 찾을 수 없습니다.");
            }

            if (await db.Coin.AnyAsync(c => c.Symbol == request.Symbol.ToUpper() && c.ID != request.ID, ct))
            {
                throw new InvalidOperationException($"`{request.Symbol.ToUpper()}`는 이미 등록된 심볼입니다.");
            }

            FileStoragePath uploadPath = new FileStoragePath(UploadTarget.Upload, UploadFolder.Crypto, coin.ID);
            string[] allowedFileExtensions = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg"];
            string? logoImagePath = coin.LogoImage;

            if (request.DeleteLogoImage && !string.IsNullOrEmpty(coin.LogoImage))
            {
                fileStorage.DeleteByUrl(coin.LogoImage);
                logoImagePath = null;
            }

            // 로고 이미지 파일이 제공된 경우 기존 이미지를 삭제하고 새 이미지 저장
            if (request.LogoImageFile is not null)
            {
                if (!string.IsNullOrEmpty(coin.LogoImage))
                {
                    fileStorage.DeleteByUrl(coin.LogoImage);
                }

                logoImagePath = (await fileStorage.SaveFileAsync(request.LogoImageFile, uploadPath, allowedFileExtensions, ct))?.Url;
            }

            coin.Update(
                request.Symbol,
                request.KorName,
                request.EngName,
                request.Description,
                request.ContractAddress,
                request.WebsiteUrl,
                request.WhitepaperUrl,
                request.TwitterUrl,
                request.TelegramUrl,
                request.IsActive,
                request.IsWarning,
                request.IsNew,
                request.IsDelisted
            );

            coin.SetLogoImage(logoImagePath);

            var existingMaps = coin.CoinCategoryMap.ToList();
            db.CoinCategoryMap.RemoveRange(existingMaps);

            foreach (var categoryID in request.CategoryIDs)
            {
                db.CoinCategoryMap.Add(new CoinCategoryMap { CoinID = coin.ID, CategoryID = categoryID });
            }

            await db.SaveChangesAsync(ct);
            await cache.RemoveByPrefixAsync("crypto:", ct);
        }
    }
}