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 RBLBankParser(BaseParser):
    bank_name = "RBL Bank"
    parser_name = "RBLBankParser"

    @classmethod
    def detect(cls, text: str) -> float:
        upper = (text or "").upper()
        score = 0.0
        if "RBL BANK" in upper or "RBLBANK" in upper:
            score += 0.58
        if "RATN000" in upper or "IFSC/RTGS/NEFT" in upper and "RATN" in upper:
            score += 0.20
        if "STATEMENT OF TRANSACTIONS IN SAVINGS ACCOUNT" in upper:
            score += 0.12
        if all(token in upper for token in ("TRANSACTION DETAILS", "VALUE DATE", "WITHDRAWAL AMT", "DEPOSIT AMT", "BALANCE AMT")):
            score += 0.10
        return min(score, 0.99)

    @staticmethod
    def _signed_balance(value) -> float | None:
        text = norm(value).upper()
        parsed = amount(text)
        if parsed is None:
            return None
        return -abs(parsed) if text.endswith("DR") else abs(parsed)

    @staticmethod
    def _reference_number(narration: str, cheque_no: str) -> str:
        if norm(cheque_no):
            return norm(cheque_no)
        for pattern in (
            r"\bUPI/([0-9]{10,18})\b",
            r"\bIMPS\s+([0-9]{10,18})\b",
            r"\b(?:NEFT|RTGS)/([A-Z0-9]{10,30})\b",
            r"\b([0-9]{12,18})-IMPS\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"Accountholder Name\s*:?\s*([^\n]+)", text, flags=re.I))
        meta.customer_id = find(r"CIF ID\s*:?\s*([0-9]+)", text, flags=re.I)
        meta.account_number = find(r"Statement Of Transactions In Savings Account No\.\s*([0-9]+)", text, flags=re.I)
        meta.ifsc = find(r"(?:IFSC/RTGS/NEFT|IFSC)\s*:?\s*([A-Z0-9]+)", text, flags=re.I)
        period = re.search(r"Period\s*:\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] = []
        printed_opening: float | None = None
        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", "TRANSACTION DETAILS", "VALUE DATE", "WITHDRAWAL AMT", "DEPOSIT AMT", "BALANCE AMT")):
                        continue
                    for raw in table[1:]:
                        cells = list(raw) + [None] * (7 - len(raw))
                        txn_date, description, cheque_no, value_date, withdrawal, deposit, balance = cells[:7]
                        txn_date = norm(txn_date)
                        balance_value = self._signed_balance(balance)
                        if not txn_date and balance_value is not None and printed_opening is None:
                            printed_opening = balance_value
                            continue
                        if not re.fullmatch(r"\d{2}-[A-Za-z]{3}-\d{4}", txn_date):
                            continue
                        debit_value = amount(withdrawal)
                        credit_value = amount(deposit)
                        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": 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(printed_opening if printed_opening is not None else 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)
