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 CityUnionBankParser(BaseParser):
    bank_name = "City Union Bank"
    parser_name = "CityUnionBankParser"

    @classmethod
    def detect(cls, text: str) -> float:
        upper = (text or "").upper()
        score = 0.0
        if "CITY UNION BANK" in upper:
            score += 0.62
        if "CIUB0" in upper or "IFSC :CIUB" in upper or "IFSC:CIUB" in upper:
            score += 0.20
        if all(token in upper for token in ("DATE", "DESCRIPTION", "CHEQUE NO", "DEBIT", "CREDIT", "BALANCE")):
            score += 0.16
        if "END OF STATEMENT - FROM INTERNET BANKING" in upper:
            score += 0.02
        return min(score, 0.99)

    @staticmethod
    def _reference_number(narration: str, cheque_no: str) -> str:
        if norm(cheque_no):
            return norm(cheque_no)
        for pattern in (
            r"\bUTR[:/\s-]*([A-Z0-9]{10,30})\b",
            r"\bCHQ\s*NO\s*([0-9]{1,12})\b",
            r"\b([A-Z]{4}H\d{10,})\b",
        ):
            match = re.search(pattern, narration, re.I)
            if match:
                return match.group(1)
        return ""

    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"CUSTOMER DETAILS\s*:\s*([^\n]+)", text, flags=re.I))
        meta.account_number = find(r"ACCOUNT NO\s*\(15 DIGIT\)\s*:\s*([0-9]+)", text, flags=re.I)
        if not meta.account_number:
            meta.account_number = find(r"ACCOUNT NO\s*:\s*([A-Z0-9-]+)", text, flags=re.I)
        meta.ifsc = find(r"IFSC\s*:\s*([A-Z0-9]+)", text, flags=re.I)
        period = re.search(r"STATEMENT OF ACCOUNT\s+from\s+(\d{2}/\d{2}/\d{4})\s+to\s+(\d{2}/\d{2}/\d{4})", text, re.I)
        if period:
            meta.period_from = date_iso(period.group(1))
            meta.period_to = date_iso(period.group(2))

        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 all(token in header for token in ("DATE", "DESCRIPTION", "DEBIT", "CREDIT", "BALANCE")):
                        continue
                    for raw in table[1:]:
                        cells = list(raw) + [None] * (6 - len(raw))
                        txn_date, description, cheque_no, debit, credit, balance = cells[:6]
                        txn_date = norm(txn_date)
                        if not re.fullmatch(r"\d{2}/\d{2}/\d{4}", txn_date):
                            continue
                        debit_value = amount(debit)
                        credit_value = amount(credit)
                        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": txn_date,
                            "narration": narration,
                            "reference_no": self._reference_number(narration, norm(cheque_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)
