# edited by glg
import sqlite3


class SettlementLockMapService:
    """
    Domain service lock-map settlement untuk guard mutasi transaksi.
    """

    def __init__(self, model):
        self.model = model

    def _extract_settled_ids_and_map_rows_from_history_rows(self, history_rows):
        m = self.model
        settled_ids = set()
        map_rows = []
        for row in history_rows or []:
            raw_blob = None
            history_id = 0
            try:
                if isinstance(row, dict):
                    history_id = m._as_positive_int(row.get("id"), 0)
                    raw_blob = row.get("data_transaksi_id")
                else:
                    history_id = m._as_positive_int(row["id"], 0)
                    raw_blob = row["data_transaksi_id"]
            except (TypeError, ValueError, KeyError, IndexError):
                raw_blob = None
            for transaksi_id in m._extract_transaksi_ids_blob(raw_blob):
                trx_id = int(transaksi_id)
                settled_ids.add(trx_id)
                if history_id > 0:
                    map_rows.append((history_id, trx_id))
        return settled_ids, map_rows

    def _backfill_settlement_map_for_pending_ids(self, cursor, pending_ids, chunk_size=1000, max_scan_rows=None):
        m = self.model
        unresolved = {int(v) for v in (pending_ids or []) if m._as_positive_int(v, 0) > 0}
        if not unresolved:
            return set(), []

        scan_offset = 0
        scanned_rows = 0
        resolved = set()
        map_rows = []
        while unresolved:
            if max_scan_rows is not None and scanned_rows >= int(max_scan_rows):
                break
            cursor.execute(
                """
                SELECT id, data_transaksi_id
                FROM settlement_history
                WHERE COALESCE(data_transaksi_id, '') <> ''
                ORDER BY id DESC
                LIMIT ? OFFSET ?
                """,
                (int(chunk_size), int(scan_offset)),
            )
            rows = cursor.fetchall() or []
            if not rows:
                break
            scanned_now = len(rows)
            scanned_rows += scanned_now
            scan_offset += scanned_now

            settled_ids, extracted_map_rows = self._extract_settled_ids_and_map_rows_from_history_rows(rows)
            if settled_ids:
                intersects = unresolved.intersection(settled_ids)
                if intersects:
                    resolved.update(intersects)
                    unresolved -= intersects
            if extracted_map_rows:
                map_rows.extend(extracted_map_rows)

        return resolved, map_rows

    def get_settlement_lock_map(self, transaksi_ids):
        m = self.model
        normalized_ids = m._normalize_transaksi_ids(transaksi_ids)
        if not normalized_ids:
            return {}

        lock_map = {int(trx_id): False for trx_id in normalized_ids}
        conn = m.connect()
        conn.row_factory = sqlite3.Row
        cursor = conn.cursor()
        map_cache_dirty = False
        try:
            m._ensure_settlement_history_map_table(cursor)
            placeholders = ",".join(["?"] * len(normalized_ids))
            cursor.execute(
                f"""
                SELECT id, COALESCE(settlement_id, 1) AS settlement_id
                FROM transaksi
                WHERE id IN ({placeholders})
                  AND IFNULL(trash, 0) = 0
                """,
                normalized_ids,
            )
            rows = cursor.fetchall() or []
            for row in rows:
                transaksi_id = m._as_positive_int(row["id"], 0)
                if transaksi_id <= 0:
                    continue
                settlement_id = int(row["settlement_id"] or 1)
                if settlement_id == 0:
                    lock_map[transaksi_id] = True

            pending_ids = [trx_id for trx_id, is_locked in lock_map.items() if not is_locked]
            if pending_ids:
                map_placeholders = ",".join(["?"] * len(pending_ids))
                cursor.execute(
                    f"""
                    SELECT DISTINCT transaksi_id
                    FROM settlement_history_transaksi_map
                    WHERE transaksi_id IN ({map_placeholders})
                    """,
                    pending_ids,
                )
                for row in cursor.fetchall() or []:
                    mapped_id = m._as_positive_int(row["transaksi_id"], 0)
                    if mapped_id > 0 and mapped_id in lock_map:
                        lock_map[mapped_id] = True

            pending_ids = [trx_id for trx_id, is_locked in lock_map.items() if not is_locked]
            if pending_ids:
                settled_ids, map_rows = self._backfill_settlement_map_for_pending_ids(
                    cursor=cursor,
                    pending_ids=pending_ids,
                    chunk_size=1000,
                    max_scan_rows=None,
                )
                for trx_id in pending_ids:
                    if trx_id in settled_ids:
                        lock_map[trx_id] = True
                if map_rows:
                    cursor.executemany(
                        """
                        INSERT OR IGNORE INTO settlement_history_transaksi_map
                        (settlement_history_id, transaksi_id)
                        VALUES (?, ?)
                        """,
                        map_rows,
                    )
                    map_cache_dirty = True
        except sqlite3.Error as exc:
            m.log_warning(f"[SETTLEMENT_LOCK_DB_ERROR] Gagal membaca lock map settlement: {exc}")
            for trx_id in list(lock_map.keys()):
                lock_map[trx_id] = True
        except (TypeError, ValueError, KeyError) as exc:
            m.log_warning(f"[SETTLEMENT_LOCK_DATA_ERROR] Data lock map settlement tidak valid: {exc}")
            for trx_id in list(lock_map.keys()):
                lock_map[trx_id] = True
        finally:
            if map_cache_dirty:
                try:
                    conn.commit()
                except sqlite3.Error as exc:
                    m.log_warning(f"[SETTLEMENT_LOCK_COMMIT_ERROR] Gagal commit cache map settlement history: {exc}")
            conn.close()
        return lock_map

    def is_transaksi_locked_for_mutation(self, transaksi_id):
        m = self.model
        normalized_ids = m._normalize_transaksi_ids([transaksi_id])
        if not normalized_ids:
            return False
        lock_map = self.get_settlement_lock_map(normalized_ids)
        return bool(lock_map.get(int(normalized_ids[0]), False))

