import re
from datetime import date


class MT940Parser:
    DC_MAP = {
        "C": "CRDT",
        "D": "DBIT",
        "RC": "CRDT",
        "RD": "DBIT",
    }

    def parse(self, raw_text: str) -> dict:
        text = raw_text.replace("\r\n", "\n").replace("\r", "\n").strip()
        fields = self._extract_fields(text)

        result = {
            "transaction_reference": self._require(fields, "20", ":20:"),
            "account_id": self._parse_account(self._require(fields, "25", ":25:")),
            "statement_number": fields.get("28C", ""),
            "opening_balance": self._parse_balance(self._require(fields, "60", ":60F: o :60M:")),
            "closing_balance": self._parse_balance(self._require(fields, "62", ":62F: o :62M:")),
            "available_balance": self._parse_balance(fields["64"]) if fields.get("64") else None,
            "transactions": self._parse_transactions(fields.get("transactions", [])),
        }
        return result

    def _extract_fields(self, text: str) -> dict:
        fields: dict = {}
        transactions = []
        current_tx = None
        current_detail = None

        lines = text.split("\n")
        i = 0
        while i < len(lines):
            line = lines[i]

            # Collect continuation lines (don't start with :tag:)
            tag_match = re.match(r"^:(\w+):(.*)", line)
            if tag_match:
                tag = tag_match.group(1)
                value = tag_match.group(2).strip()

                # Collect multi-line values
                j = i + 1
                while j < len(lines) and not re.match(r"^:(\w+):", lines[j]) and lines[j] != "-":
                    value += "\n" + lines[j]
                    j += 1

                if tag == "20":
                    fields["20"] = value
                elif tag == "25":
                    fields["25"] = value
                elif tag == "28C":
                    fields["28C"] = value
                elif tag in ("60F", "60M"):
                    fields["60"] = tag[-1] + value  # prefix F or M
                elif tag in ("62F", "62M"):
                    fields["62"] = tag[-1] + value
                elif tag == "64":
                    fields["64"] = "F" + value
                elif tag == "61":
                    if current_tx:
                        transactions.append(current_tx)
                    current_tx = {"raw_61": value, "details": ""}
                    current_detail = None
                elif tag == "86":
                    if current_tx is not None:
                        current_tx["details"] = value
                i = j
            else:
                i += 1

        if current_tx:
            transactions.append(current_tx)

        fields["transactions"] = transactions
        return fields

    def _require(self, fields: dict, key: str, label: str) -> str:
        val = fields.get(key)
        if not val:
            raise ValueError(f"Campo requerido faltante: {label}")
        return val

    def _parse_account(self, raw: str) -> str:
        # Remove BIC/ prefix if present
        if "/" in raw:
            parts = raw.split("/")
            return parts[-1].strip()
        return raw.strip()

    def _parse_balance(self, raw: str) -> dict:
        # Format: [F|M][C|D|RC|RD]YYMMDDCCCAMOUNT
        m = re.match(r"^([FM])(RC|RD|C|D)(\d{6})([A-Z]{3})([\d,]+)$", raw.strip())
        if not m:
            raise ValueError(f"Formato de balance inválido: {raw}")
        bal_type = "final" if m.group(1) == "F" else "midday"
        dc = self.DC_MAP.get(m.group(2), m.group(2))
        dt = self._parse_date(m.group(3))
        currency = m.group(4)
        amount = float(m.group(5).replace(",", "."))
        return {
            "type": bal_type,
            "date": dt,
            "currency": currency,
            "amount": amount,
            "dc_indicator": dc,
        }

    def _parse_date(self, yymmdd: str) -> str:
        yy = int(yymmdd[0:2])
        mm = int(yymmdd[2:4])
        dd = int(yymmdd[4:6])
        year = 2000 + yy if yy <= 30 else 1900 + yy
        return date(year, mm, dd).isoformat()

    def _parse_transactions(self, raw_list: list) -> list:
        result = []
        for item in raw_list:
            raw = item["raw_61"]
            details = item["details"]
            tx = self._parse_61(raw)
            tx["transaction_details"] = details
            result.append(tx)
        return result

    def _parse_61(self, raw: str) -> dict:
        # :61: VDEDCAMOUNTTYPEREF//BANKREF
        # Value date: YYMMDD, entry date: MMDD (optional)
        m = re.match(
            r"^(\d{6})(\d{4})?(RC|RD|C|D)[A-Z]?([\d,]+)([A-Z]{4})(\S+?)(?://(\S+))?$",
            raw.strip(),
        )
        if not m:
            raise ValueError(f"Línea :61: con formato inválido: {raw}")

        value_date = self._parse_date(m.group(1))
        entry_date_raw = m.group(2)
        if entry_date_raw:
            # MMDD — year from value_date
            year = value_date[:4]
            entry_date = f"{year}-{entry_date_raw[:2]}-{entry_date_raw[2:]}"
        else:
            entry_date = value_date

        dc = self.DC_MAP.get(m.group(3), m.group(3))
        amount = float(m.group(4).replace(",", "."))
        tx_type = m.group(5)
        reference = m.group(6)
        bank_reference = m.group(7) or ""

        return {
            "value_date": value_date,
            "entry_date": entry_date,
            "dc_indicator": dc,
            "amount": amount,
            "transaction_type": tx_type,
            "reference": reference,
            "bank_reference": bank_reference,
        }
