using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Tornado3_2026Election.Domain;

namespace Tornado3_2026Election.Services;

public sealed class FormatCatalogService
{
    private static readonly IReadOnlyDictionary<string, string> LegacyFormatAliases = BuildLegacyFormatAliases();
    private readonly string _t3CutPath;
    private readonly IReadOnlyList<FormatTemplateDefinition> _formats;

    public FormatCatalogService(string? configuredT3CutPath = null)
    {
        _t3CutPath = ResolveT3CutPath(configuredT3CutPath);
        _formats = BuildFormats(_t3CutPath);
    }

    public IReadOnlyList<FormatTemplateDefinition> GetAll() => _formats;

    public IReadOnlyList<FormatTemplateDefinition> GetByChannel(BroadcastChannel channel)
    {
        return _formats.Where(format => format.RecommendedChannel == channel).ToArray();
    }

    public FormatTemplateDefinition? FindById(string formatId)
    {
        if (string.IsNullOrWhiteSpace(formatId))
        {
            return null;
        }

        if (LegacyFormatAliases.TryGetValue(formatId, out var canonicalFormatId))
        {
            formatId = canonicalFormatId;
        }

        return _formats.FirstOrDefault(format => string.Equals(format.Id, formatId, StringComparison.Ordinal));
    }

    private static IReadOnlyList<FormatTemplateDefinition> BuildFormats(string t3CutPath)
    {
        List<FormatTemplateDefinition> formats = [];

        formats.AddRange(CreateFormats(
            BroadcastChannel.Bottom,
            "Elect2026_Bottom_민방",
            8,
            t3CutPath,
            "1-2위_광역단체장",
            "1-2위_기초단체장",
            "1-3위_광역단체장",
            "1-3위_기초단체장",
            "1위_광역단체장",
            "1위_기초단체장",
            "당선_광역단체장",
            "당선_광역의원",
            "당선_기초단체장",
            "당선_기초의원",
            "사전투표율_시도",
            "사전투표율_시군구",
            "전후보_광역단체장",
            "전후보_교육감",
            "전후보_기초단체장",
            "투표율_시도",
            "투표율_시군구"));

        formats.AddRange(CreateFormats(
            BroadcastChannel.Normal,
            "Elect2026_Normal_민방",
            10,
            t3CutPath,
            "1-2위_ani_광역단체장",
            "1-2위_ani_기초단체장",
            "1-2위_ani_기초단체장_5760",
            "1-2위_광역단체장",
            "1-2위_광역단체장_5760",
            "1-2위_광역단체장_시도별영상",
            "1-2위_교육감",
            "1-2위_기초단체장",
            "1-2위_기초단체장_시도별영상",
            "1-2위_보궐선거",
            "1-3위_ani_광역단체장",
            "1-3위_ani_기초단체장",
            "1-3위_기초단체장_5760",
            "1-3위_보궐선거",
            "2880_광역의원표",
            "2880_기초의원표",
            "810_광역의원표",
            "810_기초의원표",
            "8316_광역의원표",
            "8316_기초의원표",
            "경력_광역단체장_in",
            "경력_기초단체장_in",
            "광역의원표",
            "광역의원표_HD",
            "기초의원표",
            "기초의원표_HD",
            "당선_광역단체장",
            "당선_광역단체장_HD",
            "당선_광역의원",
            "당선_광역의원_HD",
            "당선_교육감",
            "당선_교육감_HD",
            "당선_기초단체장",
            "당선_기초단체장_HD",
            "당선_기초의원",
            "당선_기초의원_HD",
            "모든후보_광역단체장",
            "모든후보_광역단체장_5760",
            "모든후보_교육감",
            "모든후보_교육감_5760",
            "모든후보_기초단체장",
            "모든후보_기초단체장_5760",
            "민방_타이틀",
            "민방_타이틀_1920",
            "민방_타이틀_1920_notext",
            "민방_타이틀_5760_nologo",
            "사전_역대당선자",
            "사전_역대당선자_교육감",
            "사전_역대당선자_기초단체장",
            "사전_역대투표율",
            "사전_역대투표율_5760",
            "역대시도판세_광역단체장",
            "역대시도판세_기초단체장",
            "이시각1위_광역단체장",
            "이시각1위_광역단체장_HD",
            "이시각1위_광역단체장_L",
            "이시각1위_기초단체장",
            "이시각1위_기초단체장_HD",
            "이시각1위_기초단체장_L",
            "접전_광역단체장",
            "접전_기초단체장",
            "초접전_광역단체장",
            "초접전_기초단체장",
            "투표율",
            "투표율_선거구별 사전",
            "판세_광역단체장",
            "판세_기초단체장",
            "판세_기초단체장_5760"));

        formats.AddRange(CreateFormats(
            BroadcastChannel.TopLeft,
            "Elect2026_Top_민방",
            6,
            t3CutPath,
            "광역단체장_2인",
            "광역단체장_2인_텍스트",
            "기초단체장_2인",
            "기초단체장_2인_텍스트",
            "투표율",
            "투표율_선거구별",
            "판세_광역단체장",
            "판세_광역의원",
            "판세_교육감",
            "판세_기초단체장",
            "판세_기초의원"));

        return formats;
    }

