#nullable enable

namespace MBN_STOCK_WEBVIEW.Core.Playout.Scenes;

/// <summary>
/// Closed result for the five chart cuts whose legacy constructors selected a DB
/// branch from MainForm's code/jongmok/forCutInfo/sub playlist columns.
/// </summary>
public abstract record LegacyChartSceneLoadRequest(string BuilderKey);

public sealed record LegacyS5078ChartLoadRequest(S5078SceneDataRequest Request)
    : LegacyChartSceneLoadRequest("s5078");

public sealed record LegacyS5079ChartLoadRequest(S5079SceneDataRequest Request)
    : LegacyChartSceneLoadRequest("s5079");

public sealed record LegacyS5080ChartLoadRequest(S5080SceneDataRequest Request)
    : LegacyChartSceneLoadRequest("s5080");

public sealed record LegacyS5083ChartLoadRequest(S5083SceneDataRequest Request)
    : LegacyChartSceneLoadRequest("s5083");

public sealed record LegacyS5084ChartLoadRequest(S5084SceneDataRequest Request)
    : LegacyChartSceneLoadRequest("s5084");

/// <summary>
/// Reproduces MainForm's five chart-constructor inputs without carrying forward its
/// substring-based cut dispatch. LegacySceneSelection maps GroupCode=code,
/// Subject=jongmok, GraphicType=forCutInfo, Subtype=sub and DataCode=the hidden
/// stock-code column. Every accepted selector belongs to an explicit closed set.
/// </summary>
public sealed class ChartLegacySceneRequestResolver
{
    private const int MaximumFieldLength = 256;
    private const int MaximumDataCodeLength = 32;

    public LegacyChartSceneLoadRequest Resolve(LegacyPlaylistEntry entry)
    {
        ArgumentNullException.ThrowIfNull(entry);
        return entry.CutCode switch
        {
            "5078" => new LegacyS5078ChartLoadRequest(CreateS5078Request(entry)),
            "5079" => new LegacyS5079ChartLoadRequest(CreateS5079Request(entry)),
            "5080" => new LegacyS5080ChartLoadRequest(CreateS5080Request(entry)),
            "5083" => new LegacyS5083ChartLoadRequest(CreateS5083Request(entry)),
            "5084" => new LegacyS5084ChartLoadRequest(CreateS5084Request(entry)),
            _ => throw InvalidSelection()
        };
    }

    public S5078SceneDataRequest CreateS5078Request(LegacyPlaylistEntry entry)
    {
        var selection = Require(entry, "5078");
        RequireEmpty(selection.Subtype);
        RequireEmpty(selection.DataCode);

        var subject = RequiredExact(selection.Subject);
        var group = OptionalTrimmed(selection.GroupCode);
        var graphic = RequiredTrimmed(selection.GraphicType);
        var market = subject switch
        {
            "다우" or "DOW" => SectorIndexMarket.Dow,
            "나스닥" or "NASDAQ" => SectorIndexMarket.Nasdaq,
            "S&P" or "S&P500" => SectorIndexMarket.StandardAndPoor,
            "코스피" or "KOSPI" => SectorIndexMarket.Kospi,
            "코스닥" or "KOSDAQ" => SectorIndexMarket.Kosdaq,
            _ => throw InvalidSelection()
        };

        if (market is SectorIndexMarket.Dow or
            SectorIndexMarket.Nasdaq or
            SectorIndexMarket.StandardAndPoor)
        {
            if (!IsOneOf(group, "해외지수", "FOREIGN_INDEX") ||
                !IsOneOf(graphic, "미국업종", "US_SECTOR_INDEX"))
            {
                throw InvalidSelection();
            }
        }
        else if (market == SectorIndexMarket.Kospi)
        {
            if (!IsOneOf(group, "업종_코스피", "INDUSTRY_KOSPI") ||
                !IsOneOf(graphic, "섹터지수", "SECTOR_INDEX"))
            {
                throw InvalidSelection();
            }
        }
        else if (!IsOneOf(group, "업종_코스닥", "INDUSTRY_KOSDAQ") ||
                 !IsOneOf(graphic, "섹터지수", "SECTOR_INDEX"))
        {
            throw InvalidSelection();
        }

        return new S5078SceneDataRequest(market);
    }

    public S5079SceneDataRequest CreateS5079Request(LegacyPlaylistEntry entry)
    {
        var selection = Require(entry, "5079");
        ValidateManualStockSelection(selection, "성장성 지표", "GROWTH_METRICS");
        return new S5079SceneDataRequest(RequiredExact(selection.Subject));
    }

    public S5080SceneDataRequest CreateS5080Request(LegacyPlaylistEntry entry)
    {
        var selection = Require(entry, "5080");
        ValidateManualStockSelection(selection, "매출액", "SALES");
        return new S5080SceneDataRequest(RequiredExact(selection.Subject));
    }

