from datetime import datetime
from lxml import etree


NS = "urn:iso:std:iso:20022:tech:xsd:camt.053.001.02"
NSMAP = {None: NS}


def _e(parent, tag, text=None):
    el = etree.SubElement(parent, f"{{{NS}}}{tag}")
    if text is not None:
        el.text = str(text)
    return el


class Camt053Mapper:
    def map(self, parsed: dict, message_id: str) -> str:
        doc = etree.Element(f"{{{NS}}}Document", nsmap=NSMAP)
        stmt_root = _e(doc, "BkToCstmrStmt")

        # GrpHdr
        grp = _e(stmt_root, "GrpHdr")
        _e(grp, "MsgId", message_id)
        _e(grp, "CreDtTm", datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S"))

        # Stmt
        stmt = _e(stmt_root, "Stmt")

        stmt_num = parsed.get("statement_number", "1")
        seq = "1"
        if "/" in stmt_num:
            parts = stmt_num.split("/")
            stmt_num = parts[0]
            seq = parts[1] if len(parts) > 1 else "1"

        _e(stmt, "Id", stmt_num)
        _e(stmt, "ElctrncSeqNb", seq)

        # Acct
        acct = _e(stmt, "Acct")
        acct_id = _e(acct, "Id")
        _e(acct_id, "IBAN", parsed["account_id"])

        # Opening balance (OPBD)
        self._add_balance(stmt, parsed["opening_balance"], "OPBD")

        # Closing balance (CLBD)
        self._add_balance(stmt, parsed["closing_balance"], "CLBD")

        # Entries
        currency = parsed["opening_balance"]["currency"]
        for tx in parsed.get("transactions", []):
            self._add_entry(stmt, tx, currency)

        return etree.tostring(
            doc,
            pretty_print=True,
            xml_declaration=True,
            encoding="UTF-8",
        ).decode("utf-8")

    def _add_balance(self, stmt, balance: dict, code: str):
        bal = _e(stmt, "Bal")
        tp = _e(bal, "Tp")
        cop = _e(tp, "CdOrPrtry")
        _e(cop, "Cd", code)

        amt = _e(bal, "Amt", f"{balance['amount']:.2f}")
        amt.set("Ccy", balance["currency"])

        _e(bal, "CdtDbtInd", balance["dc_indicator"])

        dt_el = _e(bal, "Dt")
        _e(dt_el, "Dt", balance["date"])

    def _add_entry(self, stmt, tx: dict, currency: str):
        ntry = _e(stmt, "Ntry")
        amt = _e(ntry, "Amt", f"{tx['amount']:.2f}")
        amt.set("Ccy", currency)
        _e(ntry, "CdtDbtInd", tx["dc_indicator"])
        _e(ntry, "Sts", "BOOK")

        booking = _e(ntry, "BookgDt")
        _e(booking, "Dt", tx["entry_date"])

        val = _e(ntry, "ValDt")
        _e(val, "Dt", tx["value_date"])

        _e(ntry, "NtryRef", tx.get("reference", ""))

        if tx.get("transaction_details"):
            ntry_dtls = _e(ntry, "NtryDtls")
            tx_dtls = _e(ntry_dtls, "TxDtls")
            rmt = _e(tx_dtls, "RmtInf")
            _e(rmt, "Ustrd", tx["transaction_details"])