    private static IEnumerable<FormatTemplateDefinition> CreateFormats(
        BroadcastChannel channel,
        string relativeFolder,
        double defaultCutDurationSeconds,
        string t3CutPath,
        params string[] baseNames)
    {
        foreach (var baseName in baseNames)
        {
            var isAvailableInBothPhases = IsAvailableInBothPhases(baseName);
            var isPreElectionOnlyFormat = !isAvailableInBothPhases && IsPreElectionOnlyFormat(baseName);
            var formatId = Path.Combine(relativeFolder, baseName);
            var sceneIdOverride = ResolveSceneIdOverride(relativeFolder, baseName);
            var sceneId = sceneIdOverride ?? formatId;
            var sceneResolution = TryReadSceneResolution(sceneId, t3CutPath);
            var recommendedChannel = ResolveRecommendedChannel(channel, baseName, sceneResolution);

            yield return new FormatTemplateDefinition
            {
                Id = formatId,
                Name = baseName,
                Description = $"{relativeFolder} 컷",
                RecommendedChannel = recommendedChannel,
                RequiresImage = false,
                SupportsPreElection = isAvailableInBothPhases || isPreElectionOnlyFormat,
                SupportsCounting = isAvailableInBothPhases || !isPreElectionOnlyFormat,
                RequiresCandidateData = !isPreElectionOnlyFormat &&
                    !IsHistoricalPreElectionWinnerFormat(baseName) &&
                    !ScheduleTemplatePolicy.IsStaticHistoricalTrendFormat(baseName) &&
                    !ScheduleTemplatePolicy.IsTitleFormat(baseName),
                LoopMode = LoopMode.None,
                SceneWidth = sceneResolution?.Width,
                SceneHeight = sceneResolution?.Height,
                Cuts =
                [
                    new FormatCutDefinition
                    {
                        Name = baseName,
                        DurationSeconds = ScheduleTemplatePolicy.NormalizeCutDurationSeconds(
                            defaultCutDurationSeconds,
                            recommendedChannel,
                            baseName),
                        SceneIdOverride = sceneIdOverride
                    }
                ]
            };
        }
    }

    private static string? ResolveSceneIdOverride(string relativeFolder, string baseName)
    {
        if (string.Equals(relativeFolder, "Elect2026_Normal_민방", StringComparison.Ordinal) &&
            string.Equals(baseName, "투표율", StringComparison.Ordinal))
        {
            return Path.Combine(relativeFolder, "투표율_사진");
        }

        return baseName switch
        {
            "사전투표율_시도" or "사전투표율_시군구" => Path.Combine(relativeFolder, "사전투표율"),
            "투표율_시도" or "투표율_시군구" => Path.Combine(relativeFolder, "투표율"),
            _ => null
        };
    }

