"""신살 + 지장간 + 공망 + 지지 상호작용.

saju-web/lib/saju-calculator.ts 의 다음 함수와 1:1 매핑:
- getHiddenStems / getAllHiddenStems   (지장간)
- analyzeBranchInteractions             (육합/삼합/방합/충/형/자형/파/해)
- calculateShinsal                       (역마/도화/화개/천을귀인/문창귀인/천덕귀인)
- calculateGongmang                      (공망)

입력 saju dict는 core.calculate_saju 의 snake_case 출력 (year/month/day/hour pillar
각각 stem, branch, stem_kr, branch_kr, element, ten_god, fortune. day_stem 별도).
출력 키는 snake_case (branch_kr, branches_kr, result_element 등). 단, `pillar` 값은
TS와 동일하게 한글(`년주`/`월주`/`일주`/`시주`)을 사용.
"""
from __future__ import annotations

from typing import Optional

from .constants import (
    HEAVENLY_STEMS,
    HEAVENLY_STEMS_KR,
    EARTHLY_BRANCHES,
    EARTHLY_BRANCHES_KR,
    FIVE_ELEMENTS,
    HIDDEN_STEMS,
)


# ─── 지장간 ────────────────────────────────────────────────────────
def get_hidden_stems(branch: str) -> list[str]:
    """지지의 지장간(숨은 천간) — [본기, 중기, 여기]."""
    return list(HIDDEN_STEMS.get(branch, []))


def get_all_hidden_stems(saju: dict) -> list[dict]:
    """4주 전체의 지장간 정보 반환.

    출력 구조:
    [{
        "pillar": "년주",
        "branch": "午",
        "branch_kr": "오",
        "stems": [
            {"stem": "丁", "stem_kr": "정", "element": "火", "role": "정기(본기)"},
            {"stem": "己", "stem_kr": "기", "element": "土", "role": "중기"},
        ],
    }, ...]
    """
    pillars: list[dict] = [
        {"pillar": "년주", "branch": saju["year"]["branch"], "branch_kr": saju["year"]["branch_kr"]},
        {"pillar": "월주", "branch": saju["month"]["branch"], "branch_kr": saju["month"]["branch_kr"]},
        {"pillar": "일주", "branch": saju["day"]["branch"], "branch_kr": saju["day"]["branch_kr"]},
    ]
    if saju.get("hour") is not None:
        pillars.append({
            "pillar": "시주",
            "branch": saju["hour"]["branch"],
            "branch_kr": saju["hour"]["branch_kr"],
        })

    result: list[dict] = []
    for p in pillars:
        hidden = get_hidden_stems(p["branch"])
        stems = []
        for idx, stem in enumerate(hidden):
            stem_index = HEAVENLY_STEMS.index(stem)
            if idx == 0:
                role = "정기(본기)"
            elif idx == 1:
                role = "중기"
            else:
                role = "여기"
            stems.append({
                "stem": stem,
                "stem_kr": HEAVENLY_STEMS_KR[stem_index],
                "element": FIVE_ELEMENTS[stem],
                "role": role,
            })
        result.append({
            "pillar": p["pillar"],
            "branch": p["branch"],
            "branch_kr": p["branch_kr"],
            "stems": stems,
        })
    return result


# ─── 지지 상호작용 ──────────────────────────────────────────────────
# 육합 (六合): 子丑(土), 寅亥(木), 卯戌(火), 辰酉(金), 巳申(水), 午未(火)
_YUKAP_PAIRS: list[tuple[str, str, str]] = [
    ("子", "丑", "土"), ("寅", "亥", "木"), ("卯", "戌", "火"),
    ("辰", "酉", "金"), ("巳", "申", "水"), ("午", "未", "火"),
]

# 삼합 (三合)
_SAMHAP_GROUPS: list[tuple[str, str, str, str]] = [
    ("申", "子", "辰", "水"), ("亥", "卯", "未", "木"),
    ("寅", "午", "戌", "火"), ("巳", "酉", "丑", "金"),
]

# 방합 (方合)
_BANGHAP_GROUPS: list[tuple[str, str, str, str]] = [
    ("寅", "卯", "辰", "木"), ("巳", "午", "未", "火"),
    ("申", "酉", "戌", "金"), ("亥", "子", "丑", "水"),
]

