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

namespace Application.Features.Admin.Crypto.List.Sync;

public sealed class Handler(IAppDbContext db, IUpbitClient upbit, ICacheService cache) : ICommandHandler<Command, Response>
{
    public async Task<Response> Handle(Command request, CancellationToken ct)
    {
        var allMarkets = await upbit.GetMarketsAsync(ct);

        if (allMarkets.Count == 0)
        {
            throw new InvalidOperationException("Upbit API에서 마켓 데이터를 가져오지 못했습니다.");
        }

        var dbCoins = await db.Coin.Include(c => c.CoinMarket).ToListAsync(ct);
        var dbCoinMap = dbCoins.ToDictionary(c => c.Symbol, c => c, StringComparer.OrdinalIgnoreCase);
        var upbitSymbols = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

        int created = 0;
        int updated = 0;
        int delisted = 0;

        // 심볼별 첫 마켓에서 Coin 생성/갱신
        foreach (var market in allMarkets)
        {
            var symbol = market.Market.Split('-')[1];

            if (!upbitSymbols.Add(symbol))
            {
                continue;
            }

            if (dbCoinMap.TryGetValue(symbol, out var existing))
            {
                existing.SyncUpdate(market.KoreanName, market.EnglishName, market.MarketEvent.Warning);
                updated++;
            }
            else
            {
                var coin = Coin.SyncFromUpbit(symbol, market.KoreanName, market.EnglishName, market.MarketEvent.Warning);
                db.Coin.Add(coin);
                dbCoinMap[symbol] = coin;
                created++;
            }
        }

        // SaveChanges로 신규 코인 ID 확정
        await db.SaveChangesAsync(ct);

        // 거래쌍 동기화 (CoinMarket)
        foreach (var market in allMarkets)
        {
            var symbol = market.Market.Split('-')[1];

            if (!dbCoinMap.TryGetValue(symbol, out var coin))
            {
                continue;
            }

            if (!coin.CoinMarket.Any(m => m.Market.Equals(market.Market, StringComparison.OrdinalIgnoreCase)))
            {
                db.CoinMarket.Add(new CoinMarket { CoinID = coin.ID, Market = market.Market.ToUpper() });
            }
        }

        // 상폐 처리
        foreach (var coin in dbCoins)
        {
            if (!upbitSymbols.Contains(coin.Symbol) && !coin.IsDelisted)
            {
                coin.MarkDelisted();
                delisted++;
            }
        }

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

        return new Response(created, updated, delisted);
    }
}