# ============================================================
# core/ledger.py
# Sổ cái, bảng cân đối số phát sinh, số dư tài khoản
# ============================================================

import sqlite3
from typing import Optional
from core.db_init import get_conn
from core.engine_bridge import get_engine


def get_account_balance(db_path: str, account_code: str, year: Optional[int] = None, month: Optional[int] = None) -> dict:
    """
    Lấy số dư từ Finac Engine.
    Lưu ý: Finac tính toán double-entry chuẩn dựa trên toàn bộ lịch sử.
    """
    try:
        engine = get_engine(db_path)
        balance = engine.get_balance(account_code)
        
        # Lấy info TK từ DB chính
        conn = get_conn(db_path)
        acc = conn.cursor().execute("SELECT name, type FROM accounts WHERE code=?", (account_code,)).fetchone()
        conn.close()
        
        if not acc: return {}
        
        acc_type = acc[1]
        balance_side = "debit" if acc_type in ("asset", "expense") else "credit"
        
        # Finac balance: dương là Nợ, âm là Có
        # Chúng ta giả lập các trường cho tương thích với code cũ
        return {
            "code": account_code,
            "name": acc[0],
            "type": acc_type,
            "opening_debit": 0,    # Finac handles history internally
            "opening_credit": 0,
            "period_debit": 0,     # Cần tính toán thêm nếu muốn tách rời
            "period_credit": 0,
            "closing_debit": balance if balance_side == "debit" and balance >= 0 else 0,
            "closing_credit": abs(balance) if balance_side == "credit" and balance < 0 else (abs(balance) if balance_side == "debit" and balance < 0 else 0),
            "balance": abs(balance),
            "balance_side": balance_side if balance >= 0 else ("credit" if balance_side == "debit" else "debit")
        }
    except Exception as e:
        print(f"Error fetching balance from Finac: {e}")
        return {}


def get_trial_balance(db_path: str, year: int, month: int) -> list:
    """
    Bảng cân đối số phát sinh (CĐSP) — tất cả TK cấp 1 + cấp 2.
    Trả về list dict, sắp xếp theo mã TK.
    """
    conn = get_conn(db_path)
    c = conn.cursor()

    # Lấy tất cả TK cấp 1+2 có phát sinh hoặc số dư
    codes = c.execute("""
        SELECT DISTINCT je.account_code
        FROM journal_entries je
        JOIN vouchers v ON v.id = je.voucher_id
        WHERE v.period_year=? AND v.period_month<=? AND v.status='posted'
        UNION
        SELECT account_code FROM opening_balances WHERE year=? AND month=1
    """, (year, month, year)).fetchall()
    conn.close()

    rows = []
    for (code,) in codes:
        bal = get_account_balance(db_path, code, year, month)
        if bal:
            rows.append(bal)

    rows.sort(key=lambda x: x["code"])
    return rows


def get_ledger_card(db_path: str, account_code: str,
                    year: int, month: int) -> list:
    """
    Sổ cái chi tiết 1 tài khoản bằng Finac Engine.
    """
    try:
        engine = get_engine(db_path)
        start_date = f"{year}-{month:02d}-01"
        # Tính ngày cuối tháng (đơn giản hóa)
        end_date = f"{year}-{month:02d}-31"
        
        statement = engine.get_statement(account_code, start=start_date, end=end_date)
        
        # Mapping Finac statement sang format của app
        entries = []
        for s in statement:
            entries.append({
                "voucher_date": s.get("dt"),
                "voucher_no": s.get("tag", ""),
                "voucher_type": "Other",
                "description": s.get("note", ""),
                "debit": s.get("amount") if s.get("amount") > 0 else 0,
                "credit": abs(s.get("amount")) if s.get("amount") < 0 else 0,
                "partner_name": "",
                "running_balance": s.get("balance")
            })
        return entries
    except Exception as e:
        print(f"Error fetching statement: {e}")
        return []


