using MMoneyCoderSharp.Data;
using MBN_STOCK_WEBVIEW.Core.Playout.Scenes;

namespace MBN_STOCK_WEBVIEW.LegacyApplication;

public enum LegacyIndustryActionKind
{
    Single,
    Pair,
    Yield,
    Candle,
    FiveRow,
    Square,
    Sector
}

public enum LegacyIndustryCandleMode
{
    Price,
    Volume
}

public sealed record LegacyIndustryRuntimeAsset(
    IndustryMarket Market,
    string RelativePath,
    string Sha256);

public sealed record LegacyIndustryActionDefinition(
    IndustryMarket Market,
    string ActionId,
    string Label,
    string Section,
    string Category,
    LegacyIndustryActionKind Kind,
    string BuilderKey,
    string SceneAlias,
    int PageSize,
    string? YieldPeriod = null,
    LegacyIndustryCandleMode? CandleMode = null);

/// <summary>
/// Closed projection of the two runtime industry INI files. The WebView supplies an
/// action id only; market-specific aliases and page sizes remain authoritative in C#.
/// </summary>
public static class LegacyIndustryActionCatalog
{
    public const int ActionsPerMarket = 22;

    private static readonly LegacyIndustryRuntimeAsset KospiAsset = new(
        IndustryMarket.Kospi,
        "bin/Debug/Res/업종_코스피.ini",
        "F21DE58003315EAB41F9FE7B5A573C71653056203E6743D05AA96E7FF1B8BF52");

    private static readonly LegacyIndustryRuntimeAsset KosdaqAsset = new(
        IndustryMarket.Kosdaq,
        "bin/Debug/Res/업종_코스닥.ini",
        "F25B8926E8F20FC5FBC94A1891A9E2EAD22852E8C829321EF1A001FE3F42ECE4");

    private static readonly IReadOnlyList<LegacyIndustryActionDefinition> KospiActions =
        CreateActions(IndustryMarket.Kospi);

    private static readonly IReadOnlyList<LegacyIndustryActionDefinition> KosdaqActions =
        CreateActions(IndustryMarket.Kosdaq);

    private static readonly IReadOnlyDictionary<string, LegacyIndustryActionDefinition>
        KospiActionsById = CreateLookup(KospiActions);

    private static readonly IReadOnlyDictionary<string, LegacyIndustryActionDefinition>
        KosdaqActionsById = CreateLookup(KosdaqActions);

    public static LegacyIndustryRuntimeAsset GetRuntimeAsset(IndustryMarket market) => market switch
    {
        IndustryMarket.Kospi => KospiAsset,
        IndustryMarket.Kosdaq => KosdaqAsset,
        _ => throw new ArgumentOutOfRangeException(nameof(market), market, "Unsupported industry market.")
    };

    public static IReadOnlyList<LegacyIndustryActionDefinition> GetActions(
        IndustryMarket market) => market switch
    {
        IndustryMarket.Kospi => KospiActions,
        IndustryMarket.Kosdaq => KosdaqActions,
        _ => throw new ArgumentOutOfRangeException(nameof(market), market, "Unsupported industry market.")
    };

    public static bool TryGetAction(
        IndustryMarket market,
        string actionId,
        out LegacyIndustryActionDefinition? definition)
    {
        ArgumentNullException.ThrowIfNull(actionId);
        if (!IsSafeActionId(actionId))
        {
            definition = null;
            return false;
        }

        return LookupFor(market).TryGetValue(actionId, out definition);
    }

    public static LegacyIndustryActionDefinition GetRequiredAction(
        IndustryMarket market,
        string actionId)
    {
        ArgumentNullException.ThrowIfNull(actionId);
        return IsSafeActionId(actionId) &&
            LookupFor(market).TryGetValue(actionId, out var definition)
            ? definition
            : throw new KeyNotFoundException("The industry action id is not registered.");
    }

    private static IReadOnlyDictionary<string, LegacyIndustryActionDefinition> LookupFor(
        IndustryMarket market) => market switch
    {
        IndustryMarket.Kospi => KospiActionsById,
        IndustryMarket.Kosdaq => KosdaqActionsById,
        _ => throw new ArgumentOutOfRangeException(nameof(market), market, "Unsupported industry market.")
    };

    private static bool IsSafeActionId(string actionId) =>
        actionId.Length is > 0 and <= 64 &&
        !actionId.Any(char.IsControl) &&
        string.Equals(actionId, actionId.Trim(), StringComparison.Ordinal);

