using System.Text;
using System.Windows;
using AxCopilot.SDK;
using AxCopilot.Services;

namespace AxCopilot.Handlers;

/// <summary>
/// L23-4: 한/영 타이핑 오류 교정. "fix" 프리픽스로 사용합니다.
///
/// 예: fix gksrmf     → 안녕 (영타→한글 변환)
///     fix            → 클립보드 텍스트 자동 교정
/// Enter → 교정 결과 클립보드 복사
/// </summary>
public class FixHandler : IActionHandler
{
    public string? Prefix => "fix";

    public PluginMetadata Metadata => new(
        "타이핑 교정",
        "영타→한글 변환 — 잘못 입력된 영문 타이핑을 한글로 교정",
        "1.0",
        "AX");

    // ── 두벌식 영→자모 매핑 ──────────────────────────────────────────────────

    // 소문자
    private static readonly Dictionary<char, char> EngToJamo = new()
    {
        {'q', 'ㅂ'}, {'w', 'ㅈ'}, {'e', 'ㄷ'}, {'r', 'ㄱ'}, {'t', 'ㅅ'},
        {'y', 'ㅛ'}, {'u', 'ㅕ'}, {'i', 'ㅑ'}, {'o', 'ㅐ'}, {'p', 'ㅔ'},
        {'a', 'ㅁ'}, {'s', 'ㄴ'}, {'d', 'ㅇ'}, {'f', 'ㄹ'}, {'g', 'ㅎ'},
        {'h', 'ㅗ'}, {'j', 'ㅓ'}, {'k', 'ㅏ'}, {'l', 'ㅣ'},
        {'z', 'ㅋ'}, {'x', 'ㅌ'}, {'c', 'ㅊ'}, {'v', 'ㅍ'},
        {'b', 'ㅠ'}, {'n', 'ㅜ'}, {'m', 'ㅡ'},
        // 대문자 = 소문자와 동일 기본 (Shift 된소리/쌍모음 별도)
        {'Q', 'ㅃ'}, {'W', 'ㅉ'}, {'E', 'ㄸ'}, {'R', 'ㄲ'}, {'T', 'ㅆ'},
        {'Y', 'ㅛ'}, {'U', 'ㅕ'}, {'I', 'ㅑ'}, {'O', 'ㅒ'}, {'P', 'ㅖ'},
        {'A', 'ㅁ'}, {'S', 'ㄴ'}, {'D', 'ㅇ'}, {'F', 'ㄹ'}, {'G', 'ㅎ'},
        {'H', 'ㅗ'}, {'J', 'ㅓ'}, {'K', 'ㅏ'}, {'L', 'ㅣ'},
        {'Z', 'ㅋ'}, {'X', 'ㅌ'}, {'C', 'ㅊ'}, {'V', 'ㅍ'},
        {'B', 'ㅠ'}, {'N', 'ㅜ'}, {'M', 'ㅡ'},
    };

    // ── 초성 배열 (19개) ──────────────────────────────────────────────────────
    private static readonly char[] Choseong =
        ['ㄱ','ㄲ','ㄴ','ㄷ','ㄸ','ㄹ','ㅁ','ㅂ','ㅃ','ㅅ','ㅆ','ㅇ','ㅈ','ㅉ','ㅊ','ㅋ','ㅌ','ㅍ','ㅎ'];

    // ── 중성 배열 (21개) ──────────────────────────────────────────────────────
    private static readonly char[] Jungseong =
        ['ㅏ','ㅐ','ㅑ','ㅒ','ㅓ','ㅔ','ㅕ','ㅖ','ㅗ','ㅘ','ㅙ','ㅚ','ㅛ','ㅜ','ㅝ','ㅞ','ㅟ','ㅠ','ㅡ','ㅢ','ㅣ'];

