"""사주 핵심 계산 — 60갑자 + 십성 + 십이운성 + calculate_saju.

saju-web/lib/saju-calculator.ts 의 알고리즘과 1:1 매핑.

핵심 결정 사항:
- 년주: 1900-01-01 = 庚子년(stem=6, branch=0) 기준 단순 차분 (TS 동일)
- 월주: 月支 = 절기 기반 (sxtwl), 月干 = (yearStemIndex * 2 + branchIndex) % 10 (TS 동일)
- 일주: 1900-01-01 = 丙寅일(stem=2, branch=2) 기준 그레고리안 일수 차분 (TS 동일, sxtwl 미사용)
- 시주: branchIndex = hour-to-branch 매핑, stemIndex = (dayStemIndex * 2 + branchIndex) % 10
- 십성: 일간/대상 오행 관계 + isYang 플래그 (호출자가 음양 동일 여부 결정)
- 십이운성: 일간×지지 12 갑자 하드코딩 매핑 (건록, '임관' 아님)
"""
from __future__ import annotations

from datetime import date as _date
from typing import Optional

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


# ─── 기준일 (TS saju-calculator.ts와 동일) ────────────────────────────
BASE_YEAR = 1900
BASE_YEAR_STEM = 6   # 庚
BASE_YEAR_BRANCH = 0  # 子
BASE_DAY_STEM = 2    # 丙
BASE_DAY_BRANCH = 2  # 寅
_BASE_DATE = _date(1900, 1, 1)


# ─── 십이운성 ─────────────────────────────────────────────────────
TWELVE_FORTUNES = [
    "장생", "목욕", "관대", "건록", "제왕", "쇠",
    "병", "사", "묘", "절", "태", "양",
]


# 일간 × 지지 → TWELVE_FORTUNES 인덱스
# saju-web TS getTwelveFortune() 함수의 hardcoded map과 1:1 일치
_FORTUNE_MAP: dict[str, dict[str, int]] = {
    "甲": {"亥": 11, "子": 0, "丑": 1, "寅": 2, "卯": 3, "辰": 4, "巳": 5, "午": 6, "未": 7, "申": 8, "酉": 9, "戌": 10},
    "乙": {"午": 11, "未": 0, "申": 1, "酉": 2, "戌": 3, "亥": 4, "子": 5, "丑": 6, "寅": 7, "卯": 8, "辰": 9, "巳": 10},
    "丙": {"寅": 11, "卯": 0, "辰": 1, "巳": 2, "午": 3, "未": 4, "申": 5, "酉": 6, "戌": 7, "亥": 8, "子": 9, "丑": 10},
    "丁": {"酉": 11, "戌": 0, "亥": 1, "子": 2, "丑": 3, "寅": 4, "卯": 5, "辰": 6, "巳": 7, "午": 8, "未": 9, "申": 10},
    "戊": {"寅": 11, "卯": 0, "辰": 1, "巳": 2, "午": 3, "未": 4, "申": 5, "酉": 6, "戌": 7, "亥": 8, "子": 9, "丑": 10},
    "己": {"酉": 11, "戌": 0, "亥": 1, "子": 2, "丑": 3, "寅": 4, "卯": 5, "辰": 6, "巳": 7, "午": 8, "未": 9, "申": 10},
    "庚": {"巳": 11, "午": 0, "未": 1, "申": 2, "酉": 3, "戌": 4, "亥": 5, "子": 6, "丑": 7, "寅": 8, "卯": 9, "辰": 10},
    "辛": {"子": 11, "丑": 0, "寅": 1, "卯": 2, "辰": 3, "巳": 4, "午": 5, "未": 6, "申": 7, "酉": 8, "戌": 9, "亥": 10},
    "壬": {"申": 11, "酉": 0, "戌": 1, "亥": 2, "子": 3, "丑": 4, "寅": 5, "卯": 6, "辰": 7, "巳": 8, "午": 9, "未": 10},
    "癸": {"卯": 11, "辰": 0, "巳": 1, "午": 2, "未": 3, "申": 4, "酉": 5, "戌": 6, "亥": 7, "子": 8, "丑": 9, "寅": 10},
}


