from decimal import Decimal, InvalidOperation, ROUND_DOWN


class PembayaranCalculationService:
    # edited by glg
    def _to_decimal(self, value, default="0"):
        try:
            return Decimal(str(value if value is not None else default))
        except (InvalidOperation, ValueError, TypeError):
            return Decimal(str(default))

    # edited by glg
    def _to_money_int(self, value, default="0"):
        return int(
            self._to_decimal(value, default).quantize(Decimal("1"), rounding=ROUND_DOWN)
        )

    # edited by glg
    def calculate(
        self,
        total_awal,
        diskon_persen,
        ppn_persen,
        diskon_nilai=0.0,
        prefer_diskon_nilai=False,
        voucher_amount=0.0,
        ppn_mode="include",
    ):
        total = max(Decimal("0"), self._to_decimal(total_awal, "0"))
        diskon = max(Decimal("0"), self._to_decimal(diskon_persen, "0"))
        diskon_nominal = max(Decimal("0"), self._to_decimal(diskon_nilai, "0"))
        ppn = max(Decimal("0"), self._to_decimal(ppn_persen, "0"))
        voucher = max(Decimal("0"), self._to_decimal(voucher_amount, "0"))
        mode = str(ppn_mode or "include").strip().lower()
        if mode not in {"include", "exclude"}:
            mode = "include"

        if bool(prefer_diskon_nilai):
            # edited by glg
            # Mode nominal: persen harus diabaikan total, termasuk saat nominal = 0.
            diskon_nilai_final = min(total, max(Decimal("0"), diskon_nominal))
            diskon_persen_final = (
                (diskon_nilai_final / total) * Decimal("100")
                if total > 0
                else Decimal("0")
            )
        else:
            # edited by glg
            # Mode persen: pertahankan perilaku existing (floor nominal rupiah).
            diskon_nilai_calc = (total * diskon) / Decimal("100")
            diskon_nilai_final = diskon_nilai_calc.quantize(Decimal("1"), rounding=ROUND_DOWN)
            diskon_persen_final = diskon
        dasar_ppn = max(Decimal("0"), total - diskon_nilai_final)
        if mode == "exclude":
            ppn_nilai = dasar_ppn * (ppn / Decimal("100"))
            total_sebelum_voucher = dasar_ppn + ppn_nilai
        else:
            # edited by glg
            # Mode include: PPN informatif, tidak menambah total.
            ppn_nilai = (
                dasar_ppn * (ppn / (Decimal("100") + ppn))
                if ppn > 0
                else Decimal("0")
            )
            total_sebelum_voucher = dasar_ppn
        voucher_terpakai = min(voucher, total_sebelum_voucher)
        total_harus_dibayar = max(Decimal("0"), total_sebelum_voucher - voucher_terpakai)

        return {
            # edited by glg
            # Nilai uang dikunci ke integer rupiah untuk menghindari drift float.
            "diskon_nilai": self._to_money_int(diskon_nilai_final),
            "diskon_persen_efektif": float(max(Decimal("0"), diskon_persen_final)),
            "dasar_ppn": self._to_money_int(dasar_ppn),
            "ppn_nilai": self._to_money_int(ppn_nilai),
            "ppn_mode": mode,
            "total_sebelum_voucher": self._to_money_int(total_sebelum_voucher),
            "voucher_terpakai": self._to_money_int(voucher_terpakai),
            "total_harus_dibayar": self._to_money_int(total_harus_dibayar),
        }