    public S5083SceneDataRequest CreateS5083Request(LegacyPlaylistEntry entry)
    {
        var selection = Require(entry, "5083");
        RequireIndexLineGraph(selection, expectedGroup: "전체", canonicalGroup: "ALL");
        var participant = RequiredTrimmed(selection.GraphicType) switch
        {
            "개인 매매동향" or "INDIVIDUAL_TRADING_TREND" =>
                InvestorFlowParticipant.Individual,
            "기관 매매동향" or "INSTITUTION_TRADING_TREND" =>
                InvestorFlowParticipant.Institution,
            "외국인 매매동향" or "FOREIGN_TRADING_TREND" =>
                InvestorFlowParticipant.Foreign,
            _ => throw InvalidSelection()
        };
        return new S5083SceneDataRequest(participant);
    }

    public S5084SceneDataRequest CreateS5084Request(LegacyPlaylistEntry entry)
    {
        var selection = Require(entry, "5084");
        RequireCommonIndexLineFields(selection);
        var group = RequiredTrimmed(selection.GroupCode);
        var index = RequiredTrimmed(selection.GraphicType) switch
        {
            "코스피 매매동향" or "KOSPI_TRADING_TREND"
                when IsOneOf(group, "코스피", "KOSPI") => InvestorFlowIndex.Kospi,
            "코스닥 매매동향" or "KOSDAQ_TRADING_TREND"
                when IsOneOf(group, "코스닥", "KOSDAQ") => InvestorFlowIndex.Kosdaq,
            _ => throw InvalidSelection()
        };
        return new S5084SceneDataRequest(index);
    }

    private static void ValidateManualStockSelection(
        LegacySceneSelection selection,
        string originalGraphic,
        string canonicalGraphic)
    {
        _ = RequiredExact(selection.Subject);
        var group = RequiredTrimmed(selection.GroupCode);
        if (!IsOneOf(
                group,
                "코스피",
                "코스닥",
                "코스피_NXT",
                "코스닥_NXT",
                "KOSPI",
                "KOSDAQ",
                "NXT_KOSPI",
                "NXT_KOSDAQ"))
        {
            throw InvalidSelection();
        }

        if (!IsOneOf(RequiredTrimmed(selection.GraphicType), originalGraphic, canonicalGraphic))
        {
            throw InvalidSelection();
        }

        RequireEmpty(selection.Subtype);
        ValidateOptionalDataCode(selection.DataCode);
    }

    private static void RequireIndexLineGraph(
        LegacySceneSelection selection,
        string expectedGroup,
        string canonicalGroup)
    {
        RequireCommonIndexLineFields(selection);
        if (!IsOneOf(RequiredTrimmed(selection.GroupCode), expectedGroup, canonicalGroup))
        {
            throw InvalidSelection();
        }
    }

    private static void RequireCommonIndexLineFields(LegacySceneSelection selection)
    {
        if (!IsOneOf(RequiredTrimmed(selection.Subject), "지수", "INDEX") ||
            !IsOneOf(RequiredTrimmed(selection.Subtype), "라인그래프", "LINE_GRAPH"))
        {
            throw InvalidSelection();
        }

        RequireEmpty(selection.DataCode);
    }

    private static LegacySceneSelection Require(LegacyPlaylistEntry entry, string cutCode)
    {
        ArgumentNullException.ThrowIfNull(entry);
        if (entry.CutCode != cutCode || entry.Selection is null)
        {
            throw InvalidSelection();
        }

        ValidateField(entry.Selection.GroupCode);
        ValidateField(entry.Selection.Subject);
        ValidateField(entry.Selection.GraphicType);
        ValidateField(entry.Selection.Subtype);
        ValidateField(entry.Selection.DataCode);
        return entry.Selection;
    }

    private static string RequiredExact(string? value)
    {
        ValidateField(value);
        if (string.IsNullOrWhiteSpace(value) || value != value.Trim())
        {
            throw InvalidSelection();
        }

        return value;
    }

    private static string RequiredTrimmed(string? value)
    {
        ValidateField(value);
        var trimmed = value!.Trim();
        return trimmed.Length != 0 ? trimmed : throw InvalidSelection();
    }

    private static string OptionalTrimmed(string? value)
    {
        ValidateField(value);
        return value!.Trim();
    }

    private static void RequireEmpty(string? value)
    {
        ValidateField(value);
        if (!string.IsNullOrWhiteSpace(value))
        {
            throw InvalidSelection();
        }
    }

    private static void ValidateOptionalDataCode(string? value)
    {
        ValidateField(value);
        if (string.IsNullOrEmpty(value))
        {
            return;
        }

        if (value.Length > MaximumDataCodeLength ||
            value.Any(character => !char.IsAsciiLetterOrDigit(character)))
        {
            throw InvalidSelection();
        }
    }

    private static void ValidateField(string? value)
    {
        if (value is null || value.Length > MaximumFieldLength || value.Any(char.IsControl))
        {
            throw InvalidSelection();
        }
    }

    private static bool IsOneOf(string value, params string[] accepted) =>
        accepted.Contains(value, StringComparer.Ordinal);

    private static LegacySceneDataException InvalidSelection() =>
        new("The chart legacy selection is unsupported or invalid.");
}
