# edited by glg
from datetime import date

from pypos.modules.penjualan.controllers.return_controller import ReturnController


class _ReturnModelStub:
    def __init__(self, rows=None):
        self.rows = list(rows or [])
        self.insert_calls = []

    def search_transaksi_master(self, keyword="", start_date=None, end_date=None, limit=200):
        _ = (keyword, start_date, end_date, limit)
        return list(self.rows)

    def load_transaksi_detail(self, transaksi_id):
        _ = transaksi_id
        return []

    def insert_return(
        self,
        transaksi_id,
        items,
        jenis_return="partial",
        refund_method="cash",
        trace_id="",
    ):
        self.insert_calls.append(
            {
                "transaksi_id": str(transaksi_id),
                "jenis_return": str(jenis_return),
                "refund_method": str(refund_method),
                "items_len": len(items or []),
                "trace_id": str(trace_id or ""),
            }
        )
        return "RTR-TEST-001"


class _SettlementModelStub:
    def __init__(self, lock_map=None):
        self.lock_map = {int(k): bool(v) for k, v in (lock_map or {}).items()}
        self.calls = []

    def get_settlement_lock_map(self, transaksi_ids):
        ids = [int(x) for x in (transaksi_ids or [])]
        self.calls.append(ids)
        return {trx_id: bool(self.lock_map.get(trx_id, False)) for trx_id in ids}


# edited by glg
class _SettlementModelErrorStub:
    def __init__(self):
        self.calls = []

    def get_settlement_lock_map(self, transaksi_ids):
        ids = [int(x) for x in (transaksi_ids or [])]
        self.calls.append(ids)
        raise RuntimeError("db_error")


class _PrinterSettingsStub:
    pass


def _build_controller(rows, lock_map):
    model = _ReturnModelStub(rows=rows)
    settlement_model = _SettlementModelStub(lock_map=lock_map)
    controller = ReturnController(
        model=model,
        settlement_model=settlement_model,
        printer_settings_controller=_PrinterSettingsStub(),
    )
    controller.get_allowed_date_range = lambda: (date.today(), date.today())
    controller.penjualan_config_service.get_return_search_limit = lambda default=200: 200
    lock_calls = []

    def _fake_lock_map(transaksi_ids):
        ids = [int(x) for x in (transaksi_ids or [])]
        lock_calls.append(ids)
        normalized = {int(k): bool(v) for k, v in (lock_map or {}).items()}
        return {trx_id: bool(normalized.get(trx_id, False)) for trx_id in ids}

    controller.settlement_lock_service.get_lock_map = _fake_lock_map
    controller._lock_calls = lock_calls
    return controller, model, settlement_model


def test_search_transaksi_master_hanya_tampilkan_belum_settle():
    rows = [
        {"id": 101, "nomer": "INV-101", "customers_nama": "A", "transaksi_nilai": 10000},
        {"id": 102, "nomer": "INV-102", "customers_nama": "B", "transaksi_nilai": 20000},
    ]
    controller, _model, settlement_model = _build_controller(
        rows=rows,
        lock_map={101: False, 102: True},
    )

    result = controller.search_transaksi_master(keyword="")
    nomor = {str(row.get("nomer") or "") for row in result}

    assert "INV-101" in nomor
    assert "INV-102" not in nomor
    assert controller._lock_calls


def test_proses_return_ditolak_jika_transaksi_sudah_settle():
    rows = [{"id": 202, "nomer": "INV-202", "customers_nama": "C", "transaksi_nilai": 50000}]
    controller, model, _settlement_model = _build_controller(
        rows=rows,
        lock_map={202: True},
    )

    result = controller.proses_return(
        transaksi_id="202",
        selected_items=[
            {
                "produk_id": "P-1",
                "produk_nama": "Produk 1",
                "qty_return": 1,
                "harga": 10000,
                "jenis_return": "partial",
            }
        ],
        refund_method="cash",
        jenis_return="partial",
    )

    assert result.get("ok") is False
    assert "sudah disettle" in str(result.get("message") or "").lower()
    assert not model.insert_calls


def test_proses_return_lolos_jika_transaksi_belum_settle():
    rows = [{"id": 303, "nomer": "INV-303", "customers_nama": "D", "transaksi_nilai": 50000}]
    controller, model, _settlement_model = _build_controller(
        rows=rows,
        lock_map={303: False},
    )

    result = controller.proses_return(
        transaksi_id="303",
        selected_items=[
            {
                "produk_id": "P-1",
                "produk_nama": "Produk 1",
                "qty_return": 2,
                "harga": 15000,
                "jenis_return": "partial",
            }
        ],
        refund_method="voucher",
        jenis_return="partial",
    )

    assert result.get("ok") is True
    assert str(result.get("kode_return") or "").startswith("RTR-TEST")
    assert str(result.get("trace_id") or "").startswith("return-")
    assert model.insert_calls
    assert str(model.insert_calls[0].get("trace_id") or "").startswith("return-")


# edited by glg
def test_proses_return_fail_safe_saat_lock_map_error():
    rows = [{"id": 404, "nomer": "INV-404", "customers_nama": "X", "transaksi_nilai": 50000}]
    model = _ReturnModelStub(rows=rows)
    settlement_model = _SettlementModelErrorStub()
    controller = ReturnController(
        model=model,
        settlement_model=settlement_model,
        printer_settings_controller=_PrinterSettingsStub(),
    )
    controller.get_allowed_date_range = lambda: (date.today(), date.today())
    controller.penjualan_config_service.get_return_search_limit = lambda default=200: 200
    def _raise_lock_error(_ids):
        raise RuntimeError("db_error")
    controller.settlement_lock_service.get_lock_map = _raise_lock_error

    result = controller.proses_return(
        transaksi_id="404",
        selected_items=[
            {
                "produk_id": "P-1",
                "produk_nama": "Produk 1",
                "qty_return": 1,
                "harga": 10000,
                "jenis_return": "partial",
            }
        ],
        refund_method="cash",
        jenis_return="partial",
    )

    assert result.get("ok") is False
    assert "sudah disettle" in str(result.get("message") or "").lower()
    assert not model.insert_calls
