import sqlite3
import tempfile
import unittest
from pathlib import Path

from pypos.modules.penjualan.services.pembayaran_result_service import (
    PembayaranResultService,
)
from pypos.modules.penjualan.services.pembayaran_voucher_service import (
    PembayaranVoucherService,
)


class PembayaranSupportServicesTests(unittest.TestCase):
    def test_result_service_fallback_bank_saat_tabel_bank_tidak_ada(self):
        with tempfile.TemporaryDirectory() as td:
            db_path = str(Path(td) / "payment_no_bank.db")
            service = PembayaranResultService(db_path=db_path)
            result = service.create_result(
                mode="debit",
                jumlah_dibayar="1000",
                total_dibayar="1000",
                diskon_persen="0",
                jenis_kartu="BCA",
            )
            self.assertEqual(int(result.bank_id), 104)
            self.assertEqual(str(result.bank_nama), "Debit BCA")

    def test_result_service_decimal_input_comma_tetap_akurat(self):
        with tempfile.TemporaryDirectory() as td:
            db_path = str(Path(td) / "payment_bank.db")
            conn = sqlite3.connect(db_path)
            cur = conn.cursor()
            cur.execute(
                """
                CREATE TABLE IF NOT EXISTS bank (
                    id INTEGER PRIMARY KEY,
                    nama TEXT
                )
                """
            )
            cur.execute("INSERT INTO bank (id, nama) VALUES (10, 'Tunai')")
            conn.commit()
            conn.close()

            service = PembayaranResultService(db_path=db_path)
            result = service.create_result(
                mode="tunai",
                jumlah_dibayar="1000,5",
                total_dibayar="999,5",
                diskon_persen="2,5",
                diskon_nilai="10,25",
                voucher_amount="5,5",
            )
            self.assertEqual(int(result.bank_id), 10)
            self.assertAlmostEqual(float(result.kembalian), 1.0, places=6)
            self.assertAlmostEqual(float(result.diskon_member_persen), 2.5, places=6)
            self.assertAlmostEqual(float(result.diskon_rp), 10.25, places=6)
            self.assertAlmostEqual(float(result.voucher_amount), 5.5, places=6)

    def test_voucher_service_decimal_clamp_dan_consume_once(self):
        parse_rupiah = lambda text: str(text or "0").replace(".", "").replace(",", ".")
        service = PembayaranVoucherService(parse_rupiah)
        service.set_state("VCR-001", "100,30", "40,20")

        self.assertAlmostEqual(service.clamp_amount_to_saldo("999,99"), 100.30, places=6)
        total_after, used, stored = service.apply_to_total("50,10")
        self.assertAlmostEqual(total_after, 9.9, places=6)
        self.assertAlmostEqual(used, 40.2, places=6)
        self.assertAlmostEqual(stored, 40.2, places=6)

        code1, amount1 = service.consume_for_result()
        code2, amount2 = service.consume_for_result()
        self.assertEqual(code1, "VCR-001")
        self.assertAlmostEqual(amount1, 40.2, places=6)
        self.assertEqual(code2, "")
        self.assertEqual(amount2, 0.0)

    def test_voucher_service_validate_before_payment(self):
        parse_rupiah = lambda text: str(text or "0").replace(".", "").replace(",", ".")
        service = PembayaranVoucherService(parse_rupiah)
        service.set_state("VCR-002", "0", "10,50")

        ok, msg, saldo = service.validate_before_payment(
            voucher_ui_locked=False,
            cek_voucher_callback=lambda _code: {"saldo": "10,50"},
        )
        self.assertTrue(bool(ok))
        self.assertEqual(msg, "")
        self.assertAlmostEqual(float(saldo), 10.5, places=6)

        service.set_state("VCR-002", "0", "11")
        ok2, msg2, saldo2 = service.validate_before_payment(
            voucher_ui_locked=False,
            cek_voucher_callback=lambda _code: {"saldo": "10,50"},
        )
        self.assertFalse(bool(ok2))
        self.assertIn("Saldo voucher tidak mencukupi", msg2)
        self.assertAlmostEqual(float(saldo2), 10.5, places=6)


if __name__ == "__main__":
    unittest.main()
