# ============================================================
# core/journal.py
# Nhập chứng từ kế toán và bút toán (Journal Entries)
# ============================================================

import sqlite3
from datetime import datetime
from typing import List, Tuple, Optional

from core.db_init import get_conn
from core.engine_bridge import get_engine


# ── Loại chứng từ ──────────────────────────────────────────
VOUCHER_TYPES = {
    "PT":     "Phiếu thu",
    "PC":     "Phiếu chi",
    "BNKthu": "Báo nợ / Thu ngân hàng",
    "BNKchi": "Ủy nhiệm chi / Chi ngân hàng",
    "HDXB":   "Hóa đơn xuất bán",
    "HDMV":   "Hóa đơn mua vào",
    "NK":     "Phiếu nhập kho",
    "XK":     "Phiếu xuất kho",
    "PB":     "Phân bổ chi phí",
    "KC":     "Kết chuyển cuối kỳ",
    "other":  "Chứng từ khác",
}

# ── Tạo số chứng từ tự động ────────────────────────────────

def next_voucher_no(conn: sqlite3.Connection, voucher_type: str, year: int, month: int) -> str:
    """Tạo số chứng từ tự động: PC001/01-2026"""
    c = conn.cursor()
    prefix = f"{year}{month:02d}"
    last = c.execute("""
        SELECT voucher_no FROM vouchers
        WHERE voucher_type=? AND period_year=? AND period_month=?
        ORDER BY id DESC LIMIT 1
    """, (voucher_type, year, month)).fetchone()

    if last:
        m = last[0].split("/")[0]
        seq_str = "".join(filter(str.isdigit, m[-4:]))
        seq = int(seq_str) + 1 if seq_str else 1
    else:
        seq = 1
    return f"{voucher_type}{seq:03d}/{month:02d}-{year}"


# ── Kiểm tra cân bằng bút toán ─────────────────────────────

def validate_balance(entries: List[dict]) -> Tuple[bool, float, float]:
    """Kiểm tra tổng Nợ == tổng Có"""
    total_debit  = sum(e.get("debit",  0) for e in entries)
    total_credit = sum(e.get("credit", 0) for e in entries)
    return abs(total_debit - total_credit) < 0.01, total_debit, total_credit


# ── Kiểm tra tài khoản tồn tại ─────────────────────────────

def validate_accounts(conn: sqlite3.Connection, entries: List[dict]) -> List[str]:
    """Trả về danh sách mã TK không tồn tại trong DB."""
    c = conn.cursor()
    invalid = []
    for e in entries:
        code = e.get("account_code", "")
        row = c.execute("SELECT code FROM accounts WHERE code=?", (code,)).fetchone()
        if not row:
            invalid.append(code)
    return invalid


# ── Ghi chứng từ ───────────────────────────────────────────

def post_voucher(
    db_path: str,
    voucher_type: str,
    voucher_date: str,
    description: str,
    entries: List[dict],
    *,
    voucher_no: str = "",
    partner_id: Optional[int] = None,
    reference: str = "",
    currency: str = "VND",
    exchange_rate: float = 1.0,
    notes: str = "",
    status: str = "posted",
) -> int:
    """
    Ghi chứng từ kế toán vào database.

    entries: list of {account_code, debit, credit, description, partner_id, ...}
    Returns: voucher_id
    """
    conn = get_conn(db_path)
    c = conn.cursor()

    # Parse ngày
    if isinstance(voucher_date, str):
        dt = datetime.strptime(voucher_date, "%Y-%m-%d")
    else:
        dt = voucher_date
    year, month = dt.year, dt.month

    # Kiểm tra kỳ kế toán
    period = c.execute(
        "SELECT status FROM periods WHERE year=? AND month=?", (year, month)
    ).fetchone()
    if period and period[0] == "locked":
        raise ValueError(f"Kỳ {month:02d}/{year} đã khoá, không thể nhập thêm chứng từ.")
    # Kiểm tra cân bằng bút toán (TRƯỚC transaction)
    balanced, total_dr, total_cr = validate_balance(entries)
    if not balanced:
        conn.close()
        raise ValueError(
            f"Bút toán không cân bằng! Nợ: {total_dr:,.0f} | Có: {total_cr:,.0f}"
        )

    # Kiểm tra tài khoản tồn tại
    invalid_accs = validate_accounts(conn, entries)
    if invalid_accs:
        conn.close()
        raise ValueError(f"Tài khoản không tồn tại: {', '.join(invalid_accs)}")

    # ── Ghi atomically trong 1 transaction ────────────────────
    with conn:
        # Tạo kỳ nếu chưa có
        conn.execute(
            "INSERT OR IGNORE INTO periods (year, month) VALUES (?,?)", (year, month)
        )

        # Tạo số chứng từ tự động nếu chưa có
        if not voucher_no:
            voucher_no = next_voucher_no(conn, voucher_type, year, month)

        # Chèn chứng từ
        cur = conn.execute("""
        INSERT INTO vouchers
            (voucher_no, voucher_type, voucher_date, period_year, period_month,
             description, partner_id, reference, currency, exchange_rate,
             total_debit, total_credit, status, created_at, posted_at, notes)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now','localtime'),
                CASE WHEN ? = 'posted' THEN datetime('now','localtime') ELSE NULL END, ?)
        """, (
            voucher_no, voucher_type, voucher_date, year, month,
            description, partner_id, reference, currency, exchange_rate,
            total_dr, total_cr, status, status, notes
        ))
        voucher_id = cur.lastrowid

        # Chèn bút toán
        for i, e in enumerate(entries, 1):
            conn.execute("""
            INSERT INTO journal_entries
                (voucher_id, line_no, account_code, debit, credit, description,
                 partner_id, cost_center, project, tax_code)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            """, (
                voucher_id, i,
                e["account_code"],
                e.get("debit",  0),
                e.get("credit", 0),
                e.get("description", description),
                e.get("partner_id", partner_id),
                e.get("cost_center", ""),
                e.get("project", ""),
                e.get("tax_code", ""),
            ))
    # end transaction — commit tự động, rollback nếu có exception

    # --- Đồng bộ sang Finac Engine (ngoài transaction chính) ---
    try:
        engine = get_engine(db_path)
        accs = conn.execute("SELECT code, name, type FROM accounts").fetchall()
        coa = [{"code": r[0], "name": r[1], "type": r[2]} for r in accs]
        engine.setup_coa(coa)
        engine.add_transaction(
            dt=voucher_date,
            entries=[{"account": e["account_code"], "debit": e.get("debit", 0), "credit": e.get("credit", 0)} for e in entries],
            description=description,
            tag=voucher_no
        )
    except Exception as e:
        print(f"⚠️ Cảnh báo: Lỗi đồng bộ Finac Engine: {e}")

    conn.close()
    return voucher_id



