using System.Reflection;
using System.Text.Json;

namespace Eis.Infrastructure.Candidate;

internal sealed record ResolvedRegion(
    string ProvinceCode,
    string ProvinceName,
    string CityCode,
    string CityName,
    string DistrictCode,
    string DistrictName);

internal sealed class RegionCatalog
{
    private const string Marker = "export const chinaRegions = ";
    private readonly IReadOnlyDictionary<string, Province> _provinces;

    public RegionCatalog()
    {
        var assembly = typeof(RegionCatalog).Assembly;
        var resourceName = assembly.GetManifestResourceNames()
            .Single(name => name.EndsWith("china-regions.js", StringComparison.Ordinal));
        using var stream = assembly.GetManifestResourceStream(resourceName)
            ?? throw new InvalidOperationException("无法读取内嵌行政区划数据");
        using var reader = new StreamReader(stream);
        var source = reader.ReadToEnd();
        var markerIndex = source.IndexOf(Marker, StringComparison.Ordinal);
        if (markerIndex < 0)
        {
            throw new InvalidOperationException("行政区划数据格式无效");
        }

        var json = source[(markerIndex + Marker.Length)..].Trim();
        if (json.EndsWith(';'))
        {
            json = json[..^1];
        }

        var provinces = JsonSerializer.Deserialize<Province[]>(json, new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        }) ?? throw new InvalidOperationException("行政区划数据为空");
        _provinces = provinces.ToDictionary(item => item.Code, StringComparer.Ordinal);
    }

    public ResolvedRegion? Resolve(string? provinceCode, string? cityCode, string? districtCode)
    {
        var provinceKey = (provinceCode ?? string.Empty).Trim();
        var cityKey = (cityCode ?? string.Empty).Trim();
        var districtKey = (districtCode ?? string.Empty).Trim();
        if (!_provinces.TryGetValue(provinceKey, out var province))
        {
            return null;
        }

        var city = province.Cities.FirstOrDefault(item => item.Code == cityKey);
        var district = city?.Districts.FirstOrDefault(item => item.Code == districtKey);
        return city is null || district is null
            ? null
            : new ResolvedRegion(province.Code, province.Name, city.Code, city.Name, district.Code, district.Name);
    }

    private sealed class Province
    {
        public required string Code { get; init; }

        public required string Name { get; init; }

        public required City[] Cities { get; init; }
    }

    private sealed class City
    {
        public required string Code { get; init; }

        public required string Name { get; init; }

        public required District[] Districts { get; init; }
    }

    private sealed class District
    {
        public required string Code { get; init; }

        public required string Name { get; init; }
    }
}
