# edited by glg
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP


class TransaksiPaymentPayloadService:
    """
    Use-case mapper payload pembayaran -> transaksi master.
    Menjaga nilai uang sebagai integer rupiah dan data kartu tersimpan dalam bentuk masked last4.
    """

    @staticmethod
    def _safe_text(value, default=""):
        if value is None:
            return str(default or "")
        text = str(value).strip()
        return text if text else str(default or "")

    @staticmethod
    def _to_decimal(value, default="0"):
        try:
            return Decimal(str(value if value is not None else default))
        except (InvalidOperation, ValueError, TypeError):
            return Decimal(str(default or "0"))

    @classmethod
    def _to_money_int(cls, value, default=0):
        amount = cls._to_decimal(value, str(default or 0))
        # edited by glg
        # Seluruh nominal uang dibulatkan ke rupiah terdekat agar konsisten antar flow.
        return int(amount.quantize(Decimal("1"), rounding=ROUND_HALF_UP))

    @classmethod
    def _to_non_negative_money_int(cls, value, default=0):
        return max(0, int(cls._to_money_int(value, default=default)))

    @classmethod
    def _to_percent_float(cls, value, default=0.0):
        raw = cls._to_decimal(value, str(default or 0))
        return float(max(Decimal("0"), raw))

    @classmethod
    def _mask_card_last4(cls, raw_value):
        text = cls._safe_text(raw_value, "")
        if not text:
            return ""
        digits = "".join(ch for ch in text if ch.isdigit())
        if digits:
            return digits[-4:]
        compact = "".join(ch for ch in text if ch.isalnum())
        return compact[-4:]

    @classmethod
    def _resolve_card_brand(cls, payment_obj):
        # edited by glg
        # Brand kartu diprioritaskan dari jenis_kartu; fallback ke jenis_edc untuk data legacy.
        return cls._safe_text(
            getattr(payment_obj, "jenis_kartu", "")
            or getattr(payment_obj, "jenis_edc", "")
            or "",
            "",
        )

    def apply_single_payment(
        self,
        *,
        transaksi_data_dict,
        hasil_pembayaran,
        extract_diskon_customer_callback,
        resolve_bank_rekening_callback,
    ):
        updated = dict(transaksi_data_dict or {})
        total_harus_dibayar = self._to_non_negative_money_int(
            getattr(hasil_pembayaran, "total_harus_dibayar", updated.get("transaksi_nilai", 0)),
            default=updated.get("transaksi_nilai", 0),
        )
        diskon_tambahan_persen = self._to_percent_float(
            getattr(
                hasil_pembayaran,
                "diskon_tambahan_persen",
                getattr(hasil_pembayaran, "diskon_member_persen", 0.0),
            ),
            0.0,
        )
        diskon_tambahan_nilai = self._to_non_negative_money_int(
            getattr(hasil_pembayaran, "diskon_tambahan_nilai", getattr(hasil_pembayaran, "diskon_rp", 0)),
            default=0,
        )
        diskon_customer_nilai = self._to_non_negative_money_int(
            extract_diskon_customer_callback(updated.get("diskon_log", "")),
            default=0,
        )

        updated["transaksi_nilai"] = int(total_harus_dibayar)
        updated["diskon_persen"] = float(max(0.0, diskon_tambahan_persen))
        updated["tambahan_nilai"] = int(diskon_tambahan_nilai)
        updated["diskon_nilai"] = int(diskon_customer_nilai + diskon_tambahan_nilai)
        updated["settlement_id"] = getattr(hasil_pembayaran, "settlement_id", 1)
        updated["bank_id"] = getattr(hasil_pembayaran, "bank_id", 101)
        updated["bank_nama"] = getattr(hasil_pembayaran, "bank_nama", "Tunai")

        transaksi_dibayar = self._to_non_negative_money_int(
            getattr(hasil_pembayaran, "jumlah_bayar", getattr(hasil_pembayaran, "jumlah_dibayar", 0)),
            default=0,
        )
        transaksi_dibayar_return = self._to_non_negative_money_int(
            getattr(hasil_pembayaran, "kembalian", getattr(hasil_pembayaran, "kembali", 0)),
            default=0,
        )
        updated["transaksi_dibayar"] = int(transaksi_dibayar)
        updated["transaksi_dibayar_return"] = int(transaksi_dibayar_return)
        updated["jumlah_bayar"] = int(transaksi_dibayar)
        updated["kembalian"] = int(transaksi_dibayar_return)

        metode_text = getattr(hasil_pembayaran, "metode", "Tunai")
        metode_norm = self._safe_text(metode_text, "tunai").lower()
        if metode_norm == "tunai":
            pembayaran_non_tunai = 0
        else:
            pembayaran_non_tunai = int(transaksi_dibayar)
        pembayaran_tunai = max(0, int(transaksi_dibayar) - int(pembayaran_non_tunai))
        updated["pembayaran_non_tunai"] = int(pembayaran_non_tunai)
        updated["pembayaran_tunai"] = int(pembayaran_tunai)
        updated["pembayaran_sys"] = metode_norm
        updated["pembayaran"] = self._safe_text(updated.get("bank_nama"), self._safe_text(metode_text, "-"))

        updated["bank_from"] = self._resolve_card_brand(hasil_pembayaran)
        updated["kartu_nomer"] = self._mask_card_last4(
            getattr(hasil_pembayaran, "no_kartu", "")
            or getattr(hasil_pembayaran, "kartu", "")
            or ""
        )
        updated["nomer_ep"] = self._safe_text(getattr(hasil_pembayaran, "approval_code", ""), "")

        rekening_id, rekening_nama = resolve_bank_rekening_callback(
            bank_id=updated.get("bank_id"),
            cabang_id=updated.get("cabang_id"),
            is_tunai=(metode_norm == "tunai"),
        )
        updated["bank_rekening_id"] = rekening_id
        updated["bank_rekening_nama"] = rekening_nama
        updated["rekening"] = rekening_nama
        return updated, metode_text

    def apply_multi_payment(
        self,
        *,
        transaksi_data_dict,
        payment_list,
        extract_diskon_customer_callback,
        resolve_bank_rekening_callback,
    ):
        updated = dict(transaksi_data_dict or {})
        payments = list(payment_list or [])
        if not payments:
            return updated, "", 0, 0

        total_dibayar = sum(
            self._to_non_negative_money_int(getattr(p, "jumlah_dibayar", 0), default=0)
            for p in payments
        )
        total_kembalian = self._to_non_negative_money_int(getattr(payments[-1], "kembali", 0), default=0)
        total_tunai = sum(
            self._to_non_negative_money_int(getattr(p, "jumlah_dibayar", 0), default=0)
            for p in payments
            if self._safe_text(getattr(p, "metode", ""), "").lower() == "tunai"
        )

        first_payment = payments[0]
        diskon_tambahan_persen = self._to_percent_float(
            getattr(
                first_payment,
                "diskon_tambahan_persen",
                getattr(first_payment, "diskon_member_persen", 0.0),
            ),
            0.0,
        )
        diskon_tambahan_nilai = self._to_non_negative_money_int(
            getattr(first_payment, "diskon_tambahan_nilai", getattr(first_payment, "diskon_rp", 0)),
            default=0,
        )
        diskon_customer_nilai = self._to_non_negative_money_int(
            extract_diskon_customer_callback(updated.get("diskon_log", "")),
            default=0,
        )
        total_harus = self._to_non_negative_money_int(
            getattr(first_payment, "total_harus_dibayar", updated.get("transaksi_nilai", 0)),
            default=updated.get("transaksi_nilai", 0),
        )

        updated["transaksi_nilai"] = int(total_harus)
        updated["diskon_persen"] = float(max(0.0, diskon_tambahan_persen))
        updated["tambahan_nilai"] = int(diskon_tambahan_nilai)
        updated["diskon_nilai"] = int(diskon_customer_nilai + diskon_tambahan_nilai)
        updated["settlement_id"] = getattr(first_payment, "settlement_id", 1)
        updated["bank_id"] = getattr(first_payment, "bank_id", 101)

        metode_list = [self._safe_text(getattr(p, "metode", ""), "-") for p in payments]
        metode_text = " + ".join(metode_list)
        updated["bank_nama"] = f"Multi: {metode_text}"
        updated["transaksi_dibayar"] = int(total_dibayar)
        updated["transaksi_dibayar_return"] = int(total_kembalian)
        updated["jumlah_bayar"] = int(total_dibayar)
        updated["kembalian"] = int(total_kembalian)
        non_tunai = max(0, int(total_dibayar) - int(total_tunai))
        updated["pembayaran_non_tunai"] = int(non_tunai)
        updated["pembayaran_tunai"] = int(max(0, total_tunai))
        updated["pembayaran_sys"] = "multi"
        parts = []
        for pay in payments:
            metode_item = self._safe_text(getattr(pay, "metode", ""), "-").lower()
            nilai_item = self._to_non_negative_money_int(getattr(pay, "jumlah_dibayar", 0), default=0)
            parts.append(f"{metode_item}:{int(nilai_item)}")
        updated["pembayaran"] = "|".join(parts)

        non_tunai_payment = None
        for pay in payments:
            if self._safe_text(getattr(pay, "metode", ""), "").lower() != "tunai":
                non_tunai_payment = pay
                break
        source_payment = non_tunai_payment or first_payment
        updated["bank_from"] = self._resolve_card_brand(source_payment)
        updated["kartu_nomer"] = self._mask_card_last4(
            getattr(source_payment, "no_kartu", "")
            or getattr(source_payment, "kartu", "")
            or ""
        )
        updated["nomer_ep"] = self._safe_text(getattr(source_payment, "approval_code", ""), "")

        rekening_id, rekening_nama = resolve_bank_rekening_callback(
            bank_id=updated.get("bank_id"),
            cabang_id=updated.get("cabang_id"),
            is_tunai=(non_tunai <= 0),
        )
        updated["bank_rekening_id"] = rekening_id
        updated["bank_rekening_nama"] = rekening_nama if non_tunai <= 0 else f"Multi: {rekening_nama}"
        updated["rekening"] = updated["bank_rekening_nama"]
        return updated, metode_text, int(total_dibayar), int(total_kembalian)