    private static bool IsPreElectionOnlyFormat(string baseName)
    {
        return baseName.Contains("투표율", StringComparison.Ordinal);
    }

    private static IReadOnlyDictionary<string, string> BuildLegacyFormatAliases()
    {
        return new Dictionary<string, string>(StringComparer.Ordinal)
        {
            [Path.Combine("Elect2026_Bottom_민방", "1-2위_광역단체장_loop")] = Path.Combine("Elect2026_Bottom_민방", "1-2위_광역단체장"),
            [Path.Combine("Elect2026_Bottom_민방", "1-2위_기초단체장_loop")] = Path.Combine("Elect2026_Bottom_민방", "1-2위_기초단체장"),
            [Path.Combine("Elect2026_Bottom_민방", "1-3위_광역단체장_loop")] = Path.Combine("Elect2026_Bottom_민방", "1-3위_광역단체장"),
            [Path.Combine("Elect2026_Bottom_민방", "1-3위_기초단체장_loop")] = Path.Combine("Elect2026_Bottom_민방", "1-3위_기초단체장"),
            [Path.Combine("Elect2026_Bottom_민방", "1위_광역단체장_loop")] = Path.Combine("Elect2026_Bottom_민방", "1위_광역단체장"),
            [Path.Combine("Elect2026_Bottom_민방", "1위_기초단체장_loop")] = Path.Combine("Elect2026_Bottom_민방", "1위_기초단체장"),
            [Path.Combine("Elect2026_Bottom_민방", "당선_광역단체장_loop")] = Path.Combine("Elect2026_Bottom_민방", "당선_광역단체장"),
            [Path.Combine("Elect2026_Bottom_민방", "당선_광역의원_loop")] = Path.Combine("Elect2026_Bottom_민방", "당선_광역의원"),
            [Path.Combine("Elect2026_Bottom_민방", "당선_기초단체장_loop")] = Path.Combine("Elect2026_Bottom_민방", "당선_기초단체장"),
            [Path.Combine("Elect2026_Bottom_민방", "당선_기초의원_loop")] = Path.Combine("Elect2026_Bottom_민방", "당선_기초의원"),
            [Path.Combine("Elect2026_Bottom_민방", "사전투표율")] = Path.Combine("Elect2026_Bottom_민방", "사전투표율_시도"),
            [Path.Combine("Elect2026_Bottom_민방", "사전투표율_loop")] = Path.Combine("Elect2026_Bottom_민방", "사전투표율_시도"),
            [Path.Combine("Elect2026_Bottom_민방", "전후보_광역단체장_loop")] = Path.Combine("Elect2026_Bottom_민방", "전후보_광역단체장"),
            [Path.Combine("Elect2026_Bottom_민방", "전후보_교육감_loop")] = Path.Combine("Elect2026_Bottom_민방", "전후보_교육감"),
            [Path.Combine("Elect2026_Bottom_민방", "전후보_기초단체장_loop")] = Path.Combine("Elect2026_Bottom_민방", "전후보_기초단체장"),
            [Path.Combine("Elect2026_Bottom_민방", "투표율")] = Path.Combine("Elect2026_Bottom_민방", "투표율_시도"),
            [Path.Combine("Elect2026_Bottom_민방", "투표율_loop")] = Path.Combine("Elect2026_Bottom_민방", "투표율_시도"),
            [Path.Combine("Elect2026_Normal_민방", "1-2위_ani_광역단체장_loop")] = Path.Combine("Elect2026_Normal_민방", "1-2위_ani_광역단체장"),
            [Path.Combine("Elect2026_Normal_민방", "1-2위_ani_기초단체장_L")] = Path.Combine("Elect2026_Normal_민방", "1-2위_ani_기초단체장_5760"),
            [Path.Combine("Elect2026_Normal_민방", "1-2위_광역단체장_L")] = Path.Combine("Elect2026_Normal_민방", "1-2위_광역단체장_5760"),
            [Path.Combine("Elect2026_Normal_민방", "1-3위_ani_광역단체장_loop")] = Path.Combine("Elect2026_Normal_민방", "1-3위_ani_광역단체장"),
            [Path.Combine("Elect2026_Normal_민방", "1-3위_ani_기초단체장_loop")] = Path.Combine("Elect2026_Normal_민방", "1-3위_ani_기초단체장"),
            [Path.Combine("Elect2026_Normal_민방", "1-3위_기초단체장_5760_loop")] = Path.Combine("Elect2026_Normal_민방", "1-3위_기초단체장_5760"),
            [Path.Combine("Elect2026_Normal_민방", "1-3위_기초단체장_L")] = Path.Combine("Elect2026_Normal_민방", "1-3위_기초단체장_5760"),
            [Path.Combine("Elect2026_Normal_민방", "1-3위_기초단체장_L_1")] = Path.Combine("Elect2026_Normal_민방", "1-3위_기초단체장_5760"),
            [Path.Combine("Elect2026_Normal_민방", "1-3위_보궐선거_loop")] = Path.Combine("Elect2026_Normal_민방", "1-3위_보궐선거"),
            [Path.Combine("Elect2026_Normal_민방", "경력_광역단체장_loop")] = Path.Combine("Elect2026_Normal_민방", "경력_광역단체장_in"),
            [Path.Combine("Elect2026_Normal_민방", "경력_기초단체장_loop")] = Path.Combine("Elect2026_Normal_민방", "경력_기초단체장_in"),
            [Path.Combine("Elect2026_Normal_민방", "광역의원표_loop")] = Path.Combine("Elect2026_Normal_민방", "광역의원표"),
            [Path.Combine("Elect2026_Normal_민방", "광역의원표_HD_loop")] = Path.Combine("Elect2026_Normal_민방", "광역의원표_HD"),
            [Path.Combine("Elect2026_Normal_민방", "광역의원표_L")] = Path.Combine("Elect2026_Normal_민방", "광역의원표_HD"),
            [Path.Combine("Elect2026_Normal_민방", "광역의원표_L_1")] = Path.Combine("Elect2026_Normal_민방", "광역의원표_HD"),
            [Path.Combine("Elect2026_Normal_민방", "기초의원표_loop")] = Path.Combine("Elect2026_Normal_민방", "기초의원표"),
            [Path.Combine("Elect2026_Normal_민방", "기초의원표_HD_loop")] = Path.Combine("Elect2026_Normal_민방", "기초의원표_HD"),
            [Path.Combine("Elect2026_Normal_민방", "기초의원표_L")] = Path.Combine("Elect2026_Normal_민방", "기초의원표_HD"),
            [Path.Combine("Elect2026_Normal_민방", "기초의원표_L_1")] = Path.Combine("Elect2026_Normal_민방", "기초의원표_HD"),
            [Path.Combine("Elect2026_Normal_민방", "당선_광역단체장_L")] = Path.Combine("Elect2026_Normal_민방", "당선_광역단체장_HD"),
            [Path.Combine("Elect2026_Normal_민방", "당선_광역의원_L")] = Path.Combine("Elect2026_Normal_민방", "당선_광역의원_HD"),
            [Path.Combine("Elect2026_Normal_민방", "당선_교육감_L")] = Path.Combine("Elect2026_Normal_민방", "당선_교육감_HD"),
            [Path.Combine("Elect2026_Normal_민방", "당선_기초단체장_L")] = Path.Combine("Elect2026_Normal_민방", "당선_기초단체장_HD"),
            [Path.Combine("Elect2026_Normal_민방", "당선_기초의원_L")] = Path.Combine("Elect2026_Normal_민방", "당선_기초의원_HD"),
            [Path.Combine("Elect2026_Normal_민방", "모든후보_광역단체장_loop")] = Path.Combine("Elect2026_Normal_민방", "모든후보_광역단체장"),
            [Path.Combine("Elect2026_Normal_민방", "모든후보_광역단체장_5760_END")] = Path.Combine("Elect2026_Normal_민방", "모든후보_광역단체장_5760"),
            [Path.Combine("Elect2026_Normal_민방", "모든후보_광역단체장_END")] = Path.Combine("Elect2026_Normal_민방", "모든후보_광역단체장"),
            [Path.Combine("Elect2026_Normal_민방", "모든후보_광역단체장_L")] = Path.Combine("Elect2026_Normal_민방", "모든후보_광역단체장_5760"),
            [Path.Combine("Elect2026_Normal_민방", "모든후보_광역단체장_L_END")] = Path.Combine("Elect2026_Normal_민방", "모든후보_광역단체장_5760"),
            [Path.Combine("Elect2026_Normal_민방", "모든후보_교육감_loop")] = Path.Combine("Elect2026_Normal_민방", "모든후보_교육감"),
            [Path.Combine("Elect2026_Normal_민방", "모든후보_교육감_5760_END")] = Path.Combine("Elect2026_Normal_민방", "모든후보_교육감_5760"),
            [Path.Combine("Elect2026_Normal_민방", "모든후보_교육감_END")] = Path.Combine("Elect2026_Normal_민방", "모든후보_교육감"),
            [Path.Combine("Elect2026_Normal_민방", "모든후보_교육감_L")] = Path.Combine("Elect2026_Normal_민방", "모든후보_교육감_5760"),
            [Path.Combine("Elect2026_Normal_민방", "모든후보_교육감_L_END")] = Path.Combine("Elect2026_Normal_민방", "모든후보_교육감_5760"),
            [Path.Combine("Elect2026_Normal_민방", "모든후보_기초단체장_loop")] = Path.Combine("Elect2026_Normal_민방", "모든후보_기초단체장"),
            [Path.Combine("Elect2026_Normal_민방", "모든후보_기초단체장_5760_END")] = Path.Combine("Elect2026_Normal_민방", "모든후보_기초단체장_5760"),
            [Path.Combine("Elect2026_Normal_민방", "모든후보_기초단체장_END")] = Path.Combine("Elect2026_Normal_민방", "모든후보_기초단체장"),
            [Path.Combine("Elect2026_Normal_민방", "모든후보_기초단체장_L")] = Path.Combine("Elect2026_Normal_민방", "모든후보_기초단체장_5760"),
            [Path.Combine("Elect2026_Normal_민방", "모든후보_기초단체장_L_END")] = Path.Combine("Elect2026_Normal_민방", "모든후보_기초단체장_5760"),
            [Path.Combine("Elect2026_Normal_민방", "민방_타이틀_L")] = Path.Combine("Elect2026_Normal_민방", "민방_타이틀_1920"),
            [Path.Combine("Elect2026_Normal_민방", "민방_타이틀_L_nologo")] = Path.Combine("Elect2026_Normal_민방", "민방_타이틀_5760_nologo"),
            [Path.Combine("Elect2026_Normal_민방", "사전_역대투표율_loop")] = Path.Combine("Elect2026_Normal_민방", "사전_역대투표율"),
            [Path.Combine("Elect2026_Normal_민방", "사전_역대투표율_5760_loop")] = Path.Combine("Elect2026_Normal_민방", "사전_역대투표율_5760"),
            [Path.Combine("Elect2026_Normal_민방", "사전_역대투표율_L")] = Path.Combine("Elect2026_Normal_민방", "사전_역대투표율_5760"),
            [Path.Combine("Elect2026_Normal_민방", "사전_역대투표율_L_1")] = Path.Combine("Elect2026_Normal_민방", "사전_역대투표율_5760"),
            [Path.Combine("Elect2026_Normal_민방", "투표율_선거구별 사전_loop")] = Path.Combine("Elect2026_Normal_민방", "투표율_선거구별 사전"),
            [Path.Combine("Elect2026_Normal_민방", "투표율_사진")] = Path.Combine("Elect2026_Normal_민방", "투표율"),
            [Path.Combine("Elect2026_Normal_민방", "투표율_시도별")] = Path.Combine("Elect2026_Normal_민방", "투표율"),
            [Path.Combine("Elect2026_Normal_민방", "투표율_시도별_loop")] = Path.Combine("Elect2026_Normal_민방", "투표율"),
            [Path.Combine("Elect2026_Normal_민방", "투표율_시도별_L")] = Path.Combine("Elect2026_Normal_민방", "투표율"),
            [Path.Combine("Elect2026_Normal_민방", "투표율_시도별_L_loop")] = Path.Combine("Elect2026_Normal_민방", "투표율"),
            [Path.Combine("Elect2026_Normal_민방", "투표율_영상")] = Path.Combine("Elect2026_Normal_민방", "투표율"),
            [Path.Combine("Elect2026_Normal_민방", "판세_기초단체장_7680")] = Path.Combine("Elect2026_Normal_민방", "판세_기초단체장_5760"),
            [Path.Combine("Elect2026_Top_민방", "투표율_loop")] = Path.Combine("Elect2026_Top_민방", "투표율"),
            [Path.Combine("Elect2026_Top_민방", "투표율_선거구별_loop")] = Path.Combine("Elect2026_Top_민방", "투표율_선거구별")
        };
    }