    // ── 종성 배열 (28개, 0=없음) ─────────────────────────────────────────────
    private static readonly char[] Jongseong =
        ['\0','ㄱ','ㄲ','ㄳ','ㄴ','ㄵ','ㄶ','ㄷ','ㄹ','ㄺ','ㄻ','ㄼ','ㄽ','ㄾ','ㄿ','ㅀ','ㅁ','ㅂ','ㅄ','ㅅ','ㅆ','ㅇ','ㅈ','ㅊ','ㅋ','ㅌ','ㅍ','ㅎ'];

    // ── 복합 모음 ─────────────────────────────────────────────────────────────
    private static readonly Dictionary<(char, char), char> CompoundVowel = new()
    {
        {('ㅗ','ㅏ'), 'ㅘ'}, {('ㅗ','ㅐ'), 'ㅙ'}, {('ㅗ','ㅣ'), 'ㅚ'},
        {('ㅜ','ㅓ'), 'ㅝ'}, {('ㅜ','ㅔ'), 'ㅞ'}, {('ㅜ','ㅣ'), 'ㅟ'},
        {('ㅡ','ㅣ'), 'ㅢ'},
    };

    // ── 복합 종성 ─────────────────────────────────────────────────────────────
    private static readonly Dictionary<(char, char), char> CompoundJong = new()
    {
        {('ㄱ','ㅅ'), 'ㄳ'}, {('ㄴ','ㅈ'), 'ㄵ'}, {('ㄴ','ㅎ'), 'ㄶ'},
        {('ㄹ','ㄱ'), 'ㄺ'}, {('ㄹ','ㅁ'), 'ㄻ'}, {('ㄹ','ㅂ'), 'ㄼ'},
        {('ㄹ','ㅅ'), 'ㄽ'}, {('ㄹ','ㅌ'), 'ㄾ'}, {('ㄹ','ㅍ'), 'ㄿ'},
        {('ㄹ','ㅎ'), 'ㅀ'}, {('ㅂ','ㅅ'), 'ㅄ'},
    };

    // 복합 종성 분리 (모음이 올 때 jong → cho + remain)
    private static readonly Dictionary<char, (char First, char Second)> SplitJong = new()
    {
        {'ㄳ', ('ㄱ','ㅅ')}, {'ㄵ', ('ㄴ','ㅈ')}, {'ㄶ', ('ㄴ','ㅎ')},
        {'ㄺ', ('ㄹ','ㄱ')}, {'ㄻ', ('ㄹ','ㅁ')}, {'ㄼ', ('ㄹ','ㅂ')},
        {'ㄽ', ('ㄹ','ㅅ')}, {'ㄾ', ('ㄹ','ㅌ')}, {'ㄿ', ('ㄹ','ㅍ')},
        {'ㅀ', ('ㄹ','ㅎ')}, {'ㅄ', ('ㅂ','ㅅ')},
    };

    // ── 인덱스 헬퍼 ──────────────────────────────────────────────────────────

    private static int ChoIdx(char c)  => Array.IndexOf(Choseong,  c);
    private static int JungIdx(char c) => Array.IndexOf(Jungseong, c);
    private static int JongIdx(char c) => Array.IndexOf(Jongseong, c);

    private static bool IsVowel(char jamo) => JungIdx(jamo) >= 0;
    private static bool IsConsonant(char jamo) => ChoIdx(jamo) >= 0 || JongIdx(jamo) > 0;

    private static char MakeSyllable(int cho, int jung, int jong) =>
        (char)(0xAC00 + cho * 21 * 28 + jung * 28 + jong);

    // ── 한글 조합기 (두벌식 상태 기계) ───────────────────────────────────────

    private sealed class HangulComposer
    {
        private int _cho  = -1;
        private int _jung = -1;
        private int _jong = -1;
        private readonly StringBuilder _sb = new();

        public string Result => _sb.ToString();

        public void Flush()
        {
            if (_cho < 0) return;
            if (_jung < 0)
            {
                // 초성만
                _sb.Append(Choseong[_cho]);
            }
            else
            {
                _sb.Append(MakeSyllable(_cho, _jung, _jong < 0 ? 0 : _jong));
            }
            _cho = _jung = _jong = -1;
        }

