# edited by glg
from typing import Any, Callable, Dict, Tuple


class TransaksiAutocompleteAsyncUseCaseService:
    """
    Use-case service untuk orkestrasi async autocomplete.
    Fokus pada payload flow dan guard stale request.
    """

    @staticmethod
    def build_fetch_payload(
        *,
        keyword: str,
        limit: int,
        lookup_started: Any,
        fetch_callback: Callable[[str, int], Tuple[Any, Any]],
    ) -> Dict[str, Any]:
        payload: Dict[str, Any] = {
            "keyword": str(keyword or ""),
            "barang_list": [],
            "mapping": {},
            "lookup_started": lookup_started,
            "ok": True,
            "error": "",
        }
        try:
            barang_list, mapping = fetch_callback(str(keyword or ""), int(limit or 0))
            payload["barang_list"] = list(barang_list or [])
            payload["mapping"] = mapping if isinstance(mapping, dict) else {}
        except (TypeError, ValueError, RuntimeError, KeyError, AttributeError) as exc:
            payload["ok"] = False
            payload["error"] = str(exc)
        return payload

    @staticmethod
    def submit_async_fetch(
        *,
        owner_id: int,
        request_id: int,
        keyword: str,
        limit: int,
        lookup_started: Any,
        fetch_callback: Callable[[str, int], Tuple[Any, Any]],
        emit_callback: Callable[[int, Dict[str, Any]], None] = None,
        on_ready_callback: Callable[[int, Dict[str, Any]], None] = None,
        submit_task_callback: Callable[[str, Callable[[], None]], Any] = None,
    ) -> Any:
        def _worker():
            payload = TransaksiAutocompleteAsyncUseCaseService.build_fetch_payload(
                keyword=keyword,
                limit=limit,
                lookup_started=lookup_started,
                fetch_callback=fetch_callback,
            )
            if callable(emit_callback):
                emit_callback(int(request_id or 0), payload)
                return
            if callable(on_ready_callback):
                on_ready_callback(int(request_id or 0), payload)

        key = f"transaksi-autocomplete:{int(owner_id or 0)}"
        if not callable(submit_task_callback):
            return None
        return submit_task_callback(key, _worker)

    @staticmethod
    def apply_ready_payload(
        *,
        request_id: int,
        payload: Dict[str, Any],
        is_latest_request_callback: Callable[[int], bool],
        set_mapping_callback: Callable[[Dict[str, Any]], None],
        set_autocomplete_callback: Callable[[Any], None],
        record_perf_callback: Callable[..., None],
        log_warning_callback: Callable[[str], None] = None,
    ) -> bool:
        if not callable(is_latest_request_callback):
            return False
        if not bool(is_latest_request_callback(int(request_id or 0))):
            return False

        data = payload if isinstance(payload, dict) else {}
        normalized = str(data.get("keyword") or "")
        barang_list = list(data.get("barang_list") or [])
        mapping = data.get("mapping") if isinstance(data.get("mapping"), dict) else {}

        if callable(set_mapping_callback):
            set_mapping_callback(mapping)
        if callable(set_autocomplete_callback):
            set_autocomplete_callback(barang_list)

        lookup_started = data.get("lookup_started")
        context = f"keyword_len={len(normalized)} result={len(barang_list)}"
        if lookup_started is not None and callable(record_perf_callback):
            record_perf_callback(
                "autocomplete_lookup",
                lookup_started,
                threshold_ms=80,
                context=context,
            )
            if normalized:
                record_perf_callback(
                    "first_search",
                    lookup_started,
                    threshold_ms=None,
                    context=context,
                    once_key="first_search",
                )

        if not bool(data.get("ok")) and callable(log_warning_callback):
            log_warning_callback(f"Autocomplete barang gagal: {str(data.get('error') or 'unknown_error')}")
        return True
