"""calculator/core.py — saju-web TS 엔진과 1:1 매칭 검증.

fixtures/reference_saju.json 의 30 case (TS calculateSaju 결과)와 Python 구현 일치 여부 검증.
"""
import json
from pathlib import Path

import pytest

from app.calculator.core import calculate_saju


REF_PATH = Path(__file__).parent / "fixtures" / "reference_saju.json"
REF = json.loads(REF_PATH.read_text(encoding="utf-8"))


def _camel_to_snake(name: str) -> str:
    out = []
    for ch in name:
        if ch.isupper():
            out.append("_" + ch.lower())
        else:
            out.append(ch)
    return "".join(out)


def _normalize(d):
    """TS camelCase → Python snake_case 변환 (deep)."""
    if isinstance(d, dict):
        return {_camel_to_snake(k): _normalize(v) for k, v in d.items()}
    if isinstance(d, list):
        return [_normalize(x) for x in d]
    return d


@pytest.mark.parametrize(
    "case",
    REF,
    ids=lambda c: f"{c['input']['year']}-{c['input']['month']:02d}-{c['input']['day']:02d}-{c['input'].get('hour')}-{c['input']['gender']}",
)
def test_calculate_saju_matches_reference(case):
    inp = case["input"]
    expected = _normalize(case["expected"]["saju"])

    actual = calculate_saju(
        inp["year"], inp["month"], inp["day"],
        inp.get("hour"), inp["gender"],
    )

    # 4기둥 비교
    for pillar in ["year", "month", "day", "hour"]:
        if expected.get(pillar) is None:
            assert actual.get(pillar) is None, f"{pillar} should be None"
            continue
        if pillar == "hour" and inp.get("hour") is None:
            assert actual.get(pillar) is None
            continue
        ep = expected[pillar]
        ap = actual[pillar]
        for field in ["stem", "branch", "stem_kr", "branch_kr", "element", "ten_god", "fortune"]:
            assert ap[field] == ep[field], (
                f"{pillar}.{field}: actual={ap[field]} expected={ep[field]} "
                f"(input={inp})"
            )

    # day_stem
    assert actual["day_stem"] == expected["day_stem"]

    # gender
    assert actual["gender"] == expected["gender"]

    # birth_date
    bd = actual["birth_date"]
    assert bd["year"] == inp["year"]
    assert bd["month"] == inp["month"]
    assert bd["day"] == inp["day"]
    if inp.get("hour") is not None:
        assert bd.get("hour") == inp["hour"]