# 충 (沖)
_CHUNG_PAIRS: list[tuple[str, str]] = [
    ("子", "午"), ("丑", "未"), ("寅", "申"),
    ("卯", "酉"), ("辰", "戌"), ("巳", "亥"),
]

# 형 (刑)
_HYUNG_GROUPS: list[dict] = [
    {"branches": ["寅", "巳", "申"], "name": "무은지형(無恩之刑)"},
    {"branches": ["丑", "戌", "未"], "name": "지세지형(恃勢之刑)"},
    {"branches": ["子", "卯"], "name": "무례지형(無禮之刑)"},
]
_JAHYUNG_BRANCHES = ["辰", "午", "酉", "亥"]

# 파 (破)
_PA_PAIRS: list[tuple[str, str]] = [
    ("子", "酉"), ("丑", "辰"), ("寅", "亥"),
    ("卯", "午"), ("巳", "申"), ("未", "戌"),
]

# 해 (害)
_HAE_PAIRS: list[tuple[str, str]] = [
    ("子", "未"), ("丑", "午"), ("寅", "巳"),
    ("卯", "辰"), ("申", "亥"), ("酉", "戌"),
]

_ELEMENT_NAMES_KR: dict[str, str] = {
    "木": "목", "火": "화", "土": "토", "金": "금", "水": "수",
}


def _collect_pillar_branches(saju: dict) -> list[dict]:
    """4주 지지 리스트 — TS analyzeBranchInteractions 내부 변수와 동일 순서."""
    out: list[dict] = [
        {"branch": saju["year"]["branch"], "pillar": "년주", "branch_kr": saju["year"]["branch_kr"]},
        {"branch": saju["month"]["branch"], "pillar": "월주", "branch_kr": saju["month"]["branch_kr"]},
        {"branch": saju["day"]["branch"], "pillar": "일주", "branch_kr": saju["day"]["branch_kr"]},
    ]
    if saju.get("hour") is not None:
        out.append({
            "branch": saju["hour"]["branch"],
            "pillar": "시주",
            "branch_kr": saju["hour"]["branch_kr"],
        })
    return out


