using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Tornado3_2026Election.Common;

namespace Tornado3_2026Election.Domain;

public enum CutDebugItemKind
{
    TextValue,
    ImageValue,
    Counter,
    StyleColor,
    Visibility
}

public readonly record struct CutDebugItemDescriptor(
    string Key,
    CutDebugItemKind Kind,
    string GroupLabel);

public sealed class CutDebugItemState : ObservableObject
{
    private readonly Action<bool> _onIsEnabledChanged;
    private bool _isEnabled;

    public CutDebugItemState(
        string key,
        CutDebugItemKind kind,
        string groupLabel,
        bool isEnabled,
        Action<bool> onIsEnabledChanged)
    {
        Key = key;
        Kind = kind;
        GroupLabel = groupLabel;
        _isEnabled = isEnabled;
        _onIsEnabledChanged = onIsEnabledChanged;
    }

    public string Key { get; }

    public CutDebugItemKind Kind { get; }

    public string GroupLabel { get; }

    public string KindLabel => Kind switch
    {
        CutDebugItemKind.TextValue => "텍스트",
        CutDebugItemKind.ImageValue => "이미지",
        CutDebugItemKind.Counter => "카운터",
        CutDebugItemKind.StyleColor => "색상",
        CutDebugItemKind.Visibility => "표시",
        _ => "기타"
    };

    public bool IsEnabled
    {
        get => _isEnabled;
        set
        {
            if (SetProperty(ref _isEnabled, value))
            {
                _onIsEnabledChanged(value);
            }
        }
    }
}

public sealed class CutDebugTemplateState
{
    private readonly ConcurrentDictionary<string, bool> _enabledStates = new(StringComparer.OrdinalIgnoreCase);
    private readonly ConcurrentDictionary<string, CutDebugOverride> _overrides = new(StringComparer.OrdinalIgnoreCase);

    public CutDebugTemplateState(string formatId, string displayName)
    {
        FormatId = formatId;
        DisplayName = displayName;
        Items = [];
    }

    public string FormatId { get; }

    public string DisplayName { get; private set; }

    public ObservableCollection<CutDebugItemState> Items { get; }

    public void UpdateDisplayName(string displayName)
    {
        if (!string.IsNullOrWhiteSpace(displayName))
        {
            DisplayName = displayName;
        }
    }

    public void SyncItems(IEnumerable<CutDebugItemDescriptor> descriptors)
    {
        var normalizedDescriptors = descriptors
            .Where(descriptor => !string.IsNullOrWhiteSpace(descriptor.Key))
            .Select(descriptor => descriptor with { Key = NormalizeKey(descriptor.Key) })
            .GroupBy(descriptor => ComposeStateKey(descriptor.Kind, descriptor.Key), StringComparer.OrdinalIgnoreCase)
            .Select(group => group.First())
            .OrderBy(descriptor => (int)descriptor.Kind)
            .ThenBy(descriptor => descriptor.Key, StringComparer.OrdinalIgnoreCase)
            .ToArray();

        var activeKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        Items.Clear();

        foreach (var descriptor in normalizedDescriptors)
        {
            var stateKey = ComposeStateKey(descriptor.Kind, descriptor.Key);
            activeKeys.Add(stateKey);

            var initialState = _enabledStates.TryGetValue(stateKey, out var enabled) ? enabled : true;
            _enabledStates[stateKey] = initialState;
            Items.Add(new CutDebugItemState(
                descriptor.Key,
                descriptor.Kind,
                descriptor.GroupLabel,
                initialState,
                isEnabled => _enabledStates[stateKey] = isEnabled));
        }

        foreach (var staleKey in _enabledStates.Keys.Where(key => !activeKeys.Contains(key)).ToArray())
        {
            _enabledStates.TryRemove(staleKey, out _);
        }

        foreach (var staleKey in _overrides.Keys.Where(key => !activeKeys.Contains(key)).ToArray())
        {
            _overrides.TryRemove(staleKey, out _);
        }
    }

    public bool IsEnabled(string key, CutDebugItemKind kind)
    {
        var stateKey = ComposeStateKey(kind, NormalizeKey(key));
        return !_enabledStates.TryGetValue(stateKey, out var enabled) || enabled;
    }

    public void SetOverride(string key, CutDebugItemKind kind, CutDebugOverride overrideValue)
    {
        var stateKey = ComposeStateKey(kind, NormalizeKey(key));
        if (overrideValue.Mode == CutDebugOverrideMode.None)
        {
            _overrides.TryRemove(stateKey, out _);
            return;
        }

        _overrides[stateKey] = overrideValue;
    }

    public bool TryGetOverride(string key, CutDebugItemKind kind, out CutDebugOverride overrideValue)
    {
        var stateKey = ComposeStateKey(kind, NormalizeKey(key));
        if (_overrides.TryGetValue(stateKey, out overrideValue) &&
            overrideValue.Mode != CutDebugOverrideMode.None)
        {
            return true;
        }

        overrideValue = CutDebugOverride.None;
        return false;
    }

    public void ClearOverrides()
    {
        _overrides.Clear();
    }

    public static string NormalizeKey(string key)
    {
        if (string.IsNullOrWhiteSpace(key))
        {
            return string.Empty;
        }

        if (string.Equals(key, "유확당", StringComparison.Ordinal))
        {
            return "유확당01";
        }

        foreach (var prefix in IndexedPrefixes)
        {
            if (!key.StartsWith(prefix, StringComparison.Ordinal))
            {
                continue;
            }

            var suffix = key.Substring(prefix.Length);
            if (suffix.Length == 0)
            {
                return BareSlotOnePrefixes.Contains(prefix, StringComparer.Ordinal)
                    ? $"{prefix}01"
                    : key;
            }

            if (suffix.Length <= 2 && suffix.All(char.IsDigit) && int.TryParse(suffix, out var index))
            {
                return $"{prefix}{index:00}";
            }

            return key;
        }

        return key;
    }

    private static string ComposeStateKey(CutDebugItemKind kind, string key)
    {
        return $"{kind}|{key}";
    }

    private static readonly string[] IndexedPrefixes =
    [
        "순위",
        "기호",
        "기호텍스트",
        "후보명",
        "정당명",
        "득표수",
        "득표율",
        "표차",
        "득표차",
        "선거구명",
        "시도명",
        "개표율",
        "투표율",
        "전국투표율",
        "기준시",
        "유권자수",
        "투표자수",
        "유확당",
        "후보사진",
        "득표수바",
        "정당바",
        "정당판",
        "정당원",
        "정당색",
        "정당심볼",
        "그룹",
        "공약그룹",
        "공약",
        "바"
    ];

    private static readonly string[] BareSlotOnePrefixes =
    [
        "선거구명",
        "시도명",
        "개표율",
        "투표율",
        "전국투표율",
        "기준시",
        "유권자수",
        "투표자수"
    ];
}