def get_journal(db_path: str, year: int, month: int) -> list:
    """Sổ nhật ký chung — tất cả bút toán trong tháng."""
    conn = get_conn(db_path)
    c = conn.cursor()

    rows = c.execute("""
        SELECT
            v.voucher_date,
            v.voucher_no,
            v.voucher_type,
            COALESCE(je.description, v.description) AS description,
            je.account_code,
            a.name AS account_name,
            je.debit,
            je.credit
        FROM journal_entries je
        JOIN vouchers v ON v.id = je.voucher_id
        JOIN accounts a ON a.code = je.account_code
        WHERE v.period_year=? AND v.period_month=? AND v.status='posted'
        ORDER BY v.voucher_date, v.id, je.line_no
    """, (year, month)).fetchall()

    cols = [d[0] for d in c.description]
    conn.close()
    return [dict(zip(cols, r)) for r in rows]


def get_period_summary(db_path: str, year: int, month: int) -> dict:
    """Tóm tắt kỳ: tổng phát sinh, số chứng từ, trạng thái."""
    conn = get_conn(db_path)
    c = conn.cursor()

    stats = c.execute("""
        SELECT
            COUNT(*) AS voucher_count,
            COALESCE(SUM(total_debit),0) AS total_debit,
            SUM(CASE WHEN status='posted' THEN 1 ELSE 0 END) AS posted,
            SUM(CASE WHEN status='draft' THEN 1 ELSE 0 END) AS draft,
            SUM(CASE WHEN status='cancelled' THEN 1 ELSE 0 END) AS cancelled
        FROM vouchers
        WHERE period_year=? AND period_month=?
    """, (year, month)).fetchone()

    period_status = c.execute(
        "SELECT status FROM periods WHERE year=? AND month=?", (year, month)
    ).fetchone()

    conn.close()
    return {
        "year":          year,
        "month":         month,
        "period_status": period_status[0] if period_status else "open",
        "voucher_count": stats[0],
        "total_debit":   stats[1],
        "posted":        stats[2],
        "draft":         stats[3],
        "cancelled":     stats[4],
    }


def print_trial_balance(db_path: str, year: int, month: int) -> None:
    """In bảng CĐSP ra terminal."""
    rows = get_trial_balance(db_path, year, month)
    if not rows:
        print(f"(Không có số liệu kỳ {month:02d}/{year})")
        return

    total_ob_dr = total_ob_cr = total_ps_dr = total_ps_cr = 0
    total_cl_dr = total_cl_cr = 0

    print(f"\n{'─'*100}")
    print(f"  BẢNG CÂN ĐỐI SỐ PHÁT SINH — Kỳ {month:02d}/{year}")
    print(f"{'─'*100}")
    print(f"{'TK':>8}  {'Tên tài khoản':<30}  {'Đầu kỳ Nợ':>14}  {'Đầu kỳ Có':>14}  {'PS Nợ':>14}  {'PS Có':>14}  {'Cuối kỳ Nợ':>14}  {'Cuối kỳ Có':>14}")
    print(f"{'─'*100}")

    for r in rows:
        print(f"{r['code']:>8}  {r['name'][:30]:<30}  {r['opening_debit']:>14,.0f}  {r['opening_credit']:>14,.0f}  {r['period_debit']:>14,.0f}  {r['period_credit']:>14,.0f}  {r['closing_debit']:>14,.0f}  {r['closing_credit']:>14,.0f}")
        total_ob_dr += r['opening_debit']
        total_ob_cr += r['opening_credit']
        total_ps_dr += r['period_debit']
        total_ps_cr += r['period_credit']
        total_cl_dr += r['closing_debit']
        total_cl_cr += r['closing_credit']

    print(f"{'─'*100}")
    print(f"{'TỔNG':>8}  {'':30}  {total_ob_dr:>14,.0f}  {total_ob_cr:>14,.0f}  {total_ps_dr:>14,.0f}  {total_ps_cr:>14,.0f}  {total_cl_dr:>14,.0f}  {total_cl_cr:>14,.0f}")
    print()
