"""saju.db SQLite — saju_records + compat_records CRUD."""
import json
import os
import sqlite3
from typing import Any, Dict, Optional

from .config import DB_PATH


def _conn() -> sqlite3.Connection:
    os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
    conn = sqlite3.connect(DB_PATH, timeout=120.0)
    conn.row_factory = sqlite3.Row
    conn.execute("PRAGMA journal_mode=WAL")
    conn.execute("PRAGMA busy_timeout=120000")
    return conn


def init_db() -> None:
    with _conn() as conn:
        conn.execute("""
            CREATE TABLE IF NOT EXISTS saju_records (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                birth_year INTEGER NOT NULL,
                birth_month INTEGER NOT NULL,
                birth_day INTEGER NOT NULL,
                birth_hour INTEGER,
                gender TEXT NOT NULL,
                calendar_type TEXT DEFAULT 'solar',
                saju_data TEXT NOT NULL,
                analysis_data TEXT NOT NULL,
                daeun_data TEXT NOT NULL,
                interpretation_json TEXT,
                model TEXT,
                tokens_in INTEGER DEFAULT 0,
                tokens_out INTEGER DEFAULT 0,
                cost_usd REAL DEFAULT 0,
                latency_ms INTEGER DEFAULT 0,
                reroll_count INTEGER DEFAULT 0,
                favorite INTEGER DEFAULT 0,
                memo TEXT,
                created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
            )
        """)
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_saju_created
            ON saju_records(created_at DESC)
        """)
        # 신규 컬럼 ALTER (idempotent — 이미 있으면 OperationalError로 skip)
        for col in (
            "fortune_scores_json TEXT",
            "lucky_json TEXT",
            "monthly_flow_json TEXT",
        ):
            try:
                conn.execute(f"ALTER TABLE saju_records ADD COLUMN {col}")
            except sqlite3.OperationalError:
                pass

        conn.execute("""
            CREATE TABLE IF NOT EXISTS compat_records (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                person_a TEXT NOT NULL,
                person_b TEXT NOT NULL,
                saju_a TEXT NOT NULL,
                saju_b TEXT NOT NULL,
                score INTEGER NOT NULL,
                breakdown TEXT NOT NULL,
                interpretation_json TEXT,
                model TEXT,
                tokens_in INTEGER DEFAULT 0,
                tokens_out INTEGER DEFAULT 0,
                cost_usd REAL DEFAULT 0,
                latency_ms INTEGER DEFAULT 0,
                reroll_count INTEGER DEFAULT 0,
                favorite INTEGER DEFAULT 0,
                memo TEXT,
                created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
            )
        """)


# saju_records CRUD
def save_saju_record(data: Dict[str, Any]) -> int:
    with _conn() as conn:
        cur = conn.execute(
            """INSERT INTO saju_records
               (birth_year, birth_month, birth_day, birth_hour, gender, calendar_type,
                saju_data, analysis_data, daeun_data, interpretation_json,
                model, tokens_in, tokens_out, cost_usd, latency_ms, reroll_count,
                fortune_scores_json, lucky_json, monthly_flow_json)
               VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
            (
                data["birth_year"], data["birth_month"], data["birth_day"],
                data.get("birth_hour"), data["gender"], data.get("calendar_type", "solar"),
                json.dumps(data["saju_data"], ensure_ascii=False),
                json.dumps(data["analysis_data"], ensure_ascii=False),
                json.dumps(data["daeun_data"], ensure_ascii=False),
                json.dumps(data.get("interpretation_json"), ensure_ascii=False) if data.get("interpretation_json") else None,
                data.get("model"),
                data.get("tokens_in", 0), data.get("tokens_out", 0),
                data.get("cost_usd", 0.0), data.get("latency_ms", 0),
                data.get("reroll_count", 0),
                json.dumps(data["fortune_scores_json"], ensure_ascii=False) if data.get("fortune_scores_json") else None,
                json.dumps(data["lucky_json"], ensure_ascii=False) if data.get("lucky_json") else None,
                json.dumps(data["monthly_flow_json"], ensure_ascii=False) if data.get("monthly_flow_json") else None,
            ),
        )
        return int(cur.lastrowid)


def get_saju_record(record_id: int) -> Optional[Dict[str, Any]]:
    with _conn() as conn:
        r = conn.execute("SELECT * FROM saju_records WHERE id=?", (record_id,)).fetchone()
    return _saju_row_to_dict(r) if r else None


def list_saju_records(page: int = 1, size: int = 20, favorite: Optional[bool] = None) -> Dict[str, Any]:
    wheres, params = [], []
    if favorite is not None:
        wheres.append("favorite=?")
        params.append(1 if favorite else 0)
    where_sql = ("WHERE " + " AND ".join(wheres)) if wheres else ""
    offset = (page - 1) * size
    with _conn() as conn:
        total = conn.execute(f"SELECT COUNT(*) c FROM saju_records {where_sql}", params).fetchone()["c"]
        rows = conn.execute(
            f"SELECT * FROM saju_records {where_sql} ORDER BY created_at DESC LIMIT ? OFFSET ?",
            params + [size, offset],
        ).fetchall()
    return {
        "items": [_saju_row_to_dict(r) for r in rows],
        "page": page, "size": size, "total": int(total),
    }


def update_saju_record(record_id: int, **kwargs) -> None:
    sets, vals = [], []
    if "favorite" in kwargs and kwargs["favorite"] is not None:
        sets.append("favorite=?"); vals.append(1 if kwargs["favorite"] else 0)
    if "memo" in kwargs and kwargs["memo"] is not None:
        sets.append("memo=?"); vals.append(kwargs["memo"])
    if not sets:
        return
    vals.append(record_id)
    with _conn() as conn:
        conn.execute(f"UPDATE saju_records SET {','.join(sets)} WHERE id=?", vals)