        public void Feed(char jamo)
        {
            if (IsVowel(jamo))
            {
                FeedVowel(jamo);
            }
            else
            {
                FeedConsonant(jamo);
            }
        }

        private void FeedVowel(char v)
        {
            var vi = JungIdx(v);

            if (_cho < 0)
            {
                // 초성 없음 → ㅇ + 모음
                _sb.Append(MakeSyllable(ChoIdx('ㅇ'), vi, 0));
                return;
            }

            if (_jung < 0)
            {
                // 초성만 있음 → 중성 결합
                _jung = vi;
                return;
            }

            // 초성+중성 있음
            if (_jong < 0)
            {
                // 복합 모음 시도
                var curVowel = Jungseong[_jung];
                if (CompoundVowel.TryGetValue((curVowel, v), out var compound))
                {
                    _jung = JungIdx(compound);
                }
                else
                {
                    // 현재 음절 확정, 새 음절 시작 (ㅇ + 모음)
                    Flush();
                    _cho  = ChoIdx('ㅇ');
                    _jung = vi;
                }
                return;
            }

            // 초성+중성+종성 있음 → 종성을 새 음절의 초성으로
            var jongChar = Jongseong[_jong];

            if (SplitJong.TryGetValue(jongChar, out var split))
            {
                // 복합 종성: 앞 자음은 종성, 뒷 자음은 새 초성
                var newCho = ChoIdx(split.Second);
                if (newCho < 0) newCho = 0;
                var remainJong = JongIdx(split.First);
                // 현재 음절 (jong=split.First)
                _sb.Append(MakeSyllable(_cho, _jung, remainJong));
                _cho  = newCho;
                _jung = vi;
                _jong = -1;
            }
            else
            {
                // 단일 종성 → 새 초성으로
                var newCho = ChoIdx(jongChar);
                if (newCho < 0) newCho = 0;
                _sb.Append(MakeSyllable(_cho, _jung, 0));
                _cho  = newCho;
                _jung = vi;
                _jong = -1;
            }
        }

        private void FeedConsonant(char c)
        {
            var ci = ChoIdx(c);

            if (_cho < 0)
            {
                // 처음 자음
                _cho = ci >= 0 ? ci : 0;
                return;
            }

            if (_jung < 0)
            {
                // 초성만 있음 → 이전 초성 출력, 새 초성
                _sb.Append(Choseong[_cho]);
                _cho  = ci >= 0 ? ci : 0;
                _jung = -1;
                _jong = -1;
                return;
            }

            if (_jong < 0)
            {
                // 초성+중성 → 종성 후보
                _jong = JongIdx(c);
                if (_jong < 0) _jong = 0; // 종성에 없는 자음은 그냥 처리
                if (_jong == 0)
                {
                    // 종성에 들어갈 수 없는 자음(ㄸ, ㅃ, ㅉ)
                    Flush();
                    _cho  = ci >= 0 ? ci : 0;
                    _jung = -1;
                    _jong = -1;
                }
                return;
            }

            // 초성+중성+종성 있음
            var curJongChar = Jongseong[_jong];
            if (CompoundJong.TryGetValue((curJongChar, c), out var compJ))
            {
                // 복합 종성 가능
                var cji = JongIdx(compJ);
                if (cji > 0)
                {
                    _jong = cji;
                    return;
                }
            }

            // 복합 종성 불가 → 현재 음절 확정, 새 초성
            Flush();
            _cho  = ci >= 0 ? ci : 0;
            _jung = -1;
            _jong = -1;
        }
    }

    // ── 영타→한글 변환 ────────────────────────────────────────────────────────