# ── Hủy chứng từ ───────────────────────────────────────────

def cancel_voucher(db_path: str, voucher_id: int) -> bool:
    """Hủy chứng từ (đặt status='cancelled')."""
    conn = get_conn(db_path)
    c = conn.cursor()
    row = c.execute("SELECT status FROM vouchers WHERE id=?", (voucher_id,)).fetchone()
    if not row:
        conn.close()
        raise ValueError(f"Không tìm thấy chứng từ ID={voucher_id}")
    if row[0] == "cancelled":
        conn.close()
        return False
    c.execute("UPDATE vouchers SET status='cancelled' WHERE id=?", (voucher_id,))
    conn.commit()
    conn.close()
    return True


# ── Lấy chứng từ ───────────────────────────────────────────

def get_voucher(db_path: str, voucher_id: int) -> Optional[dict]:
    """Lấy đầy đủ thông tin 1 chứng từ kèm bút toán."""
    conn = get_conn(db_path)
    c = conn.cursor()

    v = c.execute("""
        SELECT v.*, p.name AS partner_name
        FROM vouchers v
        LEFT JOIN partners p ON p.id = v.partner_id
        WHERE v.id = ?
    """, (voucher_id,)).fetchone()

    if not v:
        conn.close()
        return None

    cols = [desc[0] for desc in c.description]
    voucher = dict(zip(cols, v))

    entries = c.execute("""
        SELECT je.*, a.name AS account_name
        FROM journal_entries je
        LEFT JOIN accounts a ON a.code = je.account_code
        WHERE je.voucher_id = ?
        ORDER BY je.line_no
    """, (voucher_id,)).fetchall()

    entry_cols = [desc[0] for desc in c.description]
    voucher["entries"] = [dict(zip(entry_cols, e)) for e in entries]

    conn.close()
    return voucher


# ── Template bút toán phổ biến ─────────────────────────────

def make_cash_receipt(amount: float, account_dr: str = "111", account_cr: str = "5113",
                      description: str = "") -> List[dict]:
    """Phiếu thu tiền mặt (dịch vụ)"""
    return [
        {"account_code": account_dr, "debit": amount,  "credit": 0, "description": description},
        {"account_code": account_cr, "debit": 0, "credit": amount, "description": description},
    ]


def make_cash_payment(amount: float, account_dr: str = "641", account_cr: str = "111",
                      description: str = "") -> List[dict]:
    """Phiếu chi tiền mặt"""
    return [
        {"account_code": account_dr, "debit": amount,  "credit": 0, "description": description},
        {"account_code": account_cr, "debit": 0, "credit": amount, "description": description},
    ]


def make_sales_invoice_entries(pretax: float, vat_rate: float = 0.1,
                               account_receivable: str = "131",
                               account_revenue: str = "5113",
                               account_vat_out: str = "3331") -> List[dict]:
    """Hóa đơn bán hàng + VAT đầu ra"""
    vat = pretax * vat_rate
    total = pretax + vat
    return [
        {"account_code": account_receivable, "debit": total,  "credit": 0},
        {"account_code": account_revenue,    "debit": 0, "credit": pretax},
        {"account_code": account_vat_out,    "debit": 0, "credit": vat,   "tax_code": f"VAT{int(vat_rate*100)}"},
    ]


def make_purchase_invoice_entries(pretax: float, vat_rate: float = 0.1,
                                  account_expense: str = "642",
                                  account_payable: str = "331",
                                  account_vat_in: str = "133") -> List[dict]:
    """Hóa đơn mua vào + VAT đầu vào"""
    vat = pretax * vat_rate
    total = pretax + vat
    return [
        {"account_code": account_expense, "debit": pretax, "credit": 0},
        {"account_code": account_vat_in,  "debit": vat,    "credit": 0, "tax_code": f"VAT{int(vat_rate*100)}"},
        {"account_code": account_payable, "debit": 0, "credit": total},
    ]
