# -*- coding: utf-8 -*-
"""Step 3: 패턴 매칭 + 정제 + 기존 등재 여부 확인

입력:
  - output/{corpus_type}/step1_patterns.csv
  - output/{corpus_type}/step2_eojeol_type_freq.csv
  - resource/우리말샘 XLS 디렉토리
  - resource/wordlist.xlsx
출력:
  - output/{corpus_type}/step3_candidates.csv

정제 규칙:
  1) 혼성어 패턴 포함 어절 매칭
  2) 한글 음절 외 문자 포함 어절 삭제
  3) 우리말샘 표제어 포함 어절 삭제
  4) 기등재 혼성어 포함 어절 삭제
  5) Kiwi 형태소 분석
  6) 기존 등재 여부 태깅 (우리말샘 / wordlist)
"""

from __future__ import annotations

import pandas as pd
import polars as pl
from ahocorasick_rs import AhoCorasick
from kiwipiepy import Kiwi
from tqdm.auto import tqdm

from wordextractor.config import PipelineConfig
from wordextractor.utils.korean import KO_PATTERN, ONLY_KO_PATTERN


# ── 1) 패턴 매칭 ─────────────────────────────────────


def match_patterns(
    word_freq: pl.DataFrame,
    patterns: list[str],
) -> pl.DataFrame:
    ac = AhoCorasick(patterns)
    print(f"  AC 오토마톤 구축 완료 (패턴 {len(patterns):,}개)")

    words = word_freq["어절"].to_list()
    freqs = word_freq["빈도"].to_list()

    rows = []
    for word, freq in zip(words, freqs):
        hits = ac.find_matches_as_indexes(word)
        if hits:
            matched = sorted(set(patterns[pat_idx] for pat_idx, _s, _e in hits))
            rows.append((word, freq, "; ".join(matched)))

    return pl.DataFrame(
        {
            "어절": [r[0] for r in rows],
            "빈도": [r[1] for r in rows],
            "매칭 패턴": [r[2] for r in rows],
        },
        schema={"어절": pl.Utf8, "빈도": pl.Int64, "매칭 패턴": pl.Utf8},
    ).sort("빈도", descending=True)


# ── 2) 한글 음절 외 문자 포함 어절 삭제 ──────────────


def filter_korean_only(df: pl.DataFrame) -> pl.DataFrame:
    words = df["어절"].to_list()
    mask = [bool(ONLY_KO_PATTERN.match(w)) for w in words]
    return df.filter(pl.Series(mask))


# ── 3) 우리말샘 표제어 포함 어절 삭제 ────────────────


def load_urimalsame_headwords(urimalsame_dir) -> set[str]:
    xls_files = sorted(urimalsame_dir.glob("*.xls"))
    print(f"  우리말샘 XLS 파일: {len(xls_files)}개")

    headwords: set[str] = set()
    for fp in tqdm(xls_files, desc="우리말샘 로드"):
        raw_df = pd.read_excel(fp, usecols=["어휘"], engine="xlrd")
        for raw in raw_df["어휘"].dropna().astype(str):
            clean = "".join(KO_PATTERN.findall(raw))
            if len(clean) >= 2:
                headwords.add(clean)

    print(f"  고유 표제어(2음절+): {len(headwords):,}개")
    return headwords


def filter_by_urimalsame(df: pl.DataFrame, headwords: set[str]) -> pl.DataFrame:
    hw_list = sorted(headwords)
    ac = AhoCorasick(hw_list)
    print(f"  AC 오토마톤 구축 완료 (패턴 {len(hw_list):,}개)")

    words = df["어절"].to_list()
    mask = []
    for w in tqdm(words, desc="우리말샘 필터링"):
        hits = ac.find_matches_as_indexes(w)
        mask.append(len(hits) == 0)

    return df.filter(pl.Series(mask))


# ── 4) 기등재 혼성어 목록 포함 어절 삭제 ─────────────


def load_registered_blends(wordlist_path, blend_column: str) -> set[str]:
    raw_df = pd.read_excel(wordlist_path)
    blends: set[str] = set()
    for raw in raw_df[blend_column].dropna().astype(str):
        clean = "".join(KO_PATTERN.findall(raw))
        if clean:
            blends.add(clean)
    print(f"  기등재 혼성어: {len(blends):,}개")
    return blends


def filter_by_registered(df: pl.DataFrame, blends: set[str]) -> pl.DataFrame:
    words = df["어절"].to_list()
    mask = [w not in blends for w in words]
    return df.filter(pl.Series(mask))


# ── 5) 기존 등재 여부 태깅 ───────────────────────────


