import { computed, ref, type ComputedRef, type Ref } from "vue";
import { apiErrorContextFromUnknown } from "@/api/client";
import type { ApiError, ApiResult, ErrorDiagnostic } from "@/types";

type TableLoaderResult<T> = T[] | ApiResult<T[]>;

export function useTableLoader<T>(loader: () => Promise<TableLoaderResult<T>>): { rows: Ref<T[]>; loading: Ref<boolean>; error: Ref<string | null>; apiError: Ref<ApiError | null>; diagnostic: Ref<ErrorDiagnostic | null>; empty: ComputedRef<boolean>; reload: () => Promise<void> } {
  const rows = ref([]) as Ref<T[]>;
  const loading = ref(false);
  const error = ref<string | null>(null);
  const apiError = ref<ApiError | null>(null);
  const diagnostic = ref<ErrorDiagnostic | null>(null);
  const empty = computed(() => !loading.value && rows.value.length === 0);

  async function reload(): Promise<void> {
    loading.value = true;
    error.value = null;
    apiError.value = null;
    diagnostic.value = null;
    try {
      const result = await loader();
      if (isApiResult(result)) {
        if (!result.ok) throw result;
        rows.value = result.data ?? [];
      } else {
        rows.value = result;
      }
    } catch (err) {
      const context = apiErrorContextFromUnknown(err);
      error.value = context.error;
      apiError.value = context.apiError;
      diagnostic.value = context.diagnostic;
    } finally {
      loading.value = false;
    }
  }

  return { rows, loading, error, apiError, diagnostic, empty, reload };
}

function isApiResult<T>(value: TableLoaderResult<T>): value is ApiResult<T[]> {
  return Boolean(value && !Array.isArray(value) && typeof value === "object" && "ok" in value && "status" in value);
}