def analyze_branch_interactions(saju: dict) -> list[dict]:
    """지지 상호작용 분석 (육합/삼합/반삼합/방합/충/형/자형/파/해).

    TS analyzeBranchInteractions 와 1:1 동일. branches는 첫 번째 발견 인덱스만 본다
    (중복 지지가 있을 때 TS의 indexOf 동작과 동일).
    """
    interactions: list[dict] = []
    pillar_branches = _collect_pillar_branches(saju)
    branches = [pb["branch"] for pb in pillar_branches]

    # 육합 (六合)
    for a, b, elem in _YUKAP_PAIRS:
        if a in branches and b in branches:
            ia = branches.index(a)
            ib = branches.index(b)
            interactions.append({
                "type": "육합(六合)",
                "branches": [a, b],
                "branches_kr": [pillar_branches[ia]["branch_kr"], pillar_branches[ib]["branch_kr"]],
                "pillars": [pillar_branches[ia]["pillar"], pillar_branches[ib]["pillar"]],
                "description": (
                    f"{pillar_branches[ia]['branch_kr']}{pillar_branches[ib]['branch_kr']} 육합 "
                    f"→ {_ELEMENT_NAMES_KR[elem]}({elem}) 기운 생성. 조화와 화합의 관계."
                ),
                "result_element": elem,
            })

    # 삼합 (三合) / 반삼합 (半三合)
    for a, b, c, elem in _SAMHAP_GROUPS:
        found = [x for x in (a, b, c) if x in branches]
        if len(found) >= 2:
            found_pillars = [pillar_branches[branches.index(x)] for x in found]
            is_complete = len(found) == 3
            type_kr = "삼합(三合)" if is_complete else "반삼합(半三合)"
            kr_str = "".join(p["branch_kr"] for p in found_pillars)
            samhap_label = "삼합" if is_complete else "반삼합"
            tail = "강력한 합의 기운." if is_complete else "삼합의 기운이 부분적으로 작용."
            interactions.append({
                "type": type_kr,
                "branches": found,
                "branches_kr": [p["branch_kr"] for p in found_pillars],
                "pillars": [p["pillar"] for p in found_pillars],
                "description": (
                    f"{kr_str} {samhap_label} → {_ELEMENT_NAMES_KR[elem]}({elem})국. {tail}"
                ),
                "result_element": elem,
            })

    # 방합 (方合)
    for a, b, c, elem in _BANGHAP_GROUPS:
        found = [x for x in (a, b, c) if x in branches]
        if len(found) == 3:
            found_pillars = [pillar_branches[branches.index(x)] for x in found]
            kr_str = "".join(p["branch_kr"] for p in found_pillars)
            interactions.append({
                "type": "방합(方合)",
                "branches": found,
                "branches_kr": [p["branch_kr"] for p in found_pillars],
                "pillars": [p["pillar"] for p in found_pillars],
                "description": (
                    f"{kr_str} 방합 → {_ELEMENT_NAMES_KR[elem]}({elem}) 방국. 매우 강한 오행 기운."
                ),
                "result_element": elem,
            })

    # 충 (沖)
    for a, b in _CHUNG_PAIRS:
        if a in branches and b in branches:
            ia = branches.index(a)
            ib = branches.index(b)
            interactions.append({
                "type": "충(沖)",
                "branches": [a, b],
                "branches_kr": [pillar_branches[ia]["branch_kr"], pillar_branches[ib]["branch_kr"]],
                "pillars": [pillar_branches[ia]["pillar"], pillar_branches[ib]["pillar"]],
                "description": (
                    f"{pillar_branches[ia]['branch_kr']}{pillar_branches[ib]['branch_kr']} 충 "
                    f"→ 변동, 갈등, 변화의 에너지. "
                    f"{pillar_branches[ia]['pillar']}와 {pillar_branches[ib]['pillar']} 사이의 긴장 관계."
                ),
            })

    # 형 (刑)
    for group in _HYUNG_GROUPS:
        found = [x for x in group["branches"] if x in branches]
        if len(found) >= 2:
            found_pillars = [pillar_branches[branches.index(x)] for x in found]
            kr_str = "".join(p["branch_kr"] for p in found_pillars)
            interactions.append({
                "type": "형(刑)",
                "branches": found,
                "branches_kr": [p["branch_kr"] for p in found_pillars],
                "pillars": [p["pillar"] for p in found_pillars],
                "description": f"{kr_str} {group['name']} → 시련과 갈등의 기운. 주의가 필요한 관계.",
            })

    # 자형 (自刑)
    for jb in _JAHYUNG_BRANCHES:
        count = branches.count(jb)
        if count >= 2:
            br_kr = EARTHLY_BRANCHES_KR[EARTHLY_BRANCHES.index(jb)]
            interactions.append({
                "type": "자형(自刑)",
                "branches": [jb, jb],
                "branches_kr": [br_kr, br_kr],
                "pillars": [p["pillar"] for p in pillar_branches if p["branch"] == jb],
                "description": f"{br_kr}{br_kr} 자형 → 자기 자신과의 갈등, 내면의 갈등 기운.",
            })

    # 파 (破)
    for a, b in _PA_PAIRS:
        if a in branches and b in branches:
            ia = branches.index(a)
            ib = branches.index(b)
            interactions.append({
                "type": "파(破)",
                "branches": [a, b],
                "branches_kr": [pillar_branches[ia]["branch_kr"], pillar_branches[ib]["branch_kr"]],
                "pillars": [pillar_branches[ia]["pillar"], pillar_branches[ib]["pillar"]],
                "description": (
                    f"{pillar_branches[ia]['branch_kr']}{pillar_branches[ib]['branch_kr']} 파 "
                    f"→ 관계의 균열, 계획의 차질 가능성."
                ),
            })

    # 해 (害)
    for a, b in _HAE_PAIRS:
        if a in branches and b in branches:
            ia = branches.index(a)
            ib = branches.index(b)
            interactions.append({
                "type": "해(害)",
                "branches": [a, b],
                "branches_kr": [pillar_branches[ia]["branch_kr"], pillar_branches[ib]["branch_kr"]],
                "pillars": [pillar_branches[ia]["pillar"], pillar_branches[ib]["pillar"]],
                "description": (
                    f"{pillar_branches[ia]['branch_kr']}{pillar_branches[ib]['branch_kr']} 해 "
                    f"→ 은근한 방해, 원망의 기운."
                ),
            })

    return interactions