    private static bool IsAvailableInBothPhases(string baseName)
    {
        return ScheduleTemplatePolicy.IsTitleFormat(baseName) ||
            baseName.StartsWith("사전_역대당선", StringComparison.Ordinal) ||
            baseName.StartsWith("경력_", StringComparison.Ordinal);
    }

    private static bool IsHistoricalPreElectionWinnerFormat(string baseName)
    {
        return baseName.StartsWith("사전_역대당선", StringComparison.Ordinal);
    }

    private static BroadcastChannel ResolveRecommendedChannel(
        BroadcastChannel fallbackChannel,
        string baseName,
        KarismaSceneResolution? sceneResolution)
    {
        if (fallbackChannel != BroadcastChannel.Normal)
        {
            return fallbackChannel;
        }

        if (TryResolveRecommendedChannelFromSceneSize(sceneResolution, out var resolvedChannel))
        {
            return resolvedChannel;
        }

        return IsVideoWallFormat(baseName)
            ? BroadcastChannel.VideoWall
            : fallbackChannel;
    }

    private static bool IsVideoWallFormat(string baseName)
    {
        return baseName.Contains("_3840", StringComparison.Ordinal) ||
               baseName.Contains("_2880", StringComparison.Ordinal) ||
               baseName.Contains("_8316", StringComparison.Ordinal) ||
               baseName.Contains("_5760", StringComparison.Ordinal) ||
               baseName.Contains("_L", StringComparison.Ordinal);
    }

    private static KarismaSceneResolution? TryReadSceneResolution(string sceneId, string t3CutPath)
    {
        if (string.IsNullOrWhiteSpace(t3CutPath))
        {
            return null;
        }

        var scenePath = Path.Combine(t3CutPath, sceneId + ".tscn");
        return KarismaSceneResolutionReader.TryRead(scenePath, out var resolution)
            ? resolution
            : null;
    }

    private static bool TryResolveRecommendedChannelFromSceneSize(
        KarismaSceneResolution? sceneResolution,
        out BroadcastChannel channel)
    {
        channel = BroadcastChannel.Normal;

        if (sceneResolution is null)
        {
            return false;
        }

        channel = sceneResolution.Value.Width > 1920
                ? BroadcastChannel.VideoWall
                : BroadcastChannel.Normal;

        return true;
    }

    private static string ResolveT3CutPath(string? configuredT3CutPath)
    {
        return TornadoPathResolver.GetDefaultT3CutPath();
    }
}
