internal/dashboard/frontend/src/composables/useLogEntries.ts

import { useDebounceFn } from "@vueuse/core";
import {
  buildQueryStringFromState,
  useFilterStore,
  type FilterState,
} from "src/store";
import { type HoneypotEvent } from "src/types";
import { filterEvents } from "utils/filters";
import { URL } from "utils/utils";
import { computed, onMounted, reactive, ref, watch } from "vue";
import { useWebSocket } from "./useWebSocket";

export function useLogEntries(options?: {
  filterState?: FilterState;
  onLoaded?: (events: HoneypotEvent[]) => void;
}) {
  const filterStore = useFilterStore();
  const filterState = reactive(options?.filterState || filterStore.state);

  const queryString = computed(() => {
    const state = { ...filterState };
    return buildQueryStringFromState(state);
  });

  const filterString = computed(() => new URLSearchParams(queryString.value));

  const entries = ref<HoneypotEvent[]>([]);
  const total = ref(0);
  const loading = ref(false);
  const error = ref<Error | null>(null);

  const { connect, disconnect, connectionStatus } = useWebSocket(
    (batch: HoneypotEvent[]) => {
      const filtered = filterEvents(batch, filterState);

      if (filtered.length > 0) {
        entries.value = [...filtered, ...entries.value];
        total.value += filtered.length;

        // Keep entries list manageable.
        const maxEntries = Math.max(filterState.limit, 1000);
        if (entries.value.length > maxEntries) {
          entries.value = entries.value.slice(0, maxEntries);
        }
      }
    },
  );

  async function fetchLogs() {
    loading.value = true;
    error.value = null;
    try {
      const response = await fetch(`${URL()}/api/events?${queryString.value}`);
      const data = await response.json();
      entries.value = data.events ?? [];
      total.value = data.total ?? 0;
      options?.onLoaded?.(data.events ?? []);
    } catch (err) {
      error.value = err instanceof Error ? err : new Error(String(err));
      entries.value = [];
      total.value = 0;
    } finally {
      loading.value = false;
    }
  }

  const debouncedFetchLogs = useDebounceFn(fetchLogs, 250);

  onMounted(() => {
    fetchLogs();
  });

  watch(
    () => queryString.value,
    () => {
      debouncedFetchLogs();
    },
  );

  return {
    entries,
    total,
    loading,
    error,
    fetchLogs,
    filterString,
    connectionStatus,
    connect,
    disconnect,
  };
}