    private static string EngToKorean(string input)
    {
        var composer = new HangulComposer();
        var sb       = new StringBuilder();

        foreach (var ch in input)
        {
            if (EngToJamo.TryGetValue(ch, out var jamo))
            {
                if (composer.Result.Length > 0 || jamo != '\0')
                {
                    // 현재 변환 중인 조합기에 피드
                }
                composer.Feed(jamo);
            }
            else
            {
                // 변환 불가 문자 → 조합기 플러시 후 그대로
                var cur = composer.Result;
                // 지금까지 쌓인 결과 덤프
                composer.Feed('\0'); // 플러시 트리거 안 됨 → 직접 Flush
                // 아래 로직: 조합기는 Flush()로만 비워짐
                sb.Append(ch);
            }
        }

        // 위 로직을 단순화: 문자별 처리
        return ConvertEngToKor(input);
    }

    private static string ConvertEngToKor(string input)
    {
        // 먼저 자모 문자열로 변환
        var jamoSeq = new List<char>();
        foreach (var ch in input)
        {
            if (EngToJamo.TryGetValue(ch, out var jamo))
                jamoSeq.Add(jamo);
            else
                jamoSeq.Add(ch); // 변환 불가 문자는 그대로
        }

        // 자모 시퀀스를 한글 음절로 조합
        var result = new StringBuilder();
        var i = 0;
        while (i < jamoSeq.Count)
        {
            var ch = jamoSeq[i];

            // 변환 불가 문자 (공백, 숫자, 특수문자 등)
            if (!IsKorJamo(ch))
            {
                result.Append(ch);
                i++;
                continue;
            }

            // 자모 덩어리 추출
            var jamoBlock = new List<char>();
            var j = i;
            while (j < jamoSeq.Count && IsKorJamo(jamoSeq[j]))
                jamoBlock.Add(jamoSeq[j++]);

            // 자모 블록을 한글로 조합
            result.Append(ComposeHangul(jamoBlock));
            i = j;
        }

        return result.ToString();
    }

    private static bool IsKorJamo(char c)
    {
        return (c >= 'ㄱ' && c <= 'ㅎ') || (c >= 'ㅏ' && c <= 'ㅣ');
    }

    private static string ComposeHangul(List<char> jamos)
    {
        var sb  = new StringBuilder();
        var idx = 0;

        while (idx < jamos.Count)
        {
            var c = jamos[idx];

            if (IsVowel(c))
            {
                // 단독 모음 → ㅇ + 모음
                sb.Append(MakeSyllable(ChoIdx('ㅇ'), JungIdx(c), 0));
                idx++;
                continue;
            }

            // 자음: 초성 후보
            var cho = c;
            var choI = ChoIdx(cho);
            if (choI < 0) { sb.Append(c); idx++; continue; }
            idx++;

            if (idx >= jamos.Count || IsConsonantOnly(jamos[idx]))
            {
                // 단독 초성
                sb.Append(cho);
                continue;
            }

            // 중성
            var v1    = jamos[idx];
            var jungI = JungIdx(v1);
            if (jungI < 0) { sb.Append(cho); continue; }
            idx++;

            // 복합 모음 시도
            if (idx < jamos.Count && IsVowel(jamos[idx]))
            {
                if (CompoundVowel.TryGetValue((v1, jamos[idx]), out var cv))
                {
                    jungI = JungIdx(cv);
                    idx++;
                }
            }

            // 종성 후보
            if (idx >= jamos.Count)
            {
                sb.Append(MakeSyllable(choI, jungI, 0));
                continue;
            }

            var next = jamos[idx];
            if (IsVowel(next))
            {
                sb.Append(MakeSyllable(choI, jungI, 0));
                continue;
            }

            // 종성 자음
            var jongI = JongIdx(next);
            if (jongI <= 0)
            {
                sb.Append(MakeSyllable(choI, jungI, 0));
                continue;
            }

            idx++;

            // 다음 모음 있으면 종성→초성 이동
            if (idx < jamos.Count && IsVowel(jamos[idx]))
            {
                // 복합 종성 분리 확인
                if (idx + 1 < jamos.Count && IsVowel(jamos[idx]))
                {
                    // 분리 없음: next 자음 → 다음 음절 초성
                }
                sb.Append(MakeSyllable(choI, jungI, 0));
                idx--; // next 자음을 다음 루프에서 초성으로 사용
                continue;
            }

            // 복합 종성 시도
            if (idx < jamos.Count && !IsVowel(jamos[idx]))
            {
                var next2 = jamos[idx];
                var jongChar = Jongseong[jongI];
                if (CompoundJong.TryGetValue((jongChar, next2), out var cj))
                {
                    var cji = JongIdx(cj);
                    if (cji > 0)
                    {
                        // 다음에 모음이 있으면 복합 종성 분리
                        if (idx + 1 < jamos.Count && IsVowel(jamos[idx + 1]))
                        {
                            sb.Append(MakeSyllable(choI, jungI, jongI));
                            // next2는 다음 음절 초성으로
                            idx--; // next2를 다시 처리
                            idx++;
                            continue;
                        }
                        jongI = cji;
                        idx++;
                    }
                }

                // 다음에 모음이 있으면 종성→초성
                if (idx < jamos.Count && IsVowel(jamos[idx]))
                {
                    sb.Append(MakeSyllable(choI, jungI, 0));
                    idx -= 2;
                    idx++;
                    continue;
                }
            }

            sb.Append(MakeSyllable(choI, jungI, jongI));
        }

        return sb.ToString();
    }