    private static IReadOnlyList<LegacyIndustryActionDefinition> CreateActions(
        IndustryMarket market)
    {
        var pairAlias = market == IndustryMarket.Kospi ? "8018" : "8032";
        var squareAlias = market == IndustryMarket.Kospi ? "8001" : "8002";
        var actions = new List<LegacyIndustryActionDefinition>(ActionsPerMarket)
        {
            Action(market, "one-column", "1열판", "업종 판", "plate",
                LegacyIndustryActionKind.Single, "s5001", "5001"),
            Action(market, "two-column", "2열판", "업종 판", "plate",
                LegacyIndustryActionKind.Pair, "s8018", pairAlias)
        };

        AddYield(actions, market, "5일", "FiveDays");
        AddYield(actions, market, "20일", "TwentyDays");
        AddYield(actions, market, "60일", "SixtyDays");
        AddYield(actions, market, "120일", "OneHundredTwentyDays");
        AddYield(actions, market, "240일", "TwoHundredFortyDays");

        AddCandle(actions, market, "일봉", "8035", LegacyIndustryCandleMode.Price);
        AddCandle(actions, market, "5일", "8061", LegacyIndustryCandleMode.Price);
        AddCandle(actions, market, "20일", "8040", LegacyIndustryCandleMode.Price);
        AddCandle(actions, market, "60일", "8046", LegacyIndustryCandleMode.Price);
        AddCandle(actions, market, "120일", "8051", LegacyIndustryCandleMode.Price);
        AddCandle(actions, market, "240일", "8056", LegacyIndustryCandleMode.Price);

        AddCandle(actions, market, "일봉", "8035", LegacyIndustryCandleMode.Volume);
        AddCandle(actions, market, "5일", "8061", LegacyIndustryCandleMode.Volume);
        AddCandle(actions, market, "20일", "8040", LegacyIndustryCandleMode.Volume);
        AddCandle(actions, market, "60일", "8046", LegacyIndustryCandleMode.Volume);
        AddCandle(actions, market, "120일", "8051", LegacyIndustryCandleMode.Volume);
        AddCandle(actions, market, "240일", "8056", LegacyIndustryCandleMode.Volume);

        actions.Add(Action(market, "five-row", "5단 표그래프", "시장 전체", "plate",
            LegacyIndustryActionKind.FiveRow, "s5074", "5074", pageSize: 5));
        actions.Add(Action(market, "square", "네모그래프", "시장 전체", "plate",
            LegacyIndustryActionKind.Square, "s8001", squareAlias));
        actions.Add(Action(market, "sector", "섹터지수", "시장 전체", "plate",
            LegacyIndustryActionKind.Sector, "s5078", "5078"));

        Validate(actions, market);
        return Array.AsReadOnly(actions.ToArray());
    }

    private static void AddYield(
        ICollection<LegacyIndustryActionDefinition> actions,
        IndustryMarket market,
        string periodLabel,
        string period)
    {
        actions.Add(Action(
            market,
            $"yield-{periodLabel}",
            $"수익률그래프_{periodLabel}",
            "수익률 그래프",
            "chart",
            LegacyIndustryActionKind.Yield,
            "s5086",
            "5086",
            yieldPeriod: period));
    }

    private static void AddCandle(
        ICollection<LegacyIndustryActionDefinition> actions,
        IndustryMarket market,
        string periodLabel,
        string sceneAlias,
        LegacyIndustryCandleMode mode)
    {
        var volume = mode == LegacyIndustryCandleMode.Volume;
        actions.Add(Action(
            market,
            $"candle-{(volume ? "volume-" : string.Empty)}{periodLabel}",
            $"캔들그래프{(volume ? "(거래량)" : string.Empty)}_{periodLabel}",
            volume ? "캔들 그래프 · 거래량" : "캔들 그래프",
            "chart",
            LegacyIndustryActionKind.Candle,
            "s8010",
            sceneAlias,
            candleMode: mode));
    }

    private static LegacyIndustryActionDefinition Action(
        IndustryMarket market,
        string actionId,
        string label,
        string section,
        string category,
        LegacyIndustryActionKind kind,
        string builderKey,
        string sceneAlias,
        int pageSize = 0,
        string? yieldPeriod = null,
        LegacyIndustryCandleMode? candleMode = null) => new(
            market,
            actionId,
            label,
            section,
            category,
            kind,
            builderKey,
            sceneAlias,
            pageSize,
            yieldPeriod,
            candleMode);

    private static IReadOnlyDictionary<string, LegacyIndustryActionDefinition> CreateLookup(
        IReadOnlyList<LegacyIndustryActionDefinition> actions) =>
        new System.Collections.ObjectModel.ReadOnlyDictionary<
            string,
            LegacyIndustryActionDefinition>(actions.ToDictionary(
                action => action.ActionId,
                StringComparer.Ordinal));

    private static void Validate(
        IReadOnlyCollection<LegacyIndustryActionDefinition> actions,
        IndustryMarket market)
    {
        if (actions.Count != ActionsPerMarket ||
            actions.Select(action => action.ActionId).Distinct(StringComparer.Ordinal).Count() !=
            ActionsPerMarket ||
            actions.Any(action => action.Market != market) ||
            actions.Any(action => action.PageSize is not (0 or 5)) ||
            actions.Any(action =>
                (action.Kind == LegacyIndustryActionKind.FiveRow) != (action.PageSize == 5)) ||
            actions.Any(action => action.Kind == LegacyIndustryActionKind.Yield &&
                string.IsNullOrEmpty(action.YieldPeriod)) ||
            actions.Any(action => action.Kind != LegacyIndustryActionKind.Yield &&
                action.YieldPeriod is not null) ||
            actions.Any(action => action.Kind == LegacyIndustryActionKind.Candle &&
                action.CandleMode is null) ||
            actions.Any(action => action.Kind != LegacyIndustryActionKind.Candle &&
                action.CandleMode is not null) ||
            actions.Any(action => !IsSafeActionId(action.ActionId)) ||
            actions.Any(action => !LegacySceneCatalog.FindByCutAlias(action.SceneAlias)
                .Any(scene =>
                    string.Equals(scene.BuilderKey, action.BuilderKey, StringComparison.Ordinal) &&
                    (int)scene.PageSize == action.PageSize)))
        {
            throw new InvalidOperationException("The legacy industry action catalog is invalid.");
        }
    }
}