# ─── 오행 상생/상극 (TS getTenGod 내부 매핑과 동일) ───────────────────
_PRODUCE_MAP = {"木": "火", "火": "土", "土": "金", "金": "水", "水": "木"}
_OVERCOME_MAP = {"木": "土", "火": "金", "土": "水", "金": "木", "水": "火"}


# ─── 헬퍼 ─────────────────────────────────────────────────────────
def _make_pillar(stem_idx: int, branch_idx: int) -> dict:
    """천간/지지 인덱스 → pillar 기본 필드 (element 포함)."""
    stem = HEAVENLY_STEMS[stem_idx]
    branch = EARTHLY_BRANCHES[branch_idx]
    return {
        "stem": stem,
        "branch": branch,
        "stem_kr": HEAVENLY_STEMS_KR[stem_idx],
        "branch_kr": EARTHLY_BRANCHES_KR[branch_idx],
        "element": FIVE_ELEMENTS[stem],
    }


# ─── 60갑자 ───────────────────────────────────────────────────────
def get_year_ganzi(year: int) -> dict:
    """년주 — 1900년 庚子 기준 단순 차분 (TS getYearGanzi 동일)."""
    year_diff = year - BASE_YEAR
    stem_idx = (BASE_YEAR_STEM + year_diff) % 10
    branch_idx = (BASE_YEAR_BRANCH + year_diff) % 12
    # 음수 보정
    if stem_idx < 0:
        stem_idx += 10
    if branch_idx < 0:
        branch_idx += 12
    return _make_pillar(stem_idx, branch_idx)


def get_month_ganzi(year: int, month: int, day: int) -> dict:
    """월주 — 月支 = 절기 기반, 月干 = (yearStemIdx*2 + branchIdx) % 10.

    TS getMonthGanzi와 동일.
    """
    branch_idx = get_solar_term_month_branch(year, month, day)
    year_stem = get_year_ganzi(year)["stem"]
    year_stem_idx = HEAVENLY_STEMS.index(year_stem)
    stem_idx = (year_stem_idx * 2 + branch_idx) % 10
    return _make_pillar(stem_idx, branch_idx)


def get_day_ganzi(year: int, month: int, day: int) -> dict:
    """일주 — 1900-01-01(丙寅) 기준 그레고리안 일수 차분 (TS getDayGanzi 동일).

    sxtwl을 사용하지 않음 — TS와 1:1 일치를 위해 단순 일수 차분.

    ⚠️ TS는 JavaScript `new Date(1900, 0, 1).getTime()`을 사용하는데, Asia/Seoul
       timezone에서 1900-01-01 자정은 LMT(+8:27:52) 보정 때문에 UTC 기준으로
       1899-12-31T15:32:08Z에 해당. 반면 후대 날짜는 KST(+9:00) 기준이라
       Math.floor((target - base) / DAY)는 정확한 일수보다 1 작게 계산됨
       (모든 케이스가 균일하게 -1). 본 함수는 그 결과를 재현하기 위해 days_diff -= 1.
    """
    target = _date(year, month, day)
    days_diff = (target - _BASE_DATE).days - 1  # TS Asia/Seoul LMT artifact 보정
    stem_idx = (BASE_DAY_STEM + days_diff) % 10
    branch_idx = (BASE_DAY_BRANCH + days_diff) % 12
    if stem_idx < 0:
        stem_idx += 10
    if branch_idx < 0:
        branch_idx += 12
    return _make_pillar(stem_idx, branch_idx)


def _hour_to_branch_idx(hour: int) -> int:
    """시간 → 시지 인덱스 (TS getHourGanzi 내부 분기 동일).

    23~01 = 子(0), 01~03 = 丑(1), 03~05 = 寅(2), ..., 21~23 = 亥(11)
    """
    if hour >= 23 or hour < 1:
        return 0
    if 1 <= hour < 3:
        return 1
    if 3 <= hour < 5:
        return 2
    if 5 <= hour < 7:
        return 3
    if 7 <= hour < 9:
        return 4
    if 9 <= hour < 11:
        return 5
    if 11 <= hour < 13:
        return 6
    if 13 <= hour < 15:
        return 7
    if 15 <= hour < 17:
        return 8
    if 17 <= hour < 19:
        return 9
    if 19 <= hour < 21:
        return 10
    return 11  # 21~23