def delete_saju_record(record_id: int) -> None:
    with _conn() as conn:
        conn.execute("DELETE FROM saju_records WHERE id=?", (record_id,))


def _saju_row_to_dict(r) -> Dict[str, Any]:
    def _safe_json(val):
        if val is None:
            return None
        try:
            return json.loads(val)
        except (ValueError, TypeError):
            return None

    return {
        "id": r["id"],
        "created_at": r["created_at"],
        "birth_year": r["birth_year"], "birth_month": r["birth_month"], "birth_day": r["birth_day"],
        "birth_hour": r["birth_hour"], "gender": r["gender"], "calendar_type": r["calendar_type"],
        "saju_data": _safe_json(r["saju_data"]),
        "analysis_data": _safe_json(r["analysis_data"]),
        "daeun_data": _safe_json(r["daeun_data"]),
        "interpretation_json": _safe_json(r["interpretation_json"]),
        "model": r["model"], "tokens_in": r["tokens_in"], "tokens_out": r["tokens_out"],
        "cost_usd": r["cost_usd"], "latency_ms": r["latency_ms"], "reroll_count": r["reroll_count"],
        "favorite": int(r["favorite"]), "memo": r["memo"],
        "fortune_scores": _safe_json(r["fortune_scores_json"]) if "fortune_scores_json" in r.keys() else None,
        "lucky": _safe_json(r["lucky_json"]) if "lucky_json" in r.keys() else None,
        "monthly_flow": _safe_json(r["monthly_flow_json"]) if "monthly_flow_json" in r.keys() else None,
    }


# compat_records CRUD
def save_compat_record(data: Dict[str, Any]) -> int:
    with _conn() as conn:
        cur = conn.execute(
            """INSERT INTO compat_records
               (person_a, person_b, saju_a, saju_b, score, breakdown,
                interpretation_json, model, tokens_in, tokens_out,
                cost_usd, latency_ms, reroll_count)
               VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)""",
            (
                json.dumps(data["person_a"], ensure_ascii=False),
                json.dumps(data["person_b"], ensure_ascii=False),
                json.dumps(data["saju_a"], ensure_ascii=False),
                json.dumps(data["saju_b"], ensure_ascii=False),
                data["score"],
                json.dumps(data["breakdown"], ensure_ascii=False),
                json.dumps(data.get("interpretation_json"), ensure_ascii=False) if data.get("interpretation_json") else None,
                data.get("model"),
                data.get("tokens_in", 0), data.get("tokens_out", 0),
                data.get("cost_usd", 0.0), data.get("latency_ms", 0),
                data.get("reroll_count", 0),
            ),
        )
        return int(cur.lastrowid)


def get_compat_record(record_id: int) -> Optional[Dict[str, Any]]:
    with _conn() as conn:
        r = conn.execute("SELECT * FROM compat_records WHERE id=?", (record_id,)).fetchone()
    return _compat_row_to_dict(r) if r else None


def list_compat_records(page: int = 1, size: int = 20, favorite: Optional[bool] = None) -> Dict[str, Any]:
    wheres, params = [], []
    if favorite is not None:
        wheres.append("favorite=?"); params.append(1 if favorite else 0)
    where_sql = ("WHERE " + " AND ".join(wheres)) if wheres else ""
    offset = (page - 1) * size
    with _conn() as conn:
        total = conn.execute(f"SELECT COUNT(*) c FROM compat_records {where_sql}", params).fetchone()["c"]
        rows = conn.execute(
            f"SELECT * FROM compat_records {where_sql} ORDER BY created_at DESC LIMIT ? OFFSET ?",
            params + [size, offset],
        ).fetchall()
    return {
        "items": [_compat_row_to_dict(r) for r in rows],
        "page": page, "size": size, "total": int(total),
    }


def update_compat_record(record_id: int, **kwargs) -> None:
    sets, vals = [], []
    if "favorite" in kwargs and kwargs["favorite"] is not None:
        sets.append("favorite=?"); vals.append(1 if kwargs["favorite"] else 0)
    if "memo" in kwargs and kwargs["memo"] is not None:
        sets.append("memo=?"); vals.append(kwargs["memo"])
    if not sets:
        return
    vals.append(record_id)
    with _conn() as conn:
        conn.execute(f"UPDATE compat_records SET {','.join(sets)} WHERE id=?", vals)


def delete_compat_record(record_id: int) -> None:
    with _conn() as conn:
        conn.execute("DELETE FROM compat_records WHERE id=?", (record_id,))


def _compat_row_to_dict(r) -> Dict[str, Any]:
    return {
        "id": r["id"],
        "created_at": r["created_at"],
        "person_a": json.loads(r["person_a"]),
        "person_b": json.loads(r["person_b"]),
        "saju_a": json.loads(r["saju_a"]),
        "saju_b": json.loads(r["saju_b"]),
        "score": r["score"],
        "breakdown": json.loads(r["breakdown"]),
        "interpretation_json": json.loads(r["interpretation_json"]) if r["interpretation_json"] else None,
        "model": r["model"], "tokens_in": r["tokens_in"], "tokens_out": r["tokens_out"],
        "cost_usd": r["cost_usd"], "latency_ms": r["latency_ms"], "reroll_count": r["reroll_count"],
        "favorite": int(r["favorite"]), "memo": r["memo"],
    }