# ─── 신살 ──────────────────────────────────────────────────────────
# 일지 삼합국 기준 신살 매핑
_SAMHAP_GROUP_MAP: dict[str, str] = {
    "申": "申子辰", "子": "申子辰", "辰": "申子辰",
    "寅": "寅午戌", "午": "寅午戌", "戌": "寅午戌",
    "巳": "巳酉丑", "酉": "巳酉丑", "丑": "巳酉丑",
    "亥": "亥卯未", "卯": "亥卯未", "未": "亥卯未",
}
_YEOKMA_MAP: dict[str, str] = {
    "申子辰": "寅", "寅午戌": "申", "巳酉丑": "亥", "亥卯未": "巳",
}
_DOHWA_MAP: dict[str, str] = {
    "申子辰": "酉", "寅午戌": "卯", "巳酉丑": "午", "亥卯未": "子",
}
_HWAGAE_MAP: dict[str, str] = {
    "申子辰": "辰", "寅午戌": "戌", "巳酉丑": "丑", "亥卯未": "未",
}

# 천을귀인 (天乙貴人) — 일간 기준
_CHEONUL_MAP: dict[str, list[str]] = {
    "甲": ["丑", "未"], "乙": ["子", "申"], "丙": ["亥", "酉"], "丁": ["亥", "酉"],
    "戊": ["丑", "未"], "己": ["子", "申"], "庚": ["丑", "未"], "辛": ["寅", "午"],
    "壬": ["卯", "巳"], "癸": ["卯", "巳"],
}

# 문창귀인 (文昌貴人) — 일간 기준
_MUNCHANG_MAP: dict[str, str] = {
    "甲": "巳", "乙": "午", "丙": "申", "丁": "酉",
    "戊": "申", "己": "酉", "庚": "亥", "辛": "子",
    "壬": "寅", "癸": "卯",
}

# 천덕귀인 (天德貴人) — 월지 기준 천간
_CHEONDUK_MAP: dict[str, str] = {
    "寅": "丁", "卯": "申", "辰": "壬", "巳": "辛",
    "午": "亥", "未": "甲", "申": "癸", "酉": "寅",
    "戌": "丙", "亥": "乙", "子": "巳", "丑": "庚",
}