def get_hour_ganzi(day_stem: str, hour: int) -> dict:
    """시주 — branchIdx = hour-to-branch, stemIdx = (dayStemIdx*2 + branchIdx) % 10.

    TS getHourGanzi와 동일 (五鼠遁訣 정통 공식이 아닌 단순 *2 패턴).
    """
    branch_idx = _hour_to_branch_idx(hour)
    day_stem_idx = HEAVENLY_STEMS.index(day_stem)
    stem_idx = (day_stem_idx * 2 + branch_idx) % 10
    return _make_pillar(stem_idx, branch_idx)


# ─── 십성 ─────────────────────────────────────────────────────────
def get_ten_god(day_stem: str, target_stem: str, is_yang: bool) -> str:
    """십성 — 일간/대상 오행 관계 + is_yang (TS getTenGod 동일).

    is_yang은 호출자가 '대상이 일간과 동일한 음양인가?'로 미리 계산해서 전달.
    같은 음양 → 비견/식신/편재/편관/편인
    다른 음양 → 겁재/상관/정재/정관/정인
    """
    day_element = FIVE_ELEMENTS[day_stem]
    target_element = FIVE_ELEMENTS[target_stem]

    if day_element == target_element:
        return "비견" if is_yang else "겁재"

    if _PRODUCE_MAP[day_element] == target_element:
        return "식신" if is_yang else "상관"

    if _OVERCOME_MAP[day_element] == target_element:
        return "편재" if is_yang else "정재"

    if _OVERCOME_MAP[target_element] == day_element:
        return "편관" if is_yang else "정관"

    if _PRODUCE_MAP[target_element] == day_element:
        return "편인" if is_yang else "정인"

    return "비견"


# ─── 십이운성 ─────────────────────────────────────────────────────
def get_twelve_fortune(day_stem: str, branch: str) -> str:
    """십이운성 — 일간×지지 하드코딩 매핑 (TS getTwelveFortune 동일)."""
    idx = _FORTUNE_MAP.get(day_stem, {}).get(branch, 0)
    return TWELVE_FORTUNES[idx]


# ─── 사주 통합 계산 ────────────────────────────────────────────────
def calculate_saju(
    year: int,
    month: int,
    day: int,
    hour: Optional[int],
    gender: str,
) -> dict:
    """사주팔자 전체 계산 — TS calculateSaju와 1:1 일치.

    반환 키는 snake_case (year, month, day, hour, day_stem, birth_date, gender).
    """
    year_pillar = get_year_ganzi(year)
    month_pillar = get_month_ganzi(year, month, day)
    day_pillar = get_day_ganzi(year, month, day)

    day_stem = day_pillar["stem"]
    day_stem_idx = HEAVENLY_STEMS.index(day_stem)
    is_day_yang = (day_stem_idx % 2) == 0

    def _ten_god_for(stem: str) -> str:
        target_idx = HEAVENLY_STEMS.index(stem)
        target_yang = (target_idx % 2) == 0
        # is_yang 인자는 '대상이 일간과 같은 음양인가?'
        return get_ten_god(day_stem, stem, target_yang == is_day_yang)

    # 년주 — ten_god + fortune 부착
    year_pillar["ten_god"] = _ten_god_for(year_pillar["stem"])
    year_pillar["fortune"] = get_twelve_fortune(day_stem, year_pillar["branch"])

    # 월주
    month_pillar["ten_god"] = _ten_god_for(month_pillar["stem"])
    month_pillar["fortune"] = get_twelve_fortune(day_stem, month_pillar["branch"])

    # 일주 — ten_god='일간'
    day_pillar["ten_god"] = "일간"
    day_pillar["fortune"] = get_twelve_fortune(day_stem, day_pillar["branch"])

    birth_date: dict = {"year": year, "month": month, "day": day}

    result: dict = {
        "year": year_pillar,
        "month": month_pillar,
        "day": day_pillar,
        "day_stem": day_stem,
        "birth_date": birth_date,
        "gender": gender,
    }

    # 시주
    if hour is not None:
        hour_pillar = get_hour_ganzi(day_stem, hour)
        hour_pillar["ten_god"] = _ten_god_for(hour_pillar["stem"])
        hour_pillar["fortune"] = get_twelve_fortune(day_stem, hour_pillar["branch"])
        result["hour"] = hour_pillar
        birth_date["hour"] = hour

    return result
