# edited by glg
import logging
import sqlite3
from typing import Any, Dict, List


class TransactionExportLegacyRowsBuilderService:
    """
    Builder service untuk payload legacy rows transaksi/return.
    Dipisahkan dari hotspot use-case utama agar tanggung jawab lebih kecil.
    """

    def __init__(self, export_service, logger=None):
        self._svc = export_service
        self._logger = logger or logging.getLogger(__name__)
    def build_legacy_return_rows_payload(
        self,
        *,
        return_rows: List[Dict[str, Any]],
        batch_end: str,
    ) -> List[Dict[str, Any]]:
        svc = self._svc
        rows = [dict(row) for row in (return_rows or []) if isinstance(row, dict)]
        if not rows:
            return []

        config = svc._read_config()
        device_context = svc._get_export_device_context()
        defaults = svc._get_legacy_defaults_service().build_defaults(
            config=config,
            device_context=device_context,
            machine_fallback=str(svc._get_device_id() or "").strip(),
            as_positive_int_fn=svc._as_positive_int,
        )
        machine_fallback = str(defaults.get("machine_id") or "").strip()
        default_cabang_id = svc._as_positive_int(defaults.get("cabang_id"), 0)
        default_cabang_nama = str(defaults.get("cabang_nama") or "").strip()
        default_print_operator = str(defaults.get("default_print_operator") or "system").strip() or "system"
        default_kontainer_size = str(defaults.get("default_kontainer_size") or "none").strip() or "none"
        default_ekspedisi_status = str(defaults.get("default_ekspedisi_status") or "none").strip() or "none"

        return_ids = svc._parse_int_list([svc._to_int(row.get("id"), 0) for row in rows])
        return_detail_rows = svc._fetch_detail_return_rows_for_return_ids(return_ids)
        detail_by_return: Dict[int, List[Dict[str, Any]]] = {}
        produk_ids: List[int] = []
        for detail in return_detail_rows or []:
            if not isinstance(detail, dict):
                continue
            return_id = svc._to_int(detail.get("return_id"), 0)
            if return_id <= 0:
                continue
            detail_by_return.setdefault(return_id, []).append(detail)
            produk_id = svc._to_int(detail.get("produk_id"), 0)
            if produk_id > 0:
                produk_ids.append(produk_id)
        produk_map = svc._fetch_produk_snapshot(produk_ids)

        cancellation_transaksi_ids = svc._parse_int_list(
            [
                svc._to_int(row.get("transaksi_id"), 0)
                for row in rows
                if str((row or {}).get("event_source") or "").strip().lower() == "cancellation"
            ]
        )
        cancellation_detail_by_transaksi: Dict[int, List[Dict[str, Any]]] = {}
        cancellation_produk_ids: List[int] = []
        if cancellation_transaksi_ids:
            cancellation_transaksi_data_rows = svc._fetch_transaksi_data_by_transaksi_ids(
                cancellation_transaksi_ids,
                limit=0,
            )
            for detail in cancellation_transaksi_data_rows or []:
                if not isinstance(detail, dict):
                    continue
                transaksi_id = svc._to_int(detail.get("transaksi_id"), 0)
                if transaksi_id <= 0:
                    continue
                cancellation_detail_by_transaksi.setdefault(transaksi_id, []).append(detail)
                produk_id = svc._to_int(detail.get("produk_id"), 0)
                if produk_id > 0:
                    cancellation_produk_ids.append(produk_id)
        if cancellation_produk_ids:
            produk_map.update(svc._fetch_produk_snapshot(cancellation_produk_ids))

        out_rows: List[Dict[str, Any]] = []
        cancellation_event_keys_in = set()
        cancellation_event_keys_out = set()
        for row in rows:
            event_source = str(row.get("event_source") or "return").strip().lower()
            return_id = svc._to_int(row.get("id"), 0)
            transaksi_id = svc._to_int(row.get("transaksi_id"), 0)
            if event_source == "cancellation":
                cancellation_event_keys_in.add((int(max(0, return_id)), int(max(0, transaksi_id))))
            if return_id <= 0:
                if event_source == "cancellation":
                    self._logger.warning(
                        "[DEBUG EXPORT] skip cancellation event: id return tidak valid id=%s transaksi_id=%s",
                        row.get("id"),
                        transaksi_id,
                    )
                continue
            original_nomer = str(row.get("transaksi_nomer") or "").strip()
            return_dtime = svc._normalize_dtime_value(row.get("tanggal_return"), batch_end)
            fulldate_value = str(return_dtime[:10])
            date_parts = fulldate_value.split("-")
            if len(date_parts) == 3:
                thn, bln, tgl = date_parts
            else:
                thn, bln, tgl = ("0000", "00", "00")

            cabang_id = svc._as_positive_int(
                row.get("transaksi_cabang_id"),
                svc._as_positive_int(default_cabang_id, 0),
            )
            cabang_nama = str(
                row.get("transaksi_cabang_nama")
                or default_cabang_nama
                or ""
            ).strip()
            if not cabang_nama and cabang_id > 0:
                cabang_nama = str(svc._lookup_cabang_nama_by_id(cabang_id) or "").strip()

            gudang_id = svc._to_int(row.get("transaksi_gudang_id"), 0)
            kasir_id = svc._to_int(row.get("transaksi_oleh_id"), 0)
            kasir_nama = str(row.get("transaksi_oleh_nama") or "").strip() or default_print_operator
            machine_id = str(row.get("transaksi_machine_id") or machine_fallback).strip() or machine_fallback
            cpu_info = str(row.get("transaksi_cpu_info") or "no-cpuINFO").strip() or "no-cpuINFO"
            com_info = str(row.get("transaksi_com_info") or "no-comINFO").strip() or "no-comINFO"

            refund_method = str(row.get("refund_method") or "cash").strip().lower() or "cash"
            refund_amount = max(
                0,
                svc._to_int(
                    row.get("refund_amount"),
                    svc._to_int(row.get("total_return"), 0),
                ),
            )
            detail_rows_for_return = detail_by_return.get(return_id, [])
            cancellation_transaksi_data_rows: List[Dict[str, Any]] = []
            if event_source == "cancellation" and not detail_rows_for_return and transaksi_id > 0:
                cancellation_transaksi_data_rows = cancellation_detail_by_transaksi.get(transaksi_id, [])
            if refund_amount <= 0:
                if cancellation_transaksi_data_rows:
                    for detail in cancellation_transaksi_data_rows:
                        if not isinstance(detail, dict):
                            continue
                        qty = max(
                            0,
                            svc._to_int(
                                detail.get("valid_qty"),
                                svc._to_int(detail.get("produk_ord_jml"), 0),
                            ),
                        )
                        harga = max(0, svc._to_int(detail.get("produk_ord_hrg"), 0))
                        refund_amount += max(0, qty * harga)
                else:
                    for detail in detail_rows_for_return:
                        if not isinstance(detail, dict):
                            continue
                        refund_amount += max(
                            0,
                            svc._to_int(
                                detail.get("subtotal"),
                                svc._to_int(detail.get("harga"), 0) * svc._to_int(detail.get("jumlah"), 0),
                            ),
                        )
            if cancellation_transaksi_data_rows:
                items_payload = svc._build_legacy_return_items_payload_from_transaksi_data(
                    cancellation_transaksi_data_rows,
                    produk_map,
                )
            else:
                items_payload = svc._build_legacy_return_items_payload(
                    detail_rows_for_return,
                    produk_map,
                )
            if refund_amount <= 0:
                for item in items_payload.values():
                    if not isinstance(item, dict):
                        continue
                    refund_amount += max(0, svc._to_int(item.get("subtotal"), 0))
            refund_amount = int(max(0, refund_amount))
            produk_return_list = svc._build_produk_return_list_from_items(items_payload)

            nomer_return = str(row.get("kode_voucher") or "").strip()
            if not nomer_return:
                nomer_suffix = original_nomer or str(transaksi_id or return_id)
                if event_source == "cancellation":
                    nomer_return = f"CAN-{return_id}-{nomer_suffix}"
                else:
                    nomer_return = f"RET-{return_id}-{nomer_suffix}"

            account_id = 108
            account_name = "Tunai"
            metode_pembayaran = "cash"
            bank_nama = ""
            if refund_method in {"voucher"}:
                account_id = 110
                account_name = "Voucher"
                metode_pembayaran = "voucher"
            elif refund_method in {"non cash", "non_tunai", "credit card", "credit", "debit", "debit card", "qris"}:
                account_id = 110
                account_name = "Non Tunai"
                metode_pembayaran = refund_method
            payment_payload = [
                {
                    "id_penjualan": int(return_id),
                    "nomer": nomer_return,
                    "metode_pembayaran": metode_pembayaran,
                    "nilai_bruto": int(refund_amount),
                    "nilai_tagihan": int(refund_amount),
                    "nilai_bayar": int(refund_amount),
                    "nilai_terbayar": int(refund_amount),
                    "nilai_kurang_bayar": 0,
                    "nilai_lebih_bayar": 0,
                    "diskon_persen": 0.0,
                    "diskon_rp": 0,
                    "diskon_tambahan_persen": 0.0,
                    "diskon_tambahan_nilai": 0,
                    "diskon_produk": 0,
                    "diskon_member": 0,
                    "total_diskon_produk": 0,
                    "total_diskon_member": 0,
                    "total_diskon_tambahan": 0,
                    "bank_id": "",
                    "bank_nama": bank_nama,
                    "account_id": str(account_id),
                    "account_nama": account_name,
                    "bank_tujuan": None,
                    "rekening_tujuan": None,
                    "nomor_kartu": None,
                    "nama_pemilik_kartu": None,
                    "nomor_referensi": str(transaksi_id or ""),
                    "cabang_id": str(svc._to_int(cabang_id, 0)),
                    "gudang_id": int(gudang_id),
                    "kasir_id": str(kasir_id),
                    "dtime": return_dtime,
                    "is_return": 1,
                    "return_notes": str(row.get("keterangan") or ""),
                    "return_methode": refund_method,
                }
            ]

            out_rows.append(
                {
                    "items": items_payload,
                    "free_items": {},
                    "thn": str(thn),
                    "bln": str(bln),
                    "tgl": str(tgl),
                    "id_penjualan": int(return_id),
                    "jenis_kode": "982",
                    "jenis_label": "return",
                    "nomer": nomer_return,
                    "kasirID": str(kasir_id),
                    "cabangID": str(int(max(0, cabang_id))),
                    "gudangID": int(gudang_id),
                    "machineID": str(machine_id),
                    "cpu_info": str(cpu_info),
                    "com_info": str(com_info),
                    "print_oleh_nama": str(kasir_nama),
                    "kontainer_size": str(default_kontainer_size),
                    "ekspedisi_status": str(default_ekspedisi_status),
                    "dtime": return_dtime,
                    "fulldate": fulldate_value,
                    "reference_id": int(max(0, transaksi_id)),
                    "reference_nomer": str(original_nomer or transaksi_id or ""),
                    "referenceID": int(max(0, transaksi_id)),
                    "referenceJenis": 582,
                    "referenceNomer": str(original_nomer or transaksi_id or ""),
                    "return_penjualan": 1,
                    "penjualan_return": int(refund_amount),
                    "harga_jual": int(refund_amount),
                    "diskon_rp": 0,
                    "diskon_persen": 0.0,
                    "diskon_tambahan_persen": 0.0,
                    "diskon_tambahan_nilai": 0,
                    "diskon_produk": 0,
                    "diskon_member": 0,
                    "total_diskon_produk": 0,
                    "total_diskon_member": 0,
                    "total_diskon_tambahan": 0,
                    "bayar": int(refund_amount),
                    "kembali": 0,
                    "tagihan": int(refund_amount),
                    "pihakLevel": "0",
                    "pihakLevelNama": "",
                    "customerID": str(svc._to_int(row.get("customer_id"), 1)),
                    "cashback_persen": 0,
                    "cashback_nilai": 0,
                    "point_transaksi": 0,
                    "point_nilai": 0,
                    "point_set": 0,
                    "payment": payment_payload,
                    # Metadata return eksplisit agar server dapat melacak relasi nota/transaksi asal.
                    "return_id": int(return_id),
                    "return_transaksi_id": int(max(0, transaksi_id)),
                    "return_transaksi_nomer": str(original_nomer or ""),
                    "return_jenis": str(row.get("jenis_return") or ("full" if event_source == "cancellation" else "partial")),
                    "return_refund_method": refund_method,
                    "return_refund_amount": int(refund_amount),
                    "return_keterangan": str(row.get("keterangan") or ""),
                    "return_item_count": int(max(len(detail_rows_for_return), len(produk_return_list))),
                    "produk_return_list": produk_return_list,
                    "event_source": event_source,
                }
            )
            if event_source == "cancellation":
                cancellation_event_keys_out.add((int(max(0, return_id)), int(max(0, transaksi_id))))
        if cancellation_event_keys_in and cancellation_event_keys_out != cancellation_event_keys_in:
            missing_events = sorted(list(cancellation_event_keys_in - cancellation_event_keys_out))[:10]
            self._logger.warning(
                "[DEBUG EXPORT] cancellation event gagal dibentuk menjadi row 982. expected=%s built=%s missing=%s",
                len(cancellation_event_keys_in),
                len(cancellation_event_keys_out),
                missing_events,
            )
        out_rows.sort(
            key=lambda item: (
                str(item.get("dtime") or ""),
                int(svc._to_int(item.get("id_penjualan"), 0)),
            )
        )
        return out_rows

    def build_legacy_transaksi_rows(
        self,
        *,
        transaksi_rows: List[Dict[str, Any]],
        batch_end: str,
        extra_return_rows: List[Dict[str, Any]] = None,
    ) -> List[Dict[str, Any]]:
        svc = self._svc
        if not transaksi_rows:
            # Dukung batch pembatalan untuk transaksi lama (invoice sudah pernah export):
            # kirim baris return/cancellation saja tanpa kirim ulang invoice.
            only_return_rows: List[Dict[str, Any]] = []
            for return_row in (extra_return_rows or []):
                if not isinstance(return_row, dict):
                    continue
                only_return_rows.append(dict(return_row))
            if len(only_return_rows) > 1:
                only_return_rows.sort(
                    key=lambda item: (
                        str((item or {}).get("dtime") or ""),
                        int(svc._to_int((item or {}).get("id_penjualan"), 0)),
                    )
                )
            return only_return_rows
        config = svc._read_config()

        transaksi_ids = []
        for row in transaksi_rows:
            if not isinstance(row, dict):
                continue
            parsed_id = svc._to_int(row.get("id"), 0)
            if parsed_id > 0:
                transaksi_ids.append(parsed_id)
        if not transaksi_ids:
            return []

        detail_rows = svc._fetch_transaksi_data_by_transaksi_ids(transaksi_ids, 0)
        details_by_transaksi: Dict[int, List[Dict[str, Any]]] = {}
        produk_ids = []
        for detail in detail_rows or []:
            if not isinstance(detail, dict):
                continue
            trx_id = svc._to_int(detail.get("transaksi_id"), 0)
            if trx_id <= 0:
                continue
            details_by_transaksi.setdefault(trx_id, []).append(detail)
            produk_id = svc._to_int(detail.get("produk_id"), 0)
            if produk_id > 0:
                produk_ids.append(produk_id)

        produk_map = svc._fetch_produk_snapshot(produk_ids)
        diskon_map = svc._fetch_diskon_free_snapshot(produk_ids)
        device_context = svc._get_export_device_context()
        defaults = svc._get_legacy_defaults_service().build_defaults(
            config=config,
            device_context=device_context,
            machine_fallback=str(svc._get_device_id() or "").strip(),
            as_positive_int_fn=svc._as_positive_int,
        )
        machine_fallback = str(defaults.get("machine_id") or "").strip()
        default_print_operator = str(defaults.get("default_print_operator") or "system").strip() or "system"
        default_kontainer_size = str(defaults.get("default_kontainer_size") or "none").strip() or "none"
        default_ekspedisi_status = str(defaults.get("default_ekspedisi_status") or "none").strip() or "none"

        legacy_rows = []
        lookup_conn = None
        lookup_cursor = None
        try:
            lookup_conn = svc._connect()
            lookup_cursor = lookup_conn.cursor()
        except sqlite3.Error:
            lookup_cursor = None

        for transaksi in transaksi_rows:
            if not isinstance(transaksi, dict):
                continue
            id_penjualan = svc._to_int(transaksi.get("id"), 0)
            if id_penjualan <= 0:
                continue

            dtime_value = svc._normalize_dtime_value(transaksi.get("dtime"), batch_end)
            fulldate_value = str(transaksi.get("fulldate") or dtime_value[:10]).strip() or dtime_value[:10]
            date_parts = fulldate_value.split("-")
            if len(date_parts) == 3:
                thn, bln, tgl = date_parts
            else:
                thn, bln, tgl = ("0000", "00", "00")

            diskon_log = svc._parse_semicolon_log(transaksi.get("diskon_log"))
            cashback_nilai = svc._to_int(diskon_log.get("cashback"), 0)
            point_nilai = svc._to_int(diskon_log.get("point"), 0)

            harga_jual = svc._resolve_transaksi_bruto(transaksi)
            tagihan = svc._resolve_transaksi_tagihan(transaksi, harga_jual)
            bayar = svc._to_int(transaksi.get("transaksi_dibayar"), tagihan)
            kembali = svc._to_int(transaksi.get("transaksi_dibayar_return"), max(0, bayar - tagihan))
            customer_id = str(svc._to_int(transaksi.get("customers_id"), 1))
            pihak_level = "1" if customer_id not in {"0", "1"} else "0"
            print_oleh_nama = str(transaksi.get("print_oleh_nama") or "").strip()
            if not print_oleh_nama:
                print_oleh_nama = str(transaksi.get("oleh_nama") or "").strip()
            if not print_oleh_nama:
                print_oleh_nama = default_print_operator
            kontainer_size = str(transaksi.get("kontainer_size") or "").strip() or default_kontainer_size
            ekspedisi_status = str(transaksi.get("ekspedisi_status") or "").strip() or default_ekspedisi_status
            # cabang_id wajib berasal dari record transaksi tersimpan (tanpa fallback per_cabang_device).
            resolved_cabang_id = svc._as_positive_int(transaksi.get("cabang_id"), 0)
            # Kontrak legacy endpoint: cabangID wajib terisi (>0), tidak boleh kosong.
            if resolved_cabang_id <= 0:
                ref_nomer = str(transaksi.get("nomer") or id_penjualan)
                raise ValueError(
                    f"invalid_zero_components:cabang_id table=transaksi nomer={ref_nomer} machine_id={machine_fallback}"
                )
            resolved_gudang_id = int(svc._to_int(transaksi.get("gudang_id"), 0))
            if lookup_cursor is not None:
                cabang_default_gudang_id, _ = svc._resolve_cabang_default_gudang_for_backfill(
                    lookup_cursor,
                    resolved_cabang_id,
                )
                # Prioritas gudang default cabang untuk konsistensi lintas POS.
                if cabang_default_gudang_id != 0:
                    resolved_gudang_id = int(cabang_default_gudang_id)

            trx_detail_rows = details_by_transaksi.get(id_penjualan, [])
            items_payload = svc._build_legacy_items_payload(trx_detail_rows, produk_map)
            # Total diskon produk dihitung dari detail item (akumulasi semua produk berdiskon),
            # bukan dari diskon_nilai master agar tidak tercampur diskon tambahan/member.
            diskon_produk = svc._sum_diskon_produk_from_detail_rows(trx_detail_rows)
            discount_source = dict(transaksi or {})
            if "_diskon_tambahan_total" not in discount_source:
                discount_source["_diskon_tambahan_total"] = svc._to_int(
                    transaksi.get("tambahan_nilai"),
                    svc._to_int(transaksi.get("add_disc"), 0),
                )
            discount_components = svc._resolve_transaksi_discount_components(
                discount_source,
                diskon_produk_hint=diskon_produk,
                bruto_hint=harga_jual,
                tagihan_hint=tagihan,
            )
            diskon_produk = int(max(0, discount_components.get("diskon_produk", 0)))
            diskon_member = int(max(0, discount_components.get("diskon_member", 0)))
            diskon_tambahan_nilai = int(max(0, discount_components.get("diskon_tambahan", 0)))
            if harga_jual > 0 and diskon_tambahan_nilai > 0:
                diskon_tambahan_persen = (float(diskon_tambahan_nilai) / float(harga_jual)) * 100.0
            else:
                diskon_tambahan_persen = 0.0
            free_items_payload = svc._build_legacy_free_items_payload(
                trx_detail_rows,
                produk_map,
                diskon_map,
                dtime_value,
            )
            payment_payload = svc._build_legacy_payment_payload(
                {
                    **transaksi,
                    "gudang_id": resolved_gudang_id,
                    "_diskon_produk_total": int(max(0, diskon_produk)),
                    "_diskon_member_total": int(max(0, diskon_member)),
                    "_diskon_tambahan_total": int(max(0, diskon_tambahan_nilai)),
                },
                id_penjualan,
                resolved_cabang_id,
            )
            # Pastikan payload invoice tetap membawa marker pembatalan agar
            # endpoint dapat memproses invoice batal dalam batch yang sama.
            is_cancelled = int(1 if svc._is_cancelled_transaksi_row(transaksi) else 0)
            cancel_status = svc._to_int(transaksi.get("status_cancel"), 0)
            if cancel_status <= 0 and is_cancelled == 1:
                cancel_status = 1
            cancel_dtime_raw = str(transaksi.get("cancel_dtime") or "").strip()
            cancel_dtime = (
                svc._normalize_dtime_value(cancel_dtime_raw, "")
                if cancel_dtime_raw
                else ""
            )
            cancel_id = svc._to_int(transaksi.get("cancel_id"), 0)
            cancel_name = str(transaksi.get("cancel_name") or "").strip()
            cancel_transaksi_id = svc._to_int(transaksi.get("cancel_transaksi_id"), 0)
            cancel_transaksi_nomer = str(transaksi.get("cancel_transaksi_nomer") or "").strip()

            legacy_rows.append(
                {
                    "items": items_payload,
                    "free_items": free_items_payload,
                    "thn": str(thn),
                    "bln": str(bln),
                    "tgl": str(tgl),
                    "id_penjualan": int(id_penjualan),
                    "jenis_kode": svc._map_jenis_kode(transaksi),
                    "jenis_label": str(transaksi.get("jenis_label") or "invoice"),
                    "nomer": str(transaksi.get("nomer") or ""),
                    "kasirID": str(svc._to_int(transaksi.get("oleh_id"), 0)),
                    "cabangID": str(int(resolved_cabang_id)),
                    "gudangID": int(resolved_gudang_id),
                    "machineID": str(transaksi.get("machine_id") or machine_fallback),
                    "cpu_info": str(transaksi.get("cpu_info") or "no-cpuINFO"),
                    "com_info": str(transaksi.get("com_info") or "no-comINFO"),
                    "print_oleh_nama": str(print_oleh_nama),
                    "kontainer_size": str(kontainer_size),
                    "ekspedisi_status": str(ekspedisi_status),
                    "dtime": dtime_value,
                    "fulldate": fulldate_value,
                    "referenceID": int(
                        svc._to_int(
                            transaksi.get("reference_id"),
                            svc._to_int(transaksi.get("referensi_id"), 0),
                        )
                    ),
                    "referenceJenis": int(
                        svc._to_int(
                            transaksi.get("reference_jenis"),
                            svc._to_int(transaksi.get("referensi_jenis"), 0),
                        )
                    ),
                    "referenceNomer": str(
                        transaksi.get("reference_nomer")
                        or transaksi.get("referensi_nomer")
                        or 0
                    ),
                    "is_cancelled": int(max(0, is_cancelled)),
                    "status_cancel": int(max(0, cancel_status)),
                    "cancel_dtime": str(cancel_dtime),
                    "cancel_id": int(max(0, cancel_id)),
                    "cancel_name": str(cancel_name),
                    "cancel_transaksi_id": int(max(0, cancel_transaksi_id)),
                    "cancel_transaksi_nomer": str(cancel_transaksi_nomer),
                    "trash": int(max(0, svc._to_int(transaksi.get("trash"), 0))),
                    "return_penjualan": int(
                        max(
                            svc._to_int(transaksi.get("returned"), 0),
                            svc._to_int(transaksi.get("returns"), 0),
                        )
                    ),
                    "harga_jual": int(max(0, harga_jual)),
                    # Komponen diskon eksplisit untuk kontrak server export transaksi.
                    "diskon_rp": int(max(0, diskon_tambahan_nilai)),
                    "diskon_persen": float(max(0.0, diskon_tambahan_persen)),
                    "diskon_tambahan_persen": float(max(0.0, diskon_tambahan_persen)),
                    "diskon_tambahan_nilai": int(max(0, diskon_tambahan_nilai)),
                    "diskon_produk": int(max(0, diskon_produk)),
                    "diskon_member": int(max(0, diskon_member)),
                    "total_diskon_produk": int(max(0, diskon_produk)),
                    "total_diskon_member": int(max(0, diskon_member)),
                    "total_diskon_tambahan": int(max(0, diskon_tambahan_nilai)),
                    "bayar": int(max(0, bayar)),
                    "kembali": int(max(0, kembali)),
                    "tagihan": int(max(0, tagihan)),
                    "pihakLevel": pihak_level,
                    "pihakLevelNama": str(transaksi.get("pihakLevelNama") or ""),
                    "customerID": customer_id,
                    "cashback_persen": int(0),
                    "cashback_nilai": int(max(0, cashback_nilai)),
                    "point_transaksi": int(
                        svc._to_int(
                            transaksi.get("point_transaksi"),
                            svc._to_int(diskon_log.get("point"), 0),
                        )
                    ),
                    "point_nilai": int(max(0, point_nilai)),
                    "point_set": "1.00" if point_nilai > 0 else 0,
                    "payment": payment_payload,
                }
            )
        try:
            if lookup_conn is not None:
                lookup_conn.close()
        except sqlite3.Error:
            self._logger.debug("[DEBUG EXPORT] close lookup_conn gagal")
        for return_row in (extra_return_rows or []):
            if not isinstance(return_row, dict):
                continue
            legacy_rows.append(dict(return_row))
        if len(legacy_rows) > 1:
            legacy_rows.sort(
                key=lambda item: (
                    str((item or {}).get("dtime") or ""),
                    int(svc._to_int((item or {}).get("id_penjualan"), 0)),
                )
            )
        return legacy_rows