def tag_existing_registration(
    df: pl.DataFrame,
    urimalsame_headwords: set[str],
    registered_blends: set[str],
) -> pl.DataFrame:
    """어절이 우리말샘·wordlist에 등재되어 있는지 태깅"""
    words = df["어절"].to_list()
    tags = []
    for w in words:
        in_urimal = w in urimalsame_headwords
        in_wordlist = w in registered_blends
        if in_urimal and in_wordlist:
            tags.append(f"우리말샘+wordlist")
        elif in_urimal:
            tags.append("우리말샘")
        elif in_wordlist:
            tags.append("wordlist")
        else:
            tags.append("")
    return df.with_columns(pl.Series("기존_등재_여부", tags))


# ── main ──────────────────────────────────────────────


def run(cfg: PipelineConfig) -> None:
    out_dir = cfg.ensure_output_dir()

    print("데이터 로드...")
    patterns_df = pl.read_csv(str(out_dir / "step1_patterns.csv"))
    word_freq = pl.read_csv(str(out_dir / "step2_eojeol_type_freq.csv"))
    print(f"  패턴: {len(patterns_df):,}개")
    print(f"  어절: {len(word_freq):,}개")

    patterns = patterns_df["패턴"].to_list()

    # 1) 패턴 매칭
    print("\n[1] 패턴 매칭...")
    candidates = match_patterns(word_freq, patterns)
    n1 = len(candidates)
    print(f"  매칭 어절: {n1:,}개")

    # 2) 한글 음절 외 문자 포함 삭제 + 3음절 미만 삭제
    print("\n[2] 한글 음절 외 문자 포함 어절 삭제 + 3음절 미만 삭제...")
    candidates = filter_korean_only(candidates)
    candidates = candidates.filter(pl.col("어절").str.len_chars() >= 3)
    n2 = len(candidates)
    print(f"  남은 어절: {n2:,}개 (삭제: {n1 - n2:,}개)")

    # 우리말샘 / wordlist 로드
    urimalsame_headwords: set[str] = set()
    if cfg.urimalsame_dir and cfg.urimalsame_dir.exists():
        print("\n[3] 우리말샘 표제어 로드...")
        urimalsame_headwords = load_urimalsame_headwords(cfg.urimalsame_dir)
        print("우리말샘 포함 어절 삭제...")
        candidates = filter_by_urimalsame(candidates, urimalsame_headwords)
        n3 = len(candidates)
        print(f"  남은 어절: {n3:,}개 (삭제: {n2 - n3:,}개)")
    else:
        n3 = n2
        print("\n[3] 우리말샘 디렉토리 미설정 — 스킵")

    # 4) 기등재 혼성어 삭제
    print("\n[4] 기등재 혼성어 목록 로드...")
    blends = load_registered_blends(cfg.wordlist_path, cfg.blend_column)
    candidates = filter_by_registered(candidates, blends)
    n4 = len(candidates)
    print(f"  남은 어절: {n4:,}개 (삭제: {n3 - n4:,}개)")

    # 5) Kiwi 형태소 분석
    print("\n[5] Kiwi 형태소 분석...")
    kw = Kiwi()
    words_list = candidates["어절"].to_list()
    morphs = []
    notes = []
    for w in tqdm(words_list, desc="형태소 분석"):
        tokens = kw.tokenize(w)
        morph_str = "+".join(f"{t.form}/{t.tag}" for t in tokens)
        last_tag = tokens[-1].tag if tokens else ""
        note = last_tag if last_tag.startswith(("J", "E")) else ""
        morphs.append(morph_str)
        notes.append(note)

    candidates = candidates.with_columns(
        [
            pl.Series("형태소 분석", morphs),
            pl.Series("note", notes),
        ]
    )
    print(f"  완료: {len(candidates):,}개")

    # 6) 기존 등재 여부 태깅
    print("\n[6] 기존 등재 여부 태깅...")
    candidates = tag_existing_registration(candidates, urimalsame_headwords, blends)
    tagged = candidates.filter(pl.col("기존_등재_여부") != "")
    print(f"  기존 등재 어절: {len(tagged):,}개")

    # 저장
    out_path = out_dir / "step3_candidates.csv"
    candidates.to_pandas().to_csv(out_path, index=False, encoding="utf-8-sig")
    print(f"\n결과 저장: {out_path}")

    print(f"\n=== 요약 ===")
    print(f"  [1] 패턴 매칭: {n1:,}개")
    print(f"  [2] 한글 전용: {n2:,}개 (-{n1 - n2:,})")
    print(f"  [3] 우리말샘: {n3:,}개 (-{n2 - n3:,})")
    print(f"  [4] 기등재:   {n4:,}개 (-{n3 - n4:,})")
