# backend/app/modules/trial_balance/service.py
# 試算表.pdf フォーマットに完全準拠した試算表サービス
from decimal import Decimal
from typing import Dict, Any, List
from app.core.database import get_connection

def D(x) -> Decimal:
    return Decimal(str(x or 0))

# 除外する科目コード
EXCLUDED_CODES = {'512', '513', '516'}


def fetch_trial_balance(date_from: str, date_to: str):

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

        # 勘定科目一覧
        cur.execute("""
            SELECT account_id, account_code, account_name, account_type
              FROM accounts
             WHERE is_active = true
             ORDER BY account_code
        """)
        accounts = cur.fetchall()

        # 科目データを格納
        account_data: Dict[str, Dict[str, Any]] = {}

        for acc in accounts:
            aid = acc["account_id"]
            code = acc["account_code"]

            # 除外科目をスキップ
            if code in EXCLUDED_CODES:
                continue

            # 期首残高: opening_balances テーブルの残高 + date_from より前の仕訳
            cur.execute("""
                SELECT COALESCE(opening_debit, 0) - COALESCE(opening_credit, 0) AS ob
                  FROM opening_balances
                 WHERE account_id = %s
                   AND fiscal_year = EXTRACT(YEAR FROM %s::date)
            """, (aid, date_from))
            ob_row = cur.fetchone()
            opening_balance_from_table = D(ob_row["ob"]) if ob_row else D(0)

            # date_from より前の仕訳による増減
            cur.execute("""
                SELECT COALESCE(SUM(l.debit - l.credit), 0) AS opening
                  FROM journal_lines l
                  JOIN journal_entries j
                    ON j.journal_entry_id = l.journal_entry_id
                 WHERE l.account_id = %s
                   AND j.entry_date < %s
                   AND j.is_deleted = false
                   AND j.is_latest = true
            """, (aid, date_from))
            opening_from_journals = D(cur.fetchone()["opening"])

            opening = opening_balance_from_table + opening_from_journals

            # 当期増減（date_from ～ date_to）
            cur.execute("""
                SELECT
                    COALESCE(SUM(l.debit), 0)  AS debit,
                    COALESCE(SUM(l.credit), 0) AS credit
                  FROM journal_lines l
                  JOIN journal_entries j
                    ON j.journal_entry_id = l.journal_entry_id
                 WHERE l.account_id = %s
                   AND j.entry_date BETWEEN %s AND %s
                   AND j.is_deleted = false
                   AND j.is_latest = true
            """, (aid, date_from, date_to))
            row = cur.fetchone()
            debit = D(row["debit"])
            credit = D(row["credit"])

            closing = opening + debit - credit

            # 負債・資本は絶対値で表示
            if acc["account_type"] in ("liability", "equity"):
                opening = abs(opening)
                closing = abs(closing)

            account_data[code] = {
                "account_id": aid,
                "account_code": code,
                "account_name": acc["account_name"],
                "account_type": acc["account_type"],
                "opening_balance": opening,
                "debit": debit,
                "credit": credit,
                "closing_balance": closing,
            }

    # =============================================
    # ヘルパー関数
    # =============================================
    def acct_row(code):
        """個別科目の行を返す（存在しない場合はNone）"""
        if code not in account_data:
            return None
        d = account_data[code]
        return {
            "account_code": d["account_code"],
            "account_name": d["account_name"],
            "account_type": d["account_type"],
            "opening_balance": str(d["opening_balance"]),
            "debit": str(d["debit"]),
            "credit": str(d["credit"]),
            "closing_balance": str(d["closing_balance"]),
            "ratio": None,
            "is_total": False,
        }

    def total_row(name, codes, is_total=True, indent=False):
        """合計行を生成"""
        existing = [c for c in codes if c in account_data]
        opening = sum(account_data[c]["opening_balance"] for c in existing) if existing else D(0)
        debit = sum(account_data[c]["debit"] for c in existing) if existing else D(0)
        credit = sum(account_data[c]["credit"] for c in existing) if existing else D(0)
        closing = sum(account_data[c]["closing_balance"] for c in existing) if existing else D(0)
        return {
            "account_code": "",
            "account_name": name,
            "account_type": "",
            "opening_balance": str(opening),
            "debit": str(debit),
            "credit": str(credit),
            "closing_balance": str(closing),
            "ratio": None,
            "is_total": is_total,
            "indent": indent,
        }

    def custom_row(name, opening, debit, credit, closing,
                   is_total=False, indent=False, account_type="", is_pl=False):
        """カスタム行を生成"""
        row = {
            "account_code": "",
            "account_name": name,
            "account_type": account_type,
            "opening_balance": str(opening),
            "debit": str(debit),
            "credit": str(credit),
            "closing_balance": str(closing),
            "ratio": None,
            "is_total": is_total,
            "indent": indent,
        }
        if is_pl:
            row["is_pl"] = True
        return row

    # =============================================
    # 結果リスト構築（試算表.pdf の順序に完全準拠）
    # =============================================
    result_accounts: List[Dict[str, Any]] = []
    added_codes = set()  # 追加済みの科目コードを追跡

    def add(code):
        """個別科目を追加"""
        r = acct_row(code)
        if r:
            result_accounts.append(r)
            added_codes.add(code)

    def add_total(name, codes, **kwargs):
        """合計行を追加"""
        result_accounts.append(total_row(name, codes, **kwargs))

    # ========== 資産の部 ==========

    # 現金・預金
    cash_codes = ["101", "102", "103", "104", "105"]
    for c in cash_codes:
        add(c)
    add_total("現金・預金合計", cash_codes)

    # 売上債権
    receivable_codes = ["201", "202"]
    for c in receivable_codes:
        add(c)
    add_total("売上債権合計", receivable_codes)

    # 他流動資産
    other_current_codes = ["203", "204", "205", "206", "207", "208"]
    for c in other_current_codes:
        add(c)
    add_total("他流動資産合計", other_current_codes)

    # 有価証券
    securities_codes = ["301"]
    for c in securities_codes:
        add(c)
    add_total("有価証券合計", securities_codes)

    # 流動資産合計
    current_asset_codes = cash_codes + receivable_codes + other_current_codes + securities_codes
    add_total("流動資産合計", current_asset_codes)

    # 有形固定資産
    tangible_codes = ["401", "402", "403", "404", "405", "406", "407"]
    for c in tangible_codes:
        add(c)
    add_total("有形固定資産合計", tangible_codes)

    # 無形固定資産
    intangible_codes = ["408"]
    for c in intangible_codes:
        add(c)
    add_total("無形固定資産合計", intangible_codes)

    # 投資その他の資産
    investment_codes = ["410"]
    for c in investment_codes:
        add(c)
    add_total("投資その他の資産合計", investment_codes)

    # 固定資産合計
    fixed_asset_codes = tangible_codes + intangible_codes + investment_codes
    add_total("固定資産合計", fixed_asset_codes)

    # 資産合計
    all_asset_codes = current_asset_codes + fixed_asset_codes
    add_total("資産合計", all_asset_codes)

    # ========== 負債の部 ==========

    # 仕入債務
    trade_payable_codes = ["501"]
    for c in trade_payable_codes:
        add(c)
    add_total("仕入債務合計", trade_payable_codes)

    # 流動負債その他（502-508）
    current_liability_other = ["502", "503", "504", "505", "506", "507", "508"]
    for c in current_liability_other:
        add(c)
    current_liability_codes = trade_payable_codes + current_liability_other
    add_total("流動負債合計", current_liability_codes)

    # 固定負債
    fixed_liability_codes = ["509"]
    for c in fixed_liability_codes:
        add(c)
    add_total("固定負債合計", fixed_liability_codes)

    # 負債合計
    all_liability_codes = current_liability_codes + fixed_liability_codes
    add_total("負債合計", all_liability_codes)

    # ========== 510/511: 独立科目（負債と資本の間） ==========
    add("510")
    add("511")

    # ========== 資本の部 ==========

    # 資本金
    capital_codes = ["601"]
    for c in capital_codes:
        add(c)
    add_total("資本金合計", capital_codes)

    # 繰越利益剰余金（602）
    add("602")

    # --- 当期純損益金額の計算 ---
    revenue_total = D(0)
    for acc_code, acc_data in account_data.items():
        if acc_code.startswith("7"):
            revenue_total += acc_data["credit"] - acc_data["debit"]
    expense_total = D(0)
    for acc_code, acc_data in account_data.items():
        if acc_code.startswith("8"):
            expense_total += acc_data["debit"] - acc_data["credit"]
    net_income = revenue_total - expense_total

    # 繰越利益剰余金の期首残高
    retained_opening = account_data.get("602", {}).get("opening_balance", D(0))
    retained_total = retained_opening + net_income

    # 繰越利益
    result_accounts.append(
        custom_row("繰越利益", retained_opening, D(0), D(0), retained_opening,
                   indent=True, account_type="equity")
    )

    # 繰越利益剰余金合計
    result_accounts.append(
        custom_row("繰越利益剰余金合計", retained_opening,
                   abs(net_income) if net_income < 0 else D(0),
                   net_income if net_income > 0 else D(0),
                   retained_total,
                   is_total=True, indent=True, account_type="equity")
    )

    # その他利益剰余金合計
    result_accounts.append(
        custom_row("その他利益剰余金合計", D(0), D(0), D(0), D(0),
                   is_total=True, indent=True, account_type="equity")
    )

    # 利益剰余金合計
    result_accounts.append(
        custom_row("利益剰余金合計", retained_opening,
                   abs(net_income) if net_income < 0 else D(0),
                   net_income if net_income > 0 else D(0),
                   retained_total,
                   is_total=True, account_type="equity")
    )

    # 純資産合計
    capital_closing = account_data.get("601", {}).get("closing_balance", D(0))
    equity_total_closing = capital_closing + retained_total
    equity_total_opening = sum(
        account_data[c]["opening_balance"] for c in ["601", "602"] if c in account_data
    )
    result_accounts.append(
        custom_row("純資産合計", equity_total_opening, D(0), D(0), equity_total_closing,
                   is_total=True, account_type="equity")
    )

    # ========== 損益の部（截図フォーマットに準拠） ==========

    # --- P/L 用ヘルパー ---
    def pl_row(code, indent=False):
        """P/L個別科目行（closing = credit-debit or debit-credit）"""
        if code not in account_data:
            return None
        d = account_data[code]
        if code.startswith("7"):
            pl_closing = d["credit"] - d["debit"]
        else:
            pl_closing = d["debit"] - d["credit"]
        return {
            "account_code": d["account_code"],
            "account_name": d["account_name"],
            "account_type": d["account_type"],
            "opening_balance": "0",
            "debit": str(d["debit"]),
            "credit": str(d["credit"]),
            "closing_balance": str(pl_closing),
            "ratio": None,
            "is_total": False,
            "indent": indent,
            "is_pl": True,
        }

    def add_pl(code, indent=False):
        r = pl_row(code, indent)
        if r:
            result_accounts.append(r)
            added_codes.add(code)

    # === 売上高 ===
    add_pl("701")

    # 売上高合計
    rev_d = account_data.get("701", {})
    revenue_val = rev_d.get("credit", D(0)) - rev_d.get("debit", D(0)) if "701" in account_data else D(0)
    result_accounts.append(
        custom_row("売上高合計", D(0), D(0), revenue_val, revenue_val, is_total=True, is_pl=True)
    )

    # === 売上原価セクション ===
    # 期首商品棚卸高
    result_accounts.append(
        custom_row("期首商品棚卸高", D(0), D(0), D(0), D(0), indent=True, is_pl=True)
    )
    # 当期商品仕入高（= 801 仕入高）
    if "801" in account_data:
        add_pl("801", indent=True)
    else:
        result_accounts.append(
            custom_row("当期商品仕入高", D(0), D(0), D(0), D(0), indent=True, is_pl=True)
        )
        added_codes.add("801")
    # 合計
    cogs_d = account_data.get("801", {})
    cogs_debit = cogs_d.get("debit", D(0)) if "801" in account_data else D(0)
    cogs_credit = cogs_d.get("credit", D(0)) if "801" in account_data else D(0)
    cogs_net = cogs_debit - cogs_credit
    result_accounts.append(
        custom_row("合計", D(0), cogs_debit, cogs_credit, cogs_net,
                   is_total=True, indent=True, is_pl=True)
    )
    # 期末商品棚卸高
    result_accounts.append(
        custom_row("期末商品棚卸高", D(0), D(0), D(0), D(0), indent=True, is_pl=True)
    )
    # 売上原価
    result_accounts.append(
        custom_row("売上原価", D(0), cogs_debit, cogs_credit, cogs_net,
                   is_total=True, indent=True, is_pl=True)
    )

    # === 売上総損益金額 ===
    gross_profit = revenue_val - cogs_net
    result_accounts.append(
        custom_row("売上総損益金額", D(0), D(0), gross_profit, gross_profit,
                   is_total=True, is_pl=True)
    )

    # === 販売管理費（截図の順序に準拠） ===
    sga_order = [
        "812", "813", "814", "815",  # 人件費系
        "802",                        # 外注費
        "816", "817",                 # 交際費・会議費
        "803", "804", "805",          # 旅費・通信・消耗品
        "818",                        # 新聞図書費
        "808",                        # 支払手数料
        "806",                        # 地代家賃
        "819", "820",                 # 保険料・租税公課
    ]
    # 上記リストにない8xx費用科目を末尾に追加（801除外）
    remaining_8xx = [c for c in sorted(account_data.keys())
                     if c.startswith("8") and c != "801" and c not in sga_order]
    sga_all = sga_order + remaining_8xx

    sga_debit_total = D(0)
    sga_credit_total = D(0)
    sga_closing_total = D(0)
    for code in sga_all:
        add_pl(code, indent=True)
        if code in account_data:
            d = account_data[code]
            sga_debit_total += d["debit"]
            sga_credit_total += d["credit"]
            sga_closing_total += d["debit"] - d["credit"]

    # 販売管理費計
    result_accounts.append(
        custom_row("販売管理費計", D(0), sga_debit_total, sga_credit_total,
                   sga_closing_total, is_total=True, is_pl=True)
    )

    # === 営業損益金額 ===
    operating_profit = gross_profit - sga_closing_total
    result_accounts.append(
        custom_row("営業損益金額", D(0), D(0), operating_profit, operating_profit,
                   is_total=True, is_pl=True)
    )

    # === 営業外収益 ===
    non_op_rev_codes = ["702", "709"]
    non_op_rev_debit = D(0)
    non_op_rev_credit = D(0)
    for code in non_op_rev_codes:
        add_pl(code, indent=True)
        if code in account_data:
            d = account_data[code]
            non_op_rev_debit += d["debit"]
            non_op_rev_credit += d["credit"]
    non_op_rev_closing = non_op_rev_credit - non_op_rev_debit

    # 営業外収益合計
    result_accounts.append(
        custom_row("営業外収益合計", D(0), non_op_rev_debit, non_op_rev_credit,
                   non_op_rev_closing, is_total=True, indent=True, is_pl=True)
    )

    # 営業外費用合計（現在は該当科目なし）
    result_accounts.append(
        custom_row("営業外費用合計", D(0), D(0), D(0), D(0),
                   is_total=True, indent=True, is_pl=True)
    )

    # === 経常損益金額 ===
    ordinary_profit = operating_profit + non_op_rev_closing
    result_accounts.append(
        custom_row("経常損益金額", D(0), D(0), ordinary_profit, ordinary_profit,
                   is_total=True, is_pl=True)
    )

    # === 特別利益合計 ===
    result_accounts.append(
        custom_row("特別利益合計", D(0), D(0), D(0), D(0),
                   is_total=True, indent=True, is_pl=True)
    )

    # === 特別損失合計 ===
    result_accounts.append(
        custom_row("特別損失合計", D(0), D(0), D(0), D(0),
                   is_total=True, indent=True, is_pl=True)
    )

    # === 当期純損益金額 ===
    result_accounts.append(
        custom_row("当期純損益金額", D(0), D(0), ordinary_profit, ordinary_profit,
                   is_total=True, is_pl=True)
    )

    # ========== 未分類科目（上記にない科目を末尾に追加） ==========
    for code in sorted(account_data.keys()):
        if code not in added_codes:
            add(code)

    # =============================================
    # 構成比を計算
    # B/S: 借方残合計ベース
    # P/L: 売上高（対売上比）ベース
    # =============================================
    debit_balance_total = sum(
        d["closing_balance"] for d in account_data.values()
        if d["closing_balance"] > 0
    )

    for acc in result_accounts:
        closing = D(acc["closing_balance"])
        if acc.get("is_pl"):
            # P/L: 対売上比
            if revenue_val != 0:
                ratio = (closing / revenue_val) * 100
                acc["ratio"] = f"{ratio:.2f}"
            else:
                acc["ratio"] = "0.00"
        else:
            # B/S: 借方残合計ベース
            if debit_balance_total != 0:
                ratio = (closing / debit_balance_total) * 100
                acc["ratio"] = f"{ratio:.2f}"
            else:
                acc["ratio"] = "0.00"

    # 総合計を計算
    total_opening = sum(d["opening_balance"] for d in account_data.values())
    total_debit = sum(d["debit"] for d in account_data.values())
    total_credit = sum(d["credit"] for d in account_data.values())
    total_closing = sum(d["closing_balance"] for d in account_data.values())

    result = {
        "totals": {
            "opening_balance": str(total_opening),
            "debit": str(total_debit),
            "credit": str(total_credit),
            "closing_balance": str(total_closing),
            "ratio": "100.00"
        },
        "accounts": result_accounts,
        "grand_total_closing": str(debit_balance_total),
    }

    return result
