from decimal import Decimal, InvalidOperation, ROUND_HALF_UP


class PembayaranVoucherService:
    def __init__(self, parse_rupiah_callback):
        self._parse_rupiah = parse_rupiah_callback
        self._state = {"code": "", "saldo": 0.0, "amount": 0.0}
        self._consumed = False

    # edited by glg
    def _to_decimal(self, value, default="0"):
        raw = value
        if isinstance(raw, str):
            raw = raw.strip().replace(",", ".")
        if raw in (None, ""):
            raw = default
        try:
            return Decimal(str(raw))
        except (InvalidOperation, ValueError, TypeError):
            return Decimal(str(default))

    def get_state(self):
        return dict(self._state)

    def get_code(self):
        return str(self._state.get("code", "") or "")

    def get_saldo(self):
        return float(self._state.get("saldo", 0) or 0.0)

    def get_amount(self):
        return float(self._state.get("amount", 0) or 0.0)

    def set_state(self, code, saldo, amount):
        saldo_dec = max(Decimal("0"), self._to_decimal(saldo, "0"))
        amount_dec = max(Decimal("0"), self._to_decimal(amount, "0"))
        self._state = {
            "code": str(code or "").strip(),
            "saldo": float(saldo_dec),
            "amount": float(amount_dec),
        }
        self._consumed = False
        return self.get_state()

    def reset_state(self):
        return self.set_state("", 0.0, 0.0)

    def format_amount_input(self, amount):
        amount_dec = max(Decimal("0"), self._to_decimal(amount, "0"))
        rounded = amount_dec.quantize(Decimal("1"), rounding=ROUND_HALF_UP)
        return str(int(rounded))

    def parse_amount_text(self, text):
        try:
            parsed = self._parse_rupiah(text or "0")
        except (TypeError, ValueError, ArithmeticError):
            parsed = 0
        parsed_dec = max(Decimal("0"), self._to_decimal(parsed, "0"))
        return float(parsed_dec)

    def clamp_amount_to_saldo(self, amount):
        safe_amount_dec = max(Decimal("0"), self._to_decimal(amount, "0"))
        saldo_dec = max(Decimal("0"), self._to_decimal(self.get_saldo(), "0"))
        if safe_amount_dec > saldo_dec:
            return float(saldo_dec)
        return float(safe_amount_dec)

    def apply_to_total(self, total_nilai):
        total_dec = max(Decimal("0"), self._to_decimal(total_nilai, "0"))
        code = self.get_code()
        if not code:
            return float(total_dec), 0.0, self.get_amount()
        saldo_dec = max(Decimal("0"), self._to_decimal(self.get_saldo(), "0"))
        amount_dec = max(Decimal("0"), self._to_decimal(self.get_amount(), "0"))
        if amount_dec <= Decimal("0") or saldo_dec <= Decimal("0"):
            return float(total_dec), 0.0, float(amount_dec)
        used_dec = min(amount_dec, saldo_dec, total_dec)
        total_after_dec = max(Decimal("0"), total_dec - used_dec)
        used = float(used_dec)
        return float(total_after_dec), used, used

    def consume_for_result(self):
        code = self.get_code()
        amount_dec = max(Decimal("0"), self._to_decimal(self.get_amount(), "0"))
        if not code or amount_dec <= Decimal("0") or self._consumed:
            return "", 0.0
        self._consumed = True
        return code, float(amount_dec)

    def validate_before_payment(self, voucher_ui_locked, cek_voucher_callback):
        code = self.get_code()
        amount_dec = max(Decimal("0"), self._to_decimal(self.get_amount(), "0"))
        if voucher_ui_locked:
            if code:
                return False, "Voucher dinonaktifkan untuk kasir.", None
            return True, "", None
        if not code:
            return True, "", None
        voucher = cek_voucher_callback(code)
        if not voucher:
            return False, "Voucher tidak valid atau saldo habis.", None
        saldo_dec = max(Decimal("0"), self._to_decimal((voucher or {}).get("saldo"), "0"))
        if amount_dec <= Decimal("0"):
            return False, "Nominal voucher belum diisi.", float(saldo_dec)
        if amount_dec > saldo_dec:
            return False, "Saldo voucher tidak mencukupi.", float(saldo_dec)
        return True, "", float(saldo_dec)
