"""daeun.py — 대운 8개 계산 검증.

fixtures/reference_saju.json 의 30 case (TS) 와 Python 구현 1:1 일치 검증.
"""
import json
from pathlib import Path

import pytest

from app.calculator.core import calculate_saju
from app.calculator.daeun import calculate_daeun, get_current_daeun, get_daeun_description


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']['gender']}",
)
def test_calculate_daeun_matches_reference(case):
    inp = case["input"]
    expected = [_normalize(x) for x in case["expected"]["daeun"]]

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

    assert len(actual) == len(expected), (
        f"len mismatch for {inp}: {len(actual)} vs {len(expected)}"
    )

    for i, (a, e) in enumerate(zip(actual, expected)):
        for key in ("stem", "branch", "stem_kr", "branch_kr"):
            assert a.get(key) == e.get(key), (
                f"{inp} daeun[{i}].{key}: actual={a.get(key)} expected={e.get(key)}"
            )
        # age + start_year/end_year
        assert a.get("age") == e.get("age"), (
            f"{inp} daeun[{i}].age: {a.get('age')} vs {e.get('age')}"
        )
        assert a.get("start_year") == e.get("start_year"), (
            f"{inp} daeun[{i}].start_year: {a.get('start_year')} vs {e.get('start_year')}"
        )
        assert a.get("end_year") == e.get("end_year"), (
            f"{inp} daeun[{i}].end_year: {a.get('end_year')} vs {e.get('end_year')}"
        )


def test_get_current_daeun_returns_match():
    daeun_list = [
        {"age": 10, "start_year": 2000, "end_year": 2009},
        {"age": 20, "start_year": 2010, "end_year": 2019},
        {"age": 30, "start_year": 2020, "end_year": 2029},
    ]
    assert get_current_daeun(daeun_list, 2005) == daeun_list[0]
    assert get_current_daeun(daeun_list, 2015) == daeun_list[1]
    assert get_current_daeun(daeun_list, 2025) == daeun_list[2]
    assert get_current_daeun(daeun_list, 1999) is None
    assert get_current_daeun(daeun_list, 2030) is None


def test_get_daeun_description_returns_string():
    daeun = {
        "age": 10,
        "start_year": 2000,
        "end_year": 2009,
        "stem": "壬",
        "branch": "午",
        "stem_kr": "임",
        "branch_kr": "오",
    }
    desc = get_daeun_description(daeun, "辛")
    assert isinstance(desc, str)
    assert "임오" in desc
    assert "壬午" in desc
    assert "10세" in desc
    assert "19세" in desc
