import type {
  GraphMeta,
  SelectionQueryMeta,
  SelectionQueryResult,
  SelectionTriple,
  SelectionTripleResult,
  SelectionTripleTerm,
} from "./types";

function numberArray(value: unknown): number[] {
  if (!Array.isArray(value)) return [];
  const out: number[] = [];
  for (const item of value) {
    if (typeof item === "number") out.push(item);
  }
  return out;
}

function tripleTerm(value: unknown): SelectionTripleTerm | null {
  if (!value || typeof value !== "object") return null;
  const record = value as Record<string, unknown>;
  if (typeof record.type !== "string" || typeof record.value !== "string") return null;
  return {
    type: record.type,
    value: record.value,
    lang: typeof record.lang === "string" ? record.lang : undefined,
  };
}

function tripleArray(value: unknown): SelectionTriple[] {
  if (!Array.isArray(value)) return [];
  const out: SelectionTriple[] = [];
  for (const item of value) {
    if (!item || typeof item !== "object") continue;
    const record = item as Record<string, unknown>;
    const s = tripleTerm(record.s);
    const p = tripleTerm(record.p);
    const o = tripleTerm(record.o);
    if (!s || !p || !o) continue;
    out.push({
      s,
      p,
      o,
      subject_id: typeof record.subject_id === "number" ? record.subject_id : undefined,
      predicate_id: typeof record.predicate_id === "number" ? record.predicate_id : undefined,
      object_id: typeof record.object_id === "number" ? record.object_id : undefined,
    });
  }
  return out;
}

export async function fetchSelectionQueries(signal?: AbortSignal): Promise<SelectionQueryMeta[]> {
  const res = await fetch("/api/selection_queries", { signal });
  if (!res.ok) throw new Error(`GET /api/selection_queries failed: ${res.status}`);
  const data = await res.json();
  return Array.isArray(data) ? (data as SelectionQueryMeta[]) : [];
}

export async function runSelectionQuery(
  queryId: string,
  selectedIds: number[],
  graphMeta: GraphMeta | null,
  signal: AbortSignal
): Promise<SelectionQueryResult> {
  const body = {
    query_id: queryId,
    selected_ids: selectedIds,
    node_limit: typeof graphMeta?.node_limit === "number" ? graphMeta.node_limit : undefined,
    edge_limit: typeof graphMeta?.edge_limit === "number" ? graphMeta.edge_limit : undefined,
    graph_query_id: typeof graphMeta?.graph_query_id === "string" ? graphMeta.graph_query_id : undefined,
  };

  const res = await fetch("/api/selection_query", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify(body),
    signal,
  });
  if (!res.ok) throw new Error(`POST /api/selection_query failed: ${res.status}`);
  const data = await res.json();

  return {
    queryId: typeof data?.query_id === "string" ? data.query_id : queryId,
    selectedIds: numberArray(data?.selected_ids),
    neighborIds: numberArray(data?.neighbor_ids),
  };
}

export async function runSelectionTripleQuery(
  queryId: string,
  selectedIds: number[],
  graphMeta: GraphMeta | null,
  signal: AbortSignal
): Promise<SelectionTripleResult> {
  const body = {
    query_id: queryId,
    selected_ids: selectedIds,
    node_limit: typeof graphMeta?.node_limit === "number" ? graphMeta.node_limit : undefined,
    edge_limit: typeof graphMeta?.edge_limit === "number" ? graphMeta.edge_limit : undefined,
    graph_query_id: typeof graphMeta?.graph_query_id === "string" ? graphMeta.graph_query_id : undefined,
  };

  const res = await fetch("/api/selection_triples", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify(body),
    signal,
  });
  if (!res.ok) throw new Error(`POST /api/selection_triples failed: ${res.status}`);
  const data = await res.json();

  return {
    queryId: typeof data?.query_id === "string" ? data.query_id : queryId,
    selectedIds: numberArray(data?.selected_ids),
    triples: tripleArray(data?.triples),
  };
}
