from fastapi import APIRouter, HTTPException
from pydantic import BaseModel, field_validator
from typing import List
from decimal import Decimal, InvalidOperation
from psycopg.errors import ForeignKeyViolation, CheckViolation
from app.core.database import get_connection
from app.core.fiscal_lock import check_fiscal_year_unlocked
from datetime import date

router = APIRouter(prefix="/journals", tags=["仕訳"])

class JournalLine(BaseModel):
    account_id: int
    debit: Decimal = Decimal("0")
    credit: Decimal = Decimal("0")

    @field_validator("debit", "credit", mode="before")
    @classmethod
    def to_decimal(cls, v):
        # 允许传 0 / 整数 / 字符串
        try:
            d = Decimal(str(v))
        except InvalidOperation:
            raise ValueError("金額は数値で入力してください")
        if d < 0:
            raise ValueError("金額は0以上で入力してください")
        return d

    @field_validator("credit")
    @classmethod
    def exclusive_debit_credit(cls, v, info):
        # 不能借贷同时 > 0，也不能都为 0 的校验在整体里做
        return v

class JournalCreate(BaseModel):
    journal_date: str  # YYYY-MM-DD
    description: str
    lines: List[JournalLine]

    @field_validator("journal_date")
    @classmethod
    def validate_date(cls, v: str):
        # 简单校验格式，严格校验可用 datetime.date.fromisoformat
        if len(v) != 10 or v[4] != "-" or v[7] != "-":
            raise ValueError("日付はYYYY-MM-DD形式で入力してください")
        return v

    @field_validator("description")
    @classmethod
    def validate_desc(cls, v: str):
        if not v.strip():
            raise ValueError("摘要を入力してください")
        if len(v) > 255:
            raise ValueError("摘要が長すぎます（255文字以内）")
        return v

@router.post("/", summary="仕訳登録", status_code=201)
def create_journal(data: JournalCreate):
    # 1) 行必須
    if not data.lines:
        raise HTTPException(status_code=400, detail="仕訳明細を1行以上入力してください。")

    # 2) 借貸排他 & 非ゼロ
    for i, l in enumerate(data.lines, start=1):
        if l.debit > 0 and l.credit > 0:
            raise HTTPException(status_code=400, detail=f"{i}行目：借方と貸方を同時に入力しないでください。")
        if l.debit == 0 and l.credit == 0:
            raise HTTPException(status_code=400, detail=f"{i}行目：金額が未入力です（借方または貸方のどちらかに金額を入力）。")

    # 3) 借貸一致
    debit_total = sum(l.debit for l in data.lines)
    credit_total = sum(l.credit for l in data.lines)
    if debit_total != credit_total:
        raise HTTPException(status_code=400, detail="借方合計と貸方合計が一致しません。")

    # 4) 仕訳に使う科目が全て存在するか事前チェック
    account_ids = sorted({l.account_id for l in data.lines})
    with get_connection() as conn, conn.cursor() as cur:
        cur.execute(
            "SELECT account_id FROM accounts WHERE account_id = ANY(%s)",
            (account_ids,)
        )
        found = {row["account_id"] for row in cur.fetchall()}
        missing = [a for a in account_ids if a not in found]
        if missing:
            raise HTTPException(
                status_code=400,
                detail=f"存在しない科目IDが含まれています：{missing}"
            )

        try:
            # 5) 登録（ヘッダ → 明細）
            cur.execute(
                "INSERT INTO journal_entries (journal_date, description) VALUES (%s, %s) RETURNING journal_id",
                (data.journal_date, data.description),
            )
            jid = cur.fetchone()["journal_id"]

            for l in data.lines:
                cur.execute(
                    """INSERT INTO journal_lines (journal_id, account_id, debit, credit)
                       VALUES (%s, %s, %s, %s)""",
                    (jid, l.account_id, l.debit, l.credit),
                )

            conn.commit()
            return {"journal_id": jid}

        except ForeignKeyViolation:
            # 理论上到不了（我们前面已做存在性检查），兜底
            raise HTTPException(status_code=400, detail="科目IDが不正です。")
        except CheckViolation:
            raise HTTPException(status_code=400, detail="金額の指定が不正です。")


@router.put("/{journal_id}", summary="仕訳修正")
def update_journal(journal_id: int, data: JournalCreate):

    with get_connection() as conn, conn.cursor() as cur:

        # ① 先取原仕訳の日付
        cur.execute(
            "SELECT journal_date FROM journal_entries WHERE journal_id = %s",
            (journal_id,)
        )
        row = cur.fetchone()
        if not row:
            raise HTTPException(status_code=404, detail="仕訳が存在しません。")

        # 🔒 ② 这里！！年度锁定检查
        check_fiscal_year_unlocked(row["journal_date"])

        # ③ 删除旧明细
        cur.execute(
            "DELETE FROM journal_lines WHERE journal_id = %s",
            (journal_id,)
        )

        # ④ 更新ヘッダ
        cur.execute(
            "UPDATE journal_entries SET journal_date=%s, description=%s WHERE journal_id=%s",
            (data.journal_date, data.description, journal_id)
        )

        # ⑤ 插入新明细
        for l in data.lines:
            cur.execute(
                """INSERT INTO journal_lines (journal_id, account_id, debit, credit)
                   VALUES (%s, %s, %s, %s)""",
                (journal_id, l.account_id, l.debit, l.credit)
            )

        conn.commit()
        return {"message": "仕訳を修正しました。"}

@router.delete("/{journal_id}", summary="仕訳削除")
def delete_journal(journal_id: int):

    with get_connection() as conn, conn.cursor() as cur:

        # ① 先取日付
        cur.execute(
            "SELECT journal_date FROM journal_entries WHERE journal_id = %s",
            (journal_id,)
        )
        row = cur.fetchone()
        if not row:
            raise HTTPException(status_code=404, detail="仕訳が存在しません。")

        # 🔒 ② 年度锁定检查（就在这里）
        check_fiscal_year_unlocked(row["journal_date"])

        # ③ 删除
        cur.execute(
            "DELETE FROM journal_lines WHERE journal_id = %s",
            (journal_id,)
        )
        cur.execute(
            "DELETE FROM journal_entries WHERE journal_id = %s",
            (journal_id,)
        )

        conn.commit()
        return {"message": "仕訳を削除しました。"}