using foodmarket.Domain.Catalog;
using foodmarket.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;

namespace foodmarket.Api.Seed;

// Seeds global reference data (countries, currencies). Runs in all environments.
public class SystemReferenceSeeder : IHostedService
{
    private readonly IServiceProvider _services;

    public SystemReferenceSeeder(IServiceProvider services)
    {
        _services = services;
    }

    public async Task StartAsync(CancellationToken ct)
    {
        using var scope = _services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();

        await SeedCurrenciesAsync(db, ct);      // первыми — на них ссылаются страны
        await db.SaveChangesAsync(ct);

        await SeedCountriesAsync(db, ct);
        await db.SaveChangesAsync(ct);

        await BackfillCountryDefaultsAsync(db, ct);
        await db.SaveChangesAsync(ct);
    }

    public Task StopAsync(CancellationToken ct) => Task.CompletedTask;

    private record CountrySeed(string Code, string Name, string CurrencyCode, decimal VatRate);

    private static readonly CountrySeed[] CountrySeeds =
    {
        new("KZ", "Казахстан",    "KZT", 16m),
        new("RU", "Россия",       "RUB", 20m),
        new("CN", "Китай",        "CNY", 13m),
        new("TR", "Турция",       "TRY", 18m),
        new("BY", "Беларусь",     "BYN", 20m),
        new("UZ", "Узбекистан",   "UZS", 12m),
        new("KG", "Кыргызстан",   "KGS", 12m),
        new("DE", "Германия",     "EUR", 19m),
        new("US", "США",          "USD", 0m),
        new("KR", "Южная Корея",  "KRW", 10m),
        new("IT", "Италия",       "EUR", 22m),
        new("PL", "Польша",       "PLN", 23m),
    };

    private static readonly Currency[] CurrencySeeds =
    {
        new() { Code = "KZT", Name = "Тенге",            Symbol = "₸", MinorUnit = 2 },
        new() { Code = "RUB", Name = "Российский рубль", Symbol = "₽", MinorUnit = 2 },
        new() { Code = "USD", Name = "Доллар США",       Symbol = "$", MinorUnit = 2 },
        new() { Code = "EUR", Name = "Евро",             Symbol = "€", MinorUnit = 2 },
        new() { Code = "CNY", Name = "Китайский юань",   Symbol = "¥", MinorUnit = 2 },
        new() { Code = "BYN", Name = "Белорусский рубль", Symbol = "Br", MinorUnit = 2 },
        new() { Code = "UZS", Name = "Узбекский сум",    Symbol = "сум", MinorUnit = 2 },
        new() { Code = "KGS", Name = "Кыргызский сом",   Symbol = "сом", MinorUnit = 2 },
        new() { Code = "TRY", Name = "Турецкая лира",    Symbol = "₺", MinorUnit = 2 },
        new() { Code = "KRW", Name = "Южнокорейская вона", Symbol = "₩", MinorUnit = 0 },
        new() { Code = "PLN", Name = "Польский злотый",  Symbol = "zł", MinorUnit = 2 },
    };

    private static async Task SeedCurrenciesAsync(AppDbContext db, CancellationToken ct)
    {
        var existingCodes = await db.Currencies.Select(c => c.Code).ToListAsync(ct);
        foreach (var c in CurrencySeeds)
        {
            if (!existingCodes.Contains(c.Code))
            {
                db.Currencies.Add(c);
            }
        }
    }

    private static async Task SeedCountriesAsync(AppDbContext db, CancellationToken ct)
    {
        var existingCodes = await db.Countries.Select(c => c.Code).ToListAsync(ct);
        foreach (var s in CountrySeeds)
        {
            if (!existingCodes.Contains(s.Code))
            {
                db.Countries.Add(new Country { Code = s.Code, Name = s.Name, VatRate = s.VatRate });
            }
        }
    }

    private static async Task BackfillCountryDefaultsAsync(AppDbContext db, CancellationToken ct)
    {
        // Привязываем валюту и НДС к странам (обновит и новосозданные и существующие).
        var currenciesByCode = await db.Currencies.ToDictionaryAsync(c => c.Code, ct);
        var countries = await db.Countries.ToListAsync(ct);
        foreach (var country in countries)
        {
            var seed = Array.Find(CountrySeeds, s => s.Code == country.Code);
            if (seed is null) continue;
            if (country.DefaultCurrencyId is null && currenciesByCode.TryGetValue(seed.CurrencyCode, out var cur))
            {
                country.DefaultCurrencyId = cur.Id;
            }
            if (country.VatRate == 0m && seed.VatRate > 0m)
            {
                country.VatRate = seed.VatRate;
            }
        }
    }
}