def calculate_shinsal(saju: dict) -> list[dict]:
    """신살 계산 (역마/도화/화개/천을귀인/문창귀인/천덕귀인).

    TS calculateShinsal 와 1:1 동일.
    """
    result: list[dict] = []
    day_branch = saju["day"]["branch"]
    day_stem = saju["day_stem"]
    month_branch = saju["month"]["branch"]

    pillar_branches: list[dict] = [
        {"branch": saju["year"]["branch"], "branch_kr": saju["year"]["branch_kr"], "pillar": "년주"},
        {"branch": saju["month"]["branch"], "branch_kr": saju["month"]["branch_kr"], "pillar": "월주"},
        {"branch": saju["day"]["branch"], "branch_kr": saju["day"]["branch_kr"], "pillar": "일주"},
    ]
    if saju.get("hour") is not None:
        pillar_branches.append({
            "branch": saju["hour"]["branch"],
            "branch_kr": saju["hour"]["branch_kr"],
            "pillar": "시주",
        })

    group = _SAMHAP_GROUP_MAP.get(day_branch)

    if group:
        # 역마살
        yeokma = _YEOKMA_MAP[group]
        for pb in pillar_branches:
            if pb["branch"] == yeokma and pb["pillar"] != "일주":
                result.append({
                    "name": "역마살",
                    "name_hanja": "驛馬殺",
                    "branch": yeokma,
                    "branch_kr": pb["branch_kr"],
                    "pillar": pb["pillar"],
                    "description": "이동, 변동, 해외, 출장이 많은 기운. 활동적이고 한 곳에 머물지 못하는 성향.",
                })

        # 도화살
        dohwa = _DOHWA_MAP[group]
        for pb in pillar_branches:
            if pb["branch"] == dohwa and pb["pillar"] != "일주":
                result.append({
                    "name": "도화살",
                    "name_hanja": "桃花殺",
                    "branch": dohwa,
                    "branch_kr": pb["branch_kr"],
                    "pillar": pb["pillar"],
                    "description": "매력, 인기, 예술적 감각. 이성에게 끌리는 기운이 강하며 대인관계가 화려함.",
                })

        # 화개살
        hwagae = _HWAGAE_MAP[group]
        for pb in pillar_branches:
            if pb["branch"] == hwagae and pb["pillar"] != "일주":
                result.append({
                    "name": "화개살",
                    "name_hanja": "華蓋殺",
                    "branch": hwagae,
                    "branch_kr": pb["branch_kr"],
                    "pillar": pb["pillar"],
                    "description": "학문, 종교, 예술에 심취하는 기운. 고독을 즐기며 정신적 세계에 몰두하는 성향.",
                })

    # 천을귀인 — 일간 기준
    cheonul_branches = _CHEONUL_MAP.get(day_stem, [])
    for pb in pillar_branches:
        if pb["branch"] in cheonul_branches and pb["pillar"] != "일주":
            result.append({
                "name": "천을귀인",
                "name_hanja": "天乙貴人",
                "branch": pb["branch"],
                "branch_kr": pb["branch_kr"],
                "pillar": pb["pillar"],
                "description": "위기에서 귀인의 도움을 받는 길한 기운. 어려울 때 도움을 주는 사람이 나타남.",
            })

    # 문창귀인 — 일간 기준
    munchang_branch = _MUNCHANG_MAP.get(day_stem)
    if munchang_branch:
        for pb in pillar_branches:
            if pb["branch"] == munchang_branch and pb["pillar"] != "일주":
                result.append({
                    "name": "문창귀인",
                    "name_hanja": "文昌貴人",
                    "branch": pb["branch"],
                    "branch_kr": pb["branch_kr"],
                    "pillar": pb["pillar"],
                    "description": "학문, 시험, 문서에 유리한 기운. 공부를 잘하며 시험운이 좋음.",
                })

    # 천덕귀인 — 월지 기준 천간이 4주 천간에 있으면
    cheonduk_stem = _CHEONDUK_MAP.get(month_branch)
    if cheonduk_stem:
        all_stems: list[dict] = [
            {"stem": saju["year"]["stem"], "pillar": "년주"},
            {"stem": saju["day"]["stem"], "pillar": "일주"},
        ]
        if saju.get("hour") is not None:
            all_stems.append({"stem": saju["hour"]["stem"], "pillar": "시주"})
        for ps in all_stems:
            if ps["stem"] == cheonduk_stem:
                br_kr = EARTHLY_BRANCHES_KR[EARTHLY_BRANCHES.index(month_branch)]
                result.append({
                    "name": "천덕귀인",
                    "name_hanja": "天德貴人",
                    "branch": month_branch,
                    "branch_kr": br_kr,
                    "pillar": ps["pillar"],
                    "description": "하늘의 덕을 받는 기운. 재난을 피하고 복을 받는 길신 중의 길신.",
                })

    return result


# ─── 공망 ──────────────────────────────────────────────────────────
def calculate_gongmang(day_stem: str, day_branch: str) -> dict:
    """공망(空亡) — 일주의 旬(60갑자 10단위)에서 빠지는 2개 지지.

    TS calculateGongmang 와 1:1 동일.
    """
    stem_idx = HEAVENLY_STEMS.index(day_stem)
    branch_idx = EARTHLY_BRANCHES.index(day_branch)

    # 60갑자에서 해당 旬의 시작점 = 천간이 甲인 지점
    # 旬의 시작 지지 인덱스 = (branchIdx - stemIdx + 120) % 12
    start_branch_idx = (branch_idx - stem_idx + 120) % 12

    # 공망 = 旬 시작 + 10, +11
    gongmang1_idx = (start_branch_idx + 10) % 12
    gongmang2_idx = (start_branch_idx + 11) % 12

    branch1 = EARTHLY_BRANCHES[gongmang1_idx]
    branch2 = EARTHLY_BRANCHES[gongmang2_idx]
    branch_kr1 = EARTHLY_BRANCHES_KR[gongmang1_idx]
    branch_kr2 = EARTHLY_BRANCHES_KR[gongmang2_idx]

    return {
        "branches": [branch1, branch2],
        "branches_kr": [branch_kr1, branch_kr2],
        "description": (
            f"{branch_kr1}({branch1})·{branch_kr2}({branch2}) 공망 "
            f"→ 해당 지지의 기운이 비어있어 허무하거나 집착이 없는 영역. "
            f"오히려 초월적 능력이 될 수 있음."
        ),
    }
