from fastapi import APIRouter, HTTPException, Query
from pydantic import BaseModel, field_validator
from typing import List, Optional, Tuple
from decimal import Decimal, InvalidOperation

from app.core.database import get_connection

router = APIRouter(prefix="/opening-balances", tags=["期首残高"])


# ---------- Models ----------

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

    @field_validator("debit", "credit", mode="before")
    @classmethod
    def to_decimal(cls, v):
        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/0 は許容：行スキップ用）
        debit = info.data.get("debit", Decimal("0"))
        if v > 0 and Decimal(debit) > 0:
            raise ValueError("借方と貸方を同時に入力しないでください")
        return v


class OpeningBalanceRequest(BaseModel):
    fiscal_year: int
    balances: List[OpeningBalanceItem]


# ---------- GET：期首残高取得（BS のみ） ----------

@router.get("", summary="期首残高取得")
def get_opening_balances(
    fiscal_year: int = Query(..., description="会計年度（例：2025）")
):
    with get_connection() as conn, conn.cursor() as cur:
        cur.execute("""
            SELECT
                a.account_id,
                a.account_code,
                a.account_name,
                a.account_type,
                COALESCE(o.opening_debit, 0)  AS opening_debit,
                COALESCE(o.opening_credit, 0) AS opening_credit
            FROM accounts a
            LEFT JOIN opening_balances o
              ON o.account_id = a.account_id
             AND o.fiscal_year = %s
            WHERE a.is_active = true
              AND a.account_type IN ('asset','liability','equity')
            ORDER BY a.account_code
        """, (fiscal_year,))
        return cur.fetchall()


# ---------- 内部 util：繰越利益（剰余金）アカウント検索 ----------

def _find_retained_earnings(cur) -> Optional[Tuple[int, str]]:
    """
    可能な名称で繰越利益（剰余金）科目を探す。
    優先：'繰越利益剰余金' → '繰越利益'
    戻り値： (account_id, account_name) or None
    """
    cur.execute("""
        SELECT account_id, account_name
          FROM accounts
         WHERE is_active = true
           AND account_type = 'equity'
           AND account_name IN ('繰越利益剰余金','繰越利益')
         ORDER BY CASE account_name
                    WHEN '繰越利益剰余金' THEN 1
                    WHEN '繰越利益'       THEN 2
                    ELSE 99
                  END
         LIMIT 1
    """)
    row = cur.fetchone()
    return (row["account_id"], row["account_name"]) if row else None


# ---------- POST：期首残高保存（BSのみ・繰越利益は自動計算） ----------

@router.post("", summary="期首残高保存")
def save_opening_balances(req: OpeningBalanceRequest):

    with get_connection() as conn, conn.cursor() as cur:
        # テーブルの存在確認（例外処理を避けるため）
        table_exists = False
        try:
            cur.execute("""
                SELECT EXISTS (
                    SELECT 1 FROM information_schema.tables 
                    WHERE table_name = 'fiscal_year_locks'
                )
            """)
            table_exists = cur.fetchone()[0]
        except:
            pass

        # 年度ロック（テーブルが存在する場合のみチェック）
        if table_exists:
            cur.execute("""
                SELECT is_locked
                  FROM fiscal_year_locks
                 WHERE fiscal_year = %s
            """, (req.fiscal_year,))
            row = cur.fetchone()
            if row and row["is_locked"]:
                raise HTTPException(status_code=400, detail="この会計年度の期首残高は既に確定されています。")

        # 全 BS 科目取得（存在性/種別チェック用）
        cur.execute("""
            SELECT account_id, account_type, account_name
              FROM accounts
             WHERE is_active = true
        """)
        acc_map = {r["account_id"]: (r["account_type"], r["account_name"]) for r in cur.fetchall()}

        # ★ 重要：空白データ保存防止（ユーザーが読込せずに保存した場合の対策）
        if not req.balances:
            raise HTTPException(
                status_code=400, 
                detail="保存するデータがありません。「読込」ボタンで期首残高を読み込んでください。"
            )

        # 入力の整形：BSのみ採用・繰越利益は無視（後で自動作成）
        bs_items: List[OpeningBalanceItem] = []
        total_debit = Decimal("0")
        total_credit = Decimal("0")

        for b in req.balances:
            # 科目存在
            if b.account_id not in acc_map:
                raise HTTPException(status_code=400, detail=f"存在しない科目IDです：{b.account_id}")

            acc_type, acc_name = acc_map[b.account_id]

            # 繰越利益（剰余金）は前端から送られても無視（自動計算対象）
            if acc_name in ("繰越利益剰余金", "繰越利益"):
                continue

            # BS 以外は不可
            if acc_type not in ("asset", "liability", "equity"):
                raise HTTPException(status_code=400, detail=f"BS科目のみ設定可能です（{acc_name}）")

            # 0/0 はスキップ
            if b.debit == 0 and b.credit == 0:
                continue

            # 集計（繰越利益を除く）
            total_debit += b.debit
            total_credit += b.credit
            bs_items.append(b)

        # ★ 重要：0/0の行だけで実質的にデータなし（削除防止）
        if not bs_items:
            raise HTTPException(
                status_code=400, 
                detail="全科目の残高が0です。期首残高データを入力してください。"
            )

        # 繰越利益（剰余金）を自動計算
        diff = total_debit - total_credit  # 借方合計 − 貸方合計
        re_account = _find_retained_earnings(cur)

        # 既存データ削除（年度単位）
        cur.execute("DELETE FROM opening_balances WHERE fiscal_year = %s", (req.fiscal_year,))

        # 再登録：BS各行
        for b in bs_items:
            cur.execute("""
                INSERT INTO opening_balances
                    (fiscal_year, account_id, opening_debit, opening_credit)
                VALUES (%s, %s, %s, %s)
            """, (req.fiscal_year, b.account_id, b.debit, b.credit))

        # 自動：繰越利益（剰余金）
        re_inserted = None
        if re_account:
            re_id, re_name = re_account
            if diff > 0:
                # 借方が大 → その差額を「繰越利益（剰余金）の貸方」
                cur.execute("""
                    INSERT INTO opening_balances (fiscal_year, account_id, opening_debit, opening_credit)
                    VALUES (%s, %s, %s, %s)
                """, (req.fiscal_year, re_id, Decimal("0"), diff))
                re_inserted = {"account_id": re_id, "name": re_name, "debit": "0", "credit": str(diff)}
            elif diff < 0:
                # 贷方が大 → その差額を「繰越利益（剰余金）の借方」
                cur.execute("""
                    INSERT INTO opening_balances (fiscal_year, account_id, opening_debit, opening_credit)
                    VALUES (%s, %s, %s, %s)
                """, (req.fiscal_year, re_id, -diff, Decimal("0")))
                re_inserted = {"account_id": re_id, "name": re_name, "debit": str(-diff), "credit": "0"}
            else:
                # 完全一致 → 追加不要
                pass
        else:
            # 繰越利益科目が登録されていない場合：差額が 0 でないなら警告
            if diff != 0:
                raise HTTPException(
                    status_code=400,
                    detail="差額がありますが『繰越利益（剰余金）』科目が見つかりません。先に科目マスタを登録してください。"
                )

        conn.commit()

    return {
        "message": "期首残高を保存しました。",
        "totals": {
            "debit": str(total_debit),
            "credit": str(total_credit),
            "diff_before_retained": str(diff)
        },
        "retained_earnings_auto": re_inserted
    }
