from __future__ import annotations

import re
from pathlib import Path

import pandas as pd
import pdfplumber

from .base import BaseParser, StatementMeta, amount, extract_text, finalize, norm
from .common import date_iso, find, infer_mode


class YesBankParser(BaseParser):
    bank_name = "YES Bank"
    parser_name = "YesBankParser"

    @classmethod
    def detect(cls, text: str) -> float:
        upper = (text or "").upper()
        score = 0.0
        if "YES BANK" in upper:
            score += 0.55
        if "YESB000" in upper or "IFSC CODE: YESB" in upper:
            score += 0.20
        if "TRANSACTION DETAILS FOR YOUR ACCOUNT NUMBER" in upper:
            score += 0.15
        if all(token in upper for token in ("TRANSACTION DATE", "VALUE DATE", "WITHDRAWALS", "DEPOSITS", "RUNNING")):
            score += 0.10
        return min(score, 0.99)

    def parse(self, path, text=None):
        pdf_path = Path(path)
        text = text or extract_text(pdf_path)
        meta = StatementMeta(
            bank_name=self.bank_name,
            source_file=pdf_path.name,
            parser_name=self.parser_name,
            confidence="High",
        )
        meta.customer_name = norm(find(r"Primary Holder:\s*([^\n]+?)(?:\s+A/C Opening Date:|\n)", text, flags=re.I))
        if not meta.customer_name:
            meta.customer_name = norm(find(r"Primary Account Holder Name:\s*([^\n]+)", text, flags=re.I))
        meta.customer_id = find(r"(?:Cust Id|Customer Id):\s*([0-9]+)", text, flags=re.I)
        meta.account_number = find(r"Statement of account:\s*([0-9]+)", text, flags=re.I)
        if not meta.account_number:
            meta.account_number = find(r"account number\s+([0-9]+)", text, flags=re.I)
        meta.ifsc = find(r"IFSC Code:\s*([A-Z0-9]+)", text, flags=re.I)
        period = re.search(r"Period:\s*From\s+(.+?)\s+To\s+([^\n]+)", text, re.I)
        if period:
            meta.period_from = date_iso(period.group(1).strip())
            meta.period_to = date_iso(period.group(2).strip())

        rows: list[dict] = []
        with pdfplumber.open(str(pdf_path)) as pdf:
            for page_no, page in enumerate(pdf.pages, start=1):
                for table in page.extract_tables() or []:
                    if not table:
                        continue
                    header = [norm(cell).upper() for cell in table[0]]
                    if not ("TRANSACTION DATE" in header and "VALUE DATE" in header and "DESCRIPTION" in header):
                        continue
                    for raw in table[1:]:
                        cells = list(raw) + [None] * (7 - len(raw))
                        txn_date, value_date, reference_no, description, withdrawal, deposit, balance = cells[:7]
                        txn_date = norm(txn_date)
                        if not re.fullmatch(r"\d{2}-[A-Za-z]{3}-\d{4}", txn_date):
                            continue
                        debit_value = amount(withdrawal)
                        credit_value = amount(deposit)
                        balance_value = amount(balance)
                        if debit_value is None and credit_value is None:
                            continue
                        narration = norm(description)
                        rows.append({
                            "transaction_date": txn_date,
                            "value_date": norm(value_date) or txn_date,
                            "narration": narration,
                            "reference_no": norm(reference_no),
                            "debit": debit_value,
                            "credit": credit_value,
                            "balance": balance_value,
                            "source_page": page_no,
                            "mode": infer_mode(narration),
                        })

        data = pd.DataFrame(rows)
        if not data.empty:
            first = data.iloc[0]
            first_balance = float(first["balance"])
            first_debit = float(first["debit"]) if pd.notna(first["debit"]) else 0.0
            first_credit = float(first["credit"]) if pd.notna(first["credit"]) else 0.0
            meta.opening_balance = round(first_balance + first_debit - first_credit, 2)
            meta.total_debit = round(float(pd.to_numeric(data["debit"], errors="coerce").fillna(0).sum()), 2)
            meta.total_credit = round(float(pd.to_numeric(data["credit"], errors="coerce").fillna(0).sum()), 2)
            meta.closing_balance = round(float(data.iloc[-1]["balance"]), 2)
        return meta, finalize(data, meta)