    // 초성으로만 사용 가능한 자음인지 (된소리 = 종성 불가)
    private static bool IsConsonantOnly(char c)
    {
        if (!IsKorJamo(c)) return false;
        if (IsVowel(c))    return false;
        return JongIdx(c) <= 0;
    }

    // ── GetItemsAsync ─────────────────────────────────────────────────────────

    public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        var q     = query.Trim();
        var items = new List<LauncherItem>();

        string inputText;

        if (string.IsNullOrWhiteSpace(q))
        {
            // 클립보드에서 읽기
            string? clipText = null;
            try
            {
                Application.Current.Dispatcher.Invoke(() =>
                {
                    if (Clipboard.ContainsText())
                        clipText = Clipboard.GetText();
                });
            }
            catch { }

            if (string.IsNullOrWhiteSpace(clipText))
            {
                items.Add(new LauncherItem("한/영 타이핑 교정",
                    "fix <영타 텍스트>  또는  클립보드에 텍스트 복사 후 fix 입력",
                    null, null, Symbol: "\uE8AC"));
                items.Add(new LauncherItem("예: fix gksrmf", "→ 안녕 (영타→한글)", null, null, Symbol: "\uE8AC"));
                return Task.FromResult<IEnumerable<LauncherItem>>(items);
            }

            inputText = clipText;
        }
        else
        {
            inputText = q;
        }

        var converted = ConvertEngToKor(inputText);

        if (converted == inputText)
        {
            items.Add(new LauncherItem("변환할 영타 오류가 없습니다",
                $"입력: {inputText}", null, null, Symbol: "\uE8AC"));
        }
        else
        {
            items.Add(new LauncherItem(
                converted,
                $"영타 교정 결과  ·  Enter: 클립보드 복사",
                null, ("copy", converted), Symbol: "\uE8AC"));
            items.Add(new LauncherItem(
                $"원본: {(inputText.Length > 50 ? inputText[..50] + "…" : inputText)}",
                "", null, null, Symbol: "\uE8AC"));
        }

        return Task.FromResult<IEnumerable<LauncherItem>>(items);
    }

    // ── ExecuteAsync ──────────────────────────────────────────────────────────

    public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.Data is ("copy", string text))
        {
            try
            {
                Application.Current.Dispatcher.Invoke(() => Clipboard.SetText(text));
                NotificationService.Notify("타이핑 교정", "교정 결과를 클립보드에 복사했습니다.");
            }
            catch { }
        }
        return Task.CompletedTask;
    }
}
