'use client';

/**
 * React Query hooks for Sibyl API.
 */

import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useEffect, useState } from 'react';

import type {
  AIModelKind,
  CodeExampleParams,
  CodeExampleResponse,
  CreateNoteRequest,
  EntityCreate,
  EntityUpdate,
  EpicStatus,
  LLMProviderName,
  LLMSurface,
  RAGSearchParams,
  RAGSearchResponse,
  TaskPriority,
  TaskStatus,
  UpdateLLMSurfaceRequest,
  UpdateSettingsRequest,
} from './api';
import { api } from './api';
import { TIMING } from './constants';
import { type ConnectionStatus, wsClient } from './websocket';

// =============================================================================
// Query Keys
// =============================================================================

export const queryKeys = {
  auth: {
    me: ['auth', 'me'] as const,
  },
  orgs: {
    list: ['orgs', 'list'] as const,
    detail: (slug: string) => ['orgs', 'detail', slug] as const,
    members: (slug: string) => ['orgs', 'members', slug] as const,
  },
  security: {
    sessions: ['security', 'sessions'] as const,
    apiKeys: ['security', 'apiKeys'] as const,
    connections: ['security', 'connections'] as const,
  },
  preferences: ['preferences'] as const,
  entities: {
    all: ['entities'] as const,
    list: (params?: Parameters<typeof api.entities.list>[0]) =>
      ['entities', 'list', params] as const,
    detail: (id: string, params?: Parameters<typeof api.entities.get>[1]) =>
      ['entities', 'detail', id, params] as const,
  },
  rawCaptures: {
    all: ['raw-captures'] as const,
    list: (params?: Parameters<typeof api.rawCaptures.list>[0]) =>
      ['raw-captures', 'list', params] as const,
    detail: (id: string) => ['raw-captures', 'detail', id] as const,
  },
  memory: {
    all: ['memory'] as const,
    audit: (params?: Parameters<typeof api.memory.audit.list>[0]) =>
      ['memory', 'audit', params] as const,
    spaces: ['memory', 'spaces'] as const,
    sourceAdapters: ['memory', 'source-adapters'] as const,
    sourceImport: (importId: string) => ['memory', 'source-import', importId] as const,
    sourceInspect: (sourceId: string) => ['memory', 'source-inspect', sourceId] as const,
  },
  session: {
    all: ['session'] as const,
    bundle: (params?: Parameters<typeof api.session.bundle>[0]) =>
      ['session', 'bundle', params] as const,
  },
  search: {
    all: ['search'] as const,
    query: (params: Parameters<typeof api.search.query>[0]) => ['search', 'query', params] as const,
  },
  rag: {
    all: ['rag'] as const,
    search: (params: RAGSearchParams) => ['rag', 'search', params] as const,
    hybrid: (params: RAGSearchParams) => ['rag', 'hybrid', params] as const,
    code: (params: CodeExampleParams) => ['rag', 'code', params] as const,
    page: (documentId: string) => ['rag', 'page', documentId] as const,
    pageEntities: (documentId: string) => ['rag', 'page', documentId, 'entities'] as const,
    pages: (sourceId: string, params?: Record<string, unknown>) =>
      ['rag', 'pages', sourceId, params] as const,
  },
  graph: {
    all: ['graph'] as const,
    full: (params?: Parameters<typeof api.graph.full>[0]) => ['graph', 'full', params] as const,
    subgraph: (params: Parameters<typeof api.graph.subgraph>[0]) =>
      ['graph', 'subgraph', params] as const,
    clusters: (params?: { refresh?: boolean }) => ['graph', 'clusters', params] as const,
    clusterDetail: (clusterId: string) => ['graph', 'cluster', clusterId] as const,
    stats: ['graph', 'stats'] as const,
    hierarchical: (params?: { max_nodes?: number; max_edges?: number; refresh?: boolean }) =>
      ['graph', 'hierarchical', params] as const,
  },
  admin: {
    health: ['admin', 'health'] as const,
    stats: ['admin', 'stats'] as const,
  },
  telemetry: {
    summary: (params?: Parameters<typeof api.telemetry.summary>[0]) =>
      ['telemetry', 'summary', params] as const,
  },
  setup: {
    status: ['setup', 'status'] as const,
    validation: ['setup', 'validation'] as const,
    integration: ['setup', 'integration'] as const,
  },
  settings: {
    all: ['settings'] as const,
    llm: ['settings', 'ai', 'llm'] as const,
    registry: (kind?: AIModelKind) => ['settings', 'ai', 'registry', kind ?? 'all'] as const,
  },
  tasks: {
    all: ['tasks'] as const,
    list: (params?: { project?: string; project_ids?: string[]; status?: TaskStatus }) => {
      const normalized =
        params && (params.project || params.project_ids?.length || params.status)
          ? {
              ...(params.project ? { project: params.project } : {}),
              ...(params.project_ids?.length ? { project_ids: [...params.project_ids] } : {}),
              ...(params.status ? { status: params.status } : {}),
            }
          : undefined;
      return ['tasks', 'list', normalized] as const;
    },
    detail: (id: string) => ['tasks', 'detail', id] as const,
    notes: (id: string) => ['tasks', 'notes', id] as const,
  },
  projects: {
    all: ['projects'] as const,
    list: (includeArchived = false) => ['projects', 'list', { includeArchived }] as const,
    detail: (id: string) => ['projects', 'detail', id] as const,
    members: (id: string) => ['projects', 'members', id] as const,
  },
  epics: {
    all: ['epics'] as const,
    list: (params?: { project?: string; project_ids?: string[]; status?: EpicStatus }) => {
      const normalized =
        params && (params.project || params.project_ids?.length || params.status)
          ? {
              ...(params.project ? { project: params.project } : {}),
              ...(params.project_ids?.length ? { project_ids: [...params.project_ids] } : {}),
              ...(params.status ? { status: params.status } : {}),
            }
          : undefined;
      return ['epics', 'list', normalized] as const;
    },
    detail: (id: string) => ['epics', 'detail', id] as const,
    tasks: (id: string) => ['epics', 'tasks', id] as const,
    progress: (id: string) => ['epics', 'progress', id] as const,
  },
  explore: {
    related: (entityId: string) => ['explore', 'related', entityId] as const,
  },
  sources: {
    all: ['sources'] as const,
    list: ['sources', 'list'] as const,
    detail: (id: string) => ['sources', 'detail', id] as const,
  },
  metrics: {
    org: ['metrics', 'org'] as const,
    projectsSummary: ['metrics', 'projects-summary'] as const,
    project: (id: string) => ['metrics', 'project', id] as const,
  },
  backups: {
    all: ['backups'] as const,
    settings: ['backups', 'settings'] as const,
    list: ['backups', 'list'] as const,
    detail: (id: string) => ['backups', 'detail', id] as const,
    jobStatus: (jobId: string) => ['backups', 'job', jobId] as const,
  },
  jobs: {
    all: ['jobs'] as const,
    list: (params?: Parameters<typeof api.jobs.list>[0]) => ['jobs', 'list', params] as const,
  },
};

/**
 * Invalidate queries based on entity type.
 * Avoids over-invalidation by only targeting relevant query keys.
 */
function invalidateByEntityType(
  queryClient: ReturnType<typeof useQueryClient>,
  entityType: string | undefined,
  entityId?: string,
  options?: { includeStats?: boolean }
) {
  if (options?.includeStats) {
    queryClient.invalidateQueries({ queryKey: queryKeys.admin.stats });
  }

  switch (entityType) {
    case 'task':
      queryClient.invalidateQueries({ queryKey: queryKeys.tasks.all });
      queryClient.invalidateQueries({ queryKey: ['metrics'] });
      if (entityId) {
        queryClient.invalidateQueries({ queryKey: queryKeys.tasks.detail(entityId) });
      }
      break;

    case 'project':
      queryClient.invalidateQueries({ queryKey: queryKeys.projects.all });
      queryClient.invalidateQueries({ queryKey: ['metrics'] });
      if (entityId) {
        queryClient.invalidateQueries({ queryKey: queryKeys.projects.detail(entityId) });
      }
      break;

    case 'source':
      queryClient.invalidateQueries({ queryKey: queryKeys.sources.all });
      if (entityId) {
        queryClient.invalidateQueries({ queryKey: queryKeys.sources.detail(entityId) });
      }
      break;

    default:
      // For knowledge entities (pattern, episode, rule, etc.) - invalidate graph + entities
      queryClient.invalidateQueries({ queryKey: queryKeys.entities.all });
      queryClient.invalidateQueries({ queryKey: queryKeys.graph.all });
      if (entityId) {
        queryClient.invalidateQueries({ queryKey: queryKeys.entities.detail(entityId) });
      }
      break;
  }
}

// =============================================================================
// Auth + Orgs Hooks
// =============================================================================

export function useMe(options?: {
  enabled?: boolean;
  initialData?: import('./api').AuthMeResponse;
}) {
  return useQuery({
    queryKey: queryKeys.auth.me,
    queryFn: () => api.auth.me(),
    enabled: options?.enabled ?? true,
    retry: false,
    staleTime: TIMING.STALE_TIME,
    initialData: options?.initialData,
  });
}

export function useOrgs(options?: {
  enabled?: boolean;
  initialData?: import('./api').OrgListResponse;
}) {
  return useQuery({
    queryKey: queryKeys.orgs.list,
    queryFn: () => api.orgs.list(),
    enabled: options?.enabled ?? true,
    retry: false,
    staleTime: TIMING.STALE_TIME,
    initialData: options?.initialData,
  });
}

export function useSwitchOrg() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (slug: string) => api.orgs.switch(slug),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.auth.me });
      queryClient.invalidateQueries({ queryKey: queryKeys.orgs.list });
      queryClient.invalidateQueries({ queryKey: queryKeys.entities.all });
      queryClient.invalidateQueries({ queryKey: queryKeys.tasks.all });
      queryClient.invalidateQueries({ queryKey: queryKeys.projects.all });
      queryClient.invalidateQueries({ queryKey: queryKeys.graph.all });
    },
  });
}

export function useOrg(slug: string, options?: { enabled?: boolean }) {
  return useQuery({
    queryKey: queryKeys.orgs.detail(slug),
    queryFn: () => api.orgs.get(slug),
    enabled: options?.enabled ?? !!slug,
    retry: false,
    staleTime: TIMING.STALE_TIME,
  });
}

export function useCreateOrg() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (data: import('./api').OrgCreateRequest) => api.orgs.create(data),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.orgs.list });
      queryClient.invalidateQueries({ queryKey: queryKeys.auth.me });
    },
  });
}

export function useUpdateOrg() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({ slug, data }: { slug: string; data: import('./api').OrgUpdateRequest }) =>
      api.orgs.update(slug, data),
    onSuccess: (_data, variables) => {
      queryClient.invalidateQueries({ queryKey: queryKeys.orgs.list });
      queryClient.invalidateQueries({ queryKey: queryKeys.orgs.detail(variables.slug) });
    },
  });
}

export function useDeleteOrg() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (slug: string) => api.orgs.delete(slug),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.orgs.list });
      queryClient.invalidateQueries({ queryKey: queryKeys.auth.me });
    },
  });
}

export function useOrgMembers(slug: string, options?: { enabled?: boolean }) {
  return useQuery({
    queryKey: queryKeys.orgs.members(slug),
    queryFn: () => api.orgs.members.list(slug),
    enabled: options?.enabled ?? !!slug,
    retry: false,
    staleTime: TIMING.STALE_TIME,
  });
}

export function useAddOrgMember() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({ slug, userId, role }: { slug: string; userId: string; role: string }) =>
      api.orgs.members.add(slug, userId, role),
    onSuccess: (_data, variables) => {
      queryClient.invalidateQueries({ queryKey: queryKeys.orgs.members(variables.slug) });
    },
  });
}

export function useUpdateOrgMemberRole() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({ slug, userId, role }: { slug: string; userId: string; role: string }) =>
      api.orgs.members.updateRole(slug, userId, role),
    onSuccess: (_data, variables) => {
      queryClient.invalidateQueries({ queryKey: queryKeys.orgs.members(variables.slug) });
    },
  });
}

export function useRemoveOrgMember() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({ slug, userId }: { slug: string; userId: string }) =>
      api.orgs.members.remove(slug, userId),
    onSuccess: (_data, variables) => {
      queryClient.invalidateQueries({ queryKey: queryKeys.orgs.members(variables.slug) });
    },
  });
}

// =============================================================================
// Security Hooks (Sessions, API Keys, OAuth, Password)
// =============================================================================

export function useSessions() {
  return useQuery({
    queryKey: queryKeys.security.sessions,
    queryFn: () => api.security.sessions.list(),
  });
}

export function useRevokeSession() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (sessionId: string) => api.security.sessions.revoke(sessionId),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.security.sessions });
    },
  });
}

export function useRevokeAllSessions() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: () => api.security.sessions.revokeAll(),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.security.sessions });
    },
  });
}

export function useApiKeys() {
  return useQuery({
    queryKey: queryKeys.security.apiKeys,
    queryFn: () => api.security.apiKeys.list(),
  });
}

export function useCreateApiKey() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (data: import('./api').ApiKeyCreateRequest) => api.security.apiKeys.create(data),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.security.apiKeys });
    },
  });
}

export function useRevokeApiKey() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (keyId: string) => api.security.apiKeys.revoke(keyId),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.security.apiKeys });
    },
  });
}

export function useOAuthConnections() {
  return useQuery({
    queryKey: queryKeys.security.connections,
    queryFn: () => api.security.connections.list(),
  });
}

export function useRemoveOAuthConnection() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (connectionId: string) => api.security.connections.remove(connectionId),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.security.connections });
    },
  });
}

export function useChangePassword() {
  return useMutation({
    mutationFn: (data: import('./api').PasswordChangeRequest) => api.security.changePassword(data),
  });
}

// =============================================================================
// Preferences Hooks
// =============================================================================

export function usePreferences() {
  return useQuery({
    queryKey: queryKeys.preferences,
    queryFn: () => api.preferences.get(),
    staleTime: 5 * TIMING.STALE_TIME,
    refetchOnWindowFocus: false,
  });
}

export function useUpdatePreferences() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (preferences: Partial<import('./api').UserPreferences>) =>
      api.preferences.update(preferences),
    onSuccess: data => {
      queryClient.setQueryData(queryKeys.preferences, data);
    },
  });
}

/**
 * Hook for tracking onboarding checklist progress.
 * Returns current state and methods to mark items complete.
 */
export function useOnboardingProgress() {
  const { data: prefsData, isLoading } = usePreferences();
  const updatePrefs = useUpdatePreferences();

  const checklist = prefsData?.preferences?.onboarding_checklist ?? {};

  const markComplete = (item: keyof import('./api').OnboardingChecklist) => {
    if (checklist[item]) return; // Already complete
    updatePrefs.mutate({
      onboarding_checklist: {
        ...checklist,
        [item]: true,
      },
    });
  };

  const isAllComplete =
    checklist.connected_agent && checklist.added_source && checklist.tried_search;

  return {
    checklist,
    isLoading,
    isAllComplete,
    markComplete,
    markConnectedAgent: () => markComplete('connected_agent'),
    markAddedSource: () => markComplete('added_source'),
    markTriedSearch: () => markComplete('tried_search'),
  };
}

// =============================================================================
// Entity Hooks
// =============================================================================

export function useEntities(
  params?: Parameters<typeof api.entities.list>[0],
  initialData?: import('./api').EntityListResponse
) {
  return useQuery({
    queryKey: queryKeys.entities.list(params),
    queryFn: () => api.entities.list(params),
    initialData,
    staleTime: TIMING.STALE_TIME,
    placeholderData: previousData => previousData,
  });
}

export function useEntity(
  id: string,
  initialData?: import('./api').Entity,
  params?: Parameters<typeof api.entities.get>[1]
) {
  return useQuery({
    queryKey: queryKeys.entities.detail(id, params),
    queryFn: () => api.entities.get(id, params),
    enabled: !!id,
    initialData,
  });
}

export function useRawCaptures(
  params?: Parameters<typeof api.rawCaptures.list>[0],
  options?: { enabled?: boolean; initialData?: import('./api').RawCaptureListResponse }
) {
  return useQuery({
    queryKey: queryKeys.rawCaptures.list(params),
    queryFn: () => api.rawCaptures.list(params),
    enabled: options?.enabled ?? true,
    initialData: options?.initialData,
  });
}

export function useRawCapture(
  id: string,
  options?: { enabled?: boolean; initialData?: import('./api').RawCapture }
) {
  return useQuery({
    queryKey: queryKeys.rawCaptures.detail(id),
    queryFn: () => api.rawCaptures.get(id),
    enabled: (options?.enabled ?? true) && !!id,
    initialData: options?.initialData,
  });
}

export function useSessionBundle(
  params?: Parameters<typeof api.session.bundle>[0],
  options?: { enabled?: boolean; initialData?: import('./api').SessionBundleResponse }
) {
  return useQuery({
    queryKey: queryKeys.session.bundle(params),
    queryFn: () => api.session.bundle(params),
    enabled: options?.enabled ?? true,
    initialData: options?.initialData,
  });
}

export function useUpdateRawCaptureReviewState() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({
      id,
      reviewState,
    }: {
      id: string;
      reviewState: import('./api').RawCaptureReviewState;
    }) => api.rawCaptures.updateReviewState(id, reviewState),
    onSuccess: (data, variables) => {
      queryClient.invalidateQueries({ queryKey: queryKeys.rawCaptures.all });
      queryClient.setQueryData(queryKeys.rawCaptures.detail(variables.id), data);
    },
  });
}

export function useMemoryAudit(
  params?: Parameters<typeof api.memory.audit.list>[0],
  options?: { enabled?: boolean; initialData?: import('./api').MemoryAuditListResponse }
) {
  return useQuery({
    queryKey: queryKeys.memory.audit(params),
    queryFn: () => api.memory.audit.list(params),
    enabled: options?.enabled ?? true,
    initialData: options?.initialData,
  });
}

export function useMemorySpaces(options?: {
  enabled?: boolean;
  initialData?: import('./api').MemorySpaceListResponse;
}) {
  return useQuery({
    queryKey: queryKeys.memory.spaces,
    queryFn: () => api.memory.spaces.list(),
    enabled: options?.enabled ?? true,
    initialData: options?.initialData,
  });
}

export function useMemorySourceImport(
  importId: string,
  options?: { enabled?: boolean; initialData?: import('./api').SourceImportStatusResponse }
) {
  const enabled = (options?.enabled ?? true) && !!importId;
  const wsStatus = useWebSocketStatus(enabled);

  return useQuery({
    queryKey: queryKeys.memory.sourceImport(importId),
    queryFn: () => api.memory.sourceImportStatus(importId),
    enabled,
    initialData: options?.initialData,
    refetchInterval: query => {
      if (wsStatus === 'connected') {
        return false;
      }
      const status = query.state.data?.status;
      return status === 'pending' || status === 'running' ? 2500 : false;
    },
  });
}

export function useSourceImportAdapters(options?: {
  enabled?: boolean;
  initialData?: import('./api').SourceAdapterListResponse;
}) {
  return useQuery({
    queryKey: queryKeys.memory.sourceAdapters,
    queryFn: () => api.sourceImports.adapters(),
    enabled: options?.enabled ?? true,
    initialData: options?.initialData,
  });
}

export function useStartSourceImport() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (request: import('./api').SourceImportStartRequest) =>
      api.sourceImports.start(request),
    onSuccess: data => {
      queryClient.invalidateQueries({ queryKey: queryKeys.memory.all });
      queryClient.invalidateQueries({ queryKey: queryKeys.rawCaptures.all });
      queryClient.setQueryData(queryKeys.memory.sourceImport(data.import_id), data);
    },
  });
}

export function useResumeSourceImport() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({
      importId,
      request,
    }: {
      importId: string;
      request?: import('./api').SourceImportResumeRequest;
    }) => api.sourceImports.resume(importId, request),
    onSuccess: data => {
      queryClient.invalidateQueries({ queryKey: queryKeys.memory.all });
      queryClient.invalidateQueries({ queryKey: queryKeys.rawCaptures.all });
      queryClient.setQueryData(queryKeys.memory.sourceImport(data.import_id), data);
    },
  });
}

export function useCancelSourceImport() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (importId: string) => api.sourceImports.cancel(importId),
    onSuccess: data => {
      queryClient.invalidateQueries({ queryKey: queryKeys.memory.all });
      queryClient.setQueryData(queryKeys.memory.sourceImport(data.import_id), data);
    },
  });
}

export function useMemorySourceInspect(
  sourceId: string,
  options?: { enabled?: boolean; initialData?: import('./api').MemorySourceInspectResponse }
) {
  return useQuery({
    queryKey: queryKeys.memory.sourceInspect(sourceId),
    queryFn: () => api.memory.inspect.get(sourceId),
    enabled: (options?.enabled ?? true) && !!sourceId,
    initialData: options?.initialData,
  });
}

export function useSynthesisPlan() {
  return useMutation({
    mutationFn: (request: import('./api').SynthesisRequest) => api.synthesis.plan(request),
  });
}

export function useSynthesisDraft() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (request: import('./api').SynthesisDraftRequest) => api.synthesis.draft(request),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.memory.all });
      queryClient.invalidateQueries({ queryKey: queryKeys.rawCaptures.all });
    },
  });
}

export function usePreviewMemoryCorrection() {
  return useMutation({
    mutationFn: ({
      sourceId,
      request,
    }: {
      sourceId: string;
      request: import('./api').MemoryCorrectionRequest;
    }) => api.memory.inspect.previewCorrection(sourceId, request),
  });
}

export function useApplyMemoryCorrection() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({
      sourceId,
      request,
    }: {
      sourceId: string;
      request: import('./api').MemoryCorrectionRequest;
    }) => api.memory.inspect.applyCorrection(sourceId, request),
    onSuccess: (_data, variables) => {
      queryClient.invalidateQueries({
        queryKey: queryKeys.memory.sourceInspect(variables.sourceId),
      });
      queryClient.invalidateQueries({ queryKey: queryKeys.memory.all });
      queryClient.invalidateQueries({ queryKey: queryKeys.rawCaptures.all });
    },
  });
}

export function useCreateEntity() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (entity: EntityCreate) => api.entities.create(entity),
    onSuccess: (data, variables) => {
      // Use entity type from response (most accurate) or input
      const entityType = data.entity_type || variables.entity_type;
      invalidateByEntityType(queryClient, entityType, data.id, { includeStats: true });
    },
  });
}

export function useUpdateEntity() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({ id, updates }: { id: string; updates: EntityUpdate }) =>
      api.entities.update(id, updates),
    onSuccess: (data, { id }) => {
      // Use entity type from response
      invalidateByEntityType(queryClient, data.entity_type, id);
    },
  });
}

export function useDeleteEntity() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (id: string) => api.entities.delete(id),
    onSuccess: (_data, id) => {
      // Check cache for entity type before it's removed
      const cachedEntity = queryClient.getQueryData(queryKeys.entities.detail(id)) as
        | { entity_type?: string }
        | undefined;
      const entityType = cachedEntity?.entity_type;
      invalidateByEntityType(queryClient, entityType, id, { includeStats: true });
    },
  });
}

// =============================================================================
// Search Hooks
// =============================================================================

export function useSearch(
  params: Parameters<typeof api.search.query>[0],
  options?: { enabled?: boolean; initialData?: import('./api').SearchResponse }
) {
  return useQuery({
    queryKey: queryKeys.search.query(params),
    queryFn: () => api.search.query(params),
    enabled: (options?.enabled ?? true) && !!params.query,
    initialData: options?.initialData,
  });
}

// =============================================================================
// RAG Search Hooks (Documentation Search)
// =============================================================================

/**
 * Search documentation chunks using vector similarity.
 */
export function useRAGSearch(
  params: RAGSearchParams,
  options?: { enabled?: boolean; initialData?: RAGSearchResponse }
) {
  return useQuery({
    queryKey: queryKeys.rag.search(params),
    queryFn: () => api.rag.search(params),
    enabled: (options?.enabled ?? true) && !!params.query,
    initialData: options?.initialData,
  });
}

/**
 * Hybrid search combining vector similarity and full-text search.
 */
export function useRAGHybridSearch(
  params: RAGSearchParams,
  options?: { enabled?: boolean; initialData?: RAGSearchResponse }
) {
  return useQuery({
    queryKey: queryKeys.rag.hybrid(params),
    queryFn: () => api.rag.hybridSearch(params),
    enabled: (options?.enabled ?? true) && !!params.query,
    initialData: options?.initialData,
  });
}

/**
 * Search for code examples in documentation.
 */
export function useCodeExamples(
  params: CodeExampleParams,
  options?: { enabled?: boolean; initialData?: CodeExampleResponse }
) {
  return useQuery({
    queryKey: queryKeys.rag.code(params),
    queryFn: () => api.rag.codeExamples(params),
    enabled: (options?.enabled ?? true) && !!params.query,
    initialData: options?.initialData,
  });
}

/**
 * Get full page content by document ID.
 */
export function useFullPage(documentId: string, options?: { enabled?: boolean }) {
  return useQuery({
    queryKey: queryKeys.rag.page(documentId),
    queryFn: () => api.rag.getPage(documentId),
    enabled: (options?.enabled ?? true) && !!documentId,
  });
}

/**
 * List pages for a documentation source.
 */
export function useSourcePages(
  sourceId: string,
  params?: { limit?: number; offset?: number; has_code?: boolean; is_index?: boolean }
) {
  return useQuery({
    queryKey: queryKeys.rag.pages(sourceId, params),
    queryFn: () => api.rag.listPages(sourceId, params),
    enabled: !!sourceId,
  });
}

/**
 * Update a document's title and/or content.
 */
export function useUpdateDocument() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({
      documentId,
      updates,
    }: {
      documentId: string;
      updates: { title?: string; content?: string };
    }) => api.rag.updateDocument(documentId, updates),
    onSuccess: (data, { documentId }) => {
      // Update the cache with the new data
      queryClient.setQueryData(queryKeys.rag.page(documentId), data);
      // Invalidate the pages list for the source
      queryClient.invalidateQueries({ queryKey: queryKeys.rag.pages(data.source_id) });
      // Invalidate the source detail to refresh document counts
      queryClient.invalidateQueries({ queryKey: queryKeys.sources.detail(data.source_id) });
    },
  });
}

/**
 * Get related entities for a document.
 */
export function useDocumentEntities(documentId: string, options?: { enabled?: boolean }) {
  return useQuery({
    queryKey: queryKeys.rag.pageEntities(documentId),
    queryFn: () => api.rag.getDocumentEntities(documentId),
    enabled: (options?.enabled ?? true) && !!documentId,
  });
}

// =============================================================================
// Graph Hooks
// =============================================================================

export function useGraphData(params?: Parameters<typeof api.graph.full>[0]) {
  return useQuery({
    queryKey: queryKeys.graph.full(params),
    queryFn: () => api.graph.full(params),
  });
}

export function useSubgraph(params: Parameters<typeof api.graph.subgraph>[0]) {
  return useQuery({
    queryKey: queryKeys.graph.subgraph(params),
    queryFn: () => api.graph.subgraph(params),
    enabled: !!params.entity_id,
  });
}

export function useClusters(params?: { refresh?: boolean }) {
  return useQuery({
    queryKey: queryKeys.graph.clusters(params),
    queryFn: () => api.graph.clusters(params),
  });
}

export function useClusterDetail(clusterId: string | null) {
  return useQuery({
    queryKey: queryKeys.graph.clusterDetail(clusterId || ''),
    queryFn: () => api.graph.clusterDetail(clusterId ?? ''),
    enabled: !!clusterId,
  });
}

export function useGraphStats() {
  return useQuery({
    queryKey: queryKeys.graph.stats,
    queryFn: () => api.graph.stats(),
    staleTime: 5 * TIMING.STALE_TIME,
    refetchOnWindowFocus: false,
  });
}

export function useHierarchicalGraph(params?: {
  max_nodes?: number;
  max_edges?: number;
  projects?: string[];
  types?: string[];
  refresh?: boolean;
  resolution?: 'overview' | 'detail';
  cluster_id?: string;
}) {
  return useQuery({
    queryKey: queryKeys.graph.hierarchical(params),
    queryFn: () => api.graph.hierarchical(params),
    staleTime: 5 * 60 * 1000,
    gcTime: 10 * 60 * 1000,
    placeholderData: previousData => previousData,
  });
}

// =============================================================================
// Admin Hooks
// =============================================================================

export function useHealth() {
  return useQuery({
    queryKey: queryKeys.admin.health,
    queryFn: api.admin.health,
    refetchInterval: TIMING.HEALTH_CHECK_INTERVAL,
  });
}

export function useStats(initialData?: import('./api').StatsResponse) {
  return useQuery({
    queryKey: queryKeys.admin.stats,
    queryFn: api.admin.stats,
    initialData,
    staleTime: 5 * TIMING.STALE_TIME,
    refetchOnWindowFocus: false,
  });
}

export function useTelemetrySummary(params?: Parameters<typeof api.telemetry.summary>[0]) {
  return useQuery({
    queryKey: queryKeys.telemetry.summary(params),
    queryFn: () => api.telemetry.summary(params),
    refetchInterval: TIMING.STALE_TIME,
    staleTime: TIMING.STALE_TIME,
  });
}

// =============================================================================
// WebSocket Hook
// =============================================================================

export function useWebSocketStatus(enabled = true): ConnectionStatus {
  const [status, setStatus] = useState<ConnectionStatus>(wsClient.status);

  useEffect(() => {
    if (!enabled) {
      return;
    }

    setStatus(wsClient.status);
    return wsClient.on('connection_status', data => {
      setStatus(data.status);
    });
  }, [enabled]);

  return status;
}

export function useRealtimeUpdates(isAuthenticated?: boolean) {
  const queryClient = useQueryClient();

  useEffect(() => {
    if (isAuthenticated === undefined) {
      return;
    }

    // Only connect when authenticated
    if (!isAuthenticated) {
      wsClient.disconnect();
      return;
    }

    wsClient.connect();

    // Entity created - smart invalidation based on entity type
    const unsubCreate = wsClient.on('entity_created', data => {
      const entityType = data.entity_type || data.type;
      invalidateByEntityType(queryClient, entityType, data.id, { includeStats: true });
    });

    const unsubPending = wsClient.on('entity_pending', data => {
      const entityType = data.entity_type || data.type;
      invalidateByEntityType(queryClient, entityType, data.id, { includeStats: true });
    });

    // Entity updated - smart invalidation based on entity type
    const unsubUpdate = wsClient.on('entity_updated', data => {
      const entityType = data.entity_type || data.type;
      // Also invalidate related entities explorer
      queryClient.invalidateQueries({ queryKey: queryKeys.explore.related(data.id) });
      invalidateByEntityType(queryClient, entityType, data.id);
    });

    // Entity deleted - remove from cache + smart invalidation
    const unsubDelete = wsClient.on('entity_deleted', data => {
      const entityType = data.entity_type || data.type;
      // Remove from cache before invalidation
      queryClient.removeQueries({ queryKey: queryKeys.entities.detail(data.id) });
      queryClient.removeQueries({ queryKey: queryKeys.tasks.detail(data.id) });
      queryClient.removeQueries({ queryKey: queryKeys.projects.detail(data.id) });
      queryClient.removeQueries({ queryKey: queryKeys.sources.detail(data.id) });
      invalidateByEntityType(queryClient, entityType, data.id, { includeStats: true });
    });

    // Health update
    const unsubHealth = wsClient.on('health_update', () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.admin.health });
    });

    // Search complete (if backend sends it)
    const unsubSearch = wsClient.on('search_complete', () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.search.all });
    });

    const unsubGraphUpdated = wsClient.on('graph_updated', data => {
      queryClient.invalidateQueries({ queryKey: queryKeys.graph.all });
      if ((data.new_entities_created ?? 0) > 0) {
        queryClient.invalidateQueries({ queryKey: queryKeys.entities.all });
        queryClient.invalidateQueries({ queryKey: queryKeys.admin.stats });
      }
    });

    // Permission changed - refresh auth data
    const unsubPermission = wsClient.on('permission_changed', () => {
      // Invalidate auth/me to refresh current user's permissions
      queryClient.invalidateQueries({ queryKey: queryKeys.auth.me });
      // Also invalidate org data in case role affects what's visible
      queryClient.invalidateQueries({ queryKey: queryKeys.orgs.list });
    });

    // Crawl started - refresh source to show crawling status
    const unsubCrawlStarted = wsClient.on('crawl_started', data => {
      queryClient.invalidateQueries({ queryKey: queryKeys.sources.detail(data.source_id) });
      queryClient.invalidateQueries({ queryKey: queryKeys.sources.all });
    });

    // Crawl progress - update in real-time with merged data
    const unsubCrawlProgress = wsClient.on('crawl_progress', data => {
      const { source_id, documents_stored } = data;

      // Merge new progress with existing (we get page-level and doc-level events)
      const existing = queryClient.getQueryData<CrawlProgressData>(['crawl_progress', source_id]);
      const merged: CrawlProgressData = {
        ...existing,
        source_id,
        source_name: data.source_name ?? existing?.source_name,
        pages_crawled: data.pages_crawled ?? existing?.pages_crawled ?? 0,
        max_pages: data.max_pages ?? existing?.max_pages ?? 0,
        current_url: data.current_url ?? existing?.current_url ?? '',
        percentage: data.percentage ?? existing?.percentage ?? 0,
        documents_crawled: data.documents_crawled ?? existing?.documents_crawled,
        documents_stored: documents_stored ?? existing?.documents_stored,
        chunks_created: data.chunks_created ?? existing?.chunks_created,
        chunks_added: data.chunks_added ?? existing?.chunks_added,
        errors: data.errors ?? existing?.errors,
      };
      queryClient.setQueryData(['crawl_progress', source_id], merged);

      // Also update source's document_count in cache for real-time display
      if (documents_stored !== undefined) {
        // Update source list cache
        queryClient.setQueryData(
          queryKeys.sources.list,
          (
            old: { entities: Array<{ id: string; metadata: Record<string, unknown> }> } | undefined
          ) => {
            if (!old?.entities) return old;
            return {
              ...old,
              entities: old.entities.map(s =>
                s.id === source_id
                  ? { ...s, metadata: { ...s.metadata, document_count: documents_stored } }
                  : s
              ),
            };
          }
        );

        // Also update source detail cache (for source detail page)
        queryClient.setQueryData(
          queryKeys.sources.detail(source_id),
          (old: { document_count?: number } | undefined) => {
            if (!old) return old;
            return { ...old, document_count: documents_stored };
          }
        );
      }
    });

    // Crawl complete - refresh source and documents
    const unsubCrawlComplete = wsClient.on('crawl_complete', data => {
      // Clear the progress data
      queryClient.removeQueries({ queryKey: ['crawl_progress', data.source_id] });
      // Refresh source detail and list
      queryClient.invalidateQueries({ queryKey: queryKeys.sources.detail(data.source_id) });
      queryClient.invalidateQueries({ queryKey: queryKeys.sources.all });
      // Refresh any documents/pages for this source
      queryClient.invalidateQueries({ queryKey: queryKeys.rag.pages(data.source_id) });
    });

    const unsubCrawlSyncComplete = wsClient.on('crawl_sync_complete', data => {
      queryClient.invalidateQueries({ queryKey: queryKeys.sources.detail(data.source_id) });
      queryClient.invalidateQueries({ queryKey: queryKeys.sources.all });
      queryClient.invalidateQueries({ queryKey: queryKeys.rag.pages(data.source_id) });
    });

    const refreshBackupQueries = (backupId: string, jobId?: string) => {
      queryClient.invalidateQueries({ queryKey: queryKeys.backups.all });
      queryClient.invalidateQueries({ queryKey: queryKeys.jobs.all });
      queryClient.invalidateQueries({ queryKey: queryKeys.backups.detail(backupId) });
      if (jobId) {
        queryClient.invalidateQueries({ queryKey: queryKeys.backups.jobStatus(jobId) });
      }
    };

    const unsubBackupStarted = wsClient.on('backup_started', data => {
      refreshBackupQueries(data.backup_id, data.job_id);
    });

    const unsubBackupComplete = wsClient.on('backup_complete', data => {
      refreshBackupQueries(data.backup_id, data.job_id);
    });

    const unsubBackupFailed = wsClient.on('backup_failed', data => {
      refreshBackupQueries(data.backup_id, data.job_id);
    });

    const refreshTaskNotes = (taskId: string) => {
      queryClient.invalidateQueries({ queryKey: queryKeys.tasks.notes(taskId) });
      queryClient.invalidateQueries({ queryKey: queryKeys.tasks.detail(taskId) });
    };

    const unsubNotePending = wsClient.on('note_pending', data => {
      refreshTaskNotes(data.task_id);
    });

    const unsubNoteCreated = wsClient.on('note_created', data => {
      refreshTaskNotes(data.task_id);
    });

    const unsubSourceImportUpdated = wsClient.on('source_import_updated', data => {
      queryClient.setQueryData(queryKeys.memory.sourceImport(data.import_id), data);
      if (['paused', 'completed', 'failed', 'canceled'].includes(data.status)) {
        queryClient.invalidateQueries({ queryKey: queryKeys.memory.all });
        queryClient.invalidateQueries({ queryKey: queryKeys.rawCaptures.all });
      }
    });

    // Cleanup on unmount
    return () => {
      unsubCreate();
      unsubPending();
      unsubUpdate();
      unsubDelete();
      unsubHealth();
      unsubSearch();
      unsubGraphUpdated();
      unsubPermission();
      unsubCrawlStarted();
      unsubCrawlProgress();
      unsubCrawlComplete();
      unsubCrawlSyncComplete();
      unsubBackupStarted();
      unsubBackupComplete();
      unsubBackupFailed();
      unsubNotePending();
      unsubNoteCreated();
      unsubSourceImportUpdated();
      wsClient.disconnect();
    };
  }, [queryClient, isAuthenticated]);
}

// =============================================================================
// Task Hooks
// =============================================================================

export function useTasks(
  params?: {
    project?: string;
    project_ids?: string[];
    status?: TaskStatus;
  },
  options?: { enabled?: boolean; initialData?: import('./api').TaskListResponse }
) {
  const normalized =
    params && (params.project || params.project_ids?.length || params.status)
      ? {
          ...(params.project ? { project: params.project } : {}),
          ...(params.project_ids?.length ? { project_ids: [...params.project_ids] } : {}),
          ...(params.status ? { status: params.status } : {}),
        }
      : undefined;

  return useQuery({
    queryKey: queryKeys.tasks.list(normalized),
    queryFn: () => api.tasks.list(normalized),
    enabled: options?.enabled ?? true,
    initialData: options?.initialData,
  });
}

export function useTask(id: string) {
  return useQuery({
    queryKey: queryKeys.tasks.detail(id),
    queryFn: () => api.tasks.get(id),
    enabled: !!id,
  });
}

export function useTaskManage() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({
      action,
      entity_id,
      params,
    }: {
      action:
        | 'start_task'
        | 'block_task'
        | 'unblock_task'
        | 'submit_review'
        | 'complete_task'
        | 'archive';
      entity_id: string;
      params?: {
        assignee?: string;
        blocker?: string;
        reason?: string;
        commit_shas?: string[];
        pr_url?: string;
        actual_hours?: number;
        learnings?: string;
      };
    }) => {
      // Route to RESTful endpoints based on action
      switch (action) {
        case 'start_task':
          return api.tasks.start(
            entity_id,
            params?.assignee ? { assignee: params.assignee } : undefined
          );
        case 'block_task':
          return api.tasks.block(entity_id, params?.blocker || params?.reason || 'Blocked');
        case 'unblock_task':
          return api.tasks.unblock(entity_id);
        case 'submit_review':
          return api.tasks.review(entity_id, {
            pr_url: params?.pr_url,
            commit_shas: params?.commit_shas,
          });
        case 'complete_task':
          return api.tasks.complete(entity_id, {
            actual_hours: params?.actual_hours,
            learnings: params?.learnings,
          });
        case 'archive':
          return api.tasks.archive(
            entity_id,
            params?.reason ? { reason: params.reason } : undefined
          );
        default:
          throw new Error(`Unknown action: ${action}`);
      }
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.tasks.all });
      queryClient.invalidateQueries({ queryKey: ['metrics'] });
    },
  });
}

export function useTaskUpdateStatus() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({ id, status }: { id: string; status: TaskStatus }) =>
      api.tasks.updateStatus(id, status),
    onSuccess: (_data, { id }) => {
      queryClient.invalidateQueries({ queryKey: queryKeys.tasks.detail(id) });
      queryClient.invalidateQueries({ queryKey: queryKeys.tasks.all });
      queryClient.invalidateQueries({ queryKey: queryKeys.entities.detail(id) });
      queryClient.invalidateQueries({ queryKey: ['metrics'] });
    },
  });
}

// =============================================================================
// Task Notes Hooks
// =============================================================================

export function useTaskNotes(taskId: string, options?: { enabled?: boolean }) {
  return useQuery({
    queryKey: queryKeys.tasks.notes(taskId),
    queryFn: () => api.tasks.notes.list(taskId),
    enabled: (options?.enabled ?? true) && !!taskId,
  });
}

export function useAddTaskNote() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({ taskId, data }: { taskId: string; data: CreateNoteRequest }) =>
      api.tasks.notes.create(taskId, data),
    onSuccess: (_data, { taskId }) => {
      queryClient.invalidateQueries({ queryKey: queryKeys.tasks.notes(taskId) });
    },
  });
}

// =============================================================================
// Project Hooks
// =============================================================================

export function useProjects(
  options?: { includeArchived?: boolean; enabled?: boolean },
  initialData?: import('./api').TaskListResponse
) {
  const includeArchived = options?.includeArchived ?? false;
  return useQuery({
    queryKey: queryKeys.projects.list(includeArchived),
    queryFn: () => api.projects.list({ includeArchived }),
    enabled: options?.enabled ?? true,
    staleTime: TIMING.STALE_TIME,
    initialData,
  });
}

export function useProject(id: string) {
  return useQuery({
    queryKey: queryKeys.projects.detail(id),
    queryFn: () => api.projects.get(id),
    enabled: !!id,
    staleTime: TIMING.STALE_TIME,
  });
}

export function useProjectMembers(projectId: string, options?: { enabled?: boolean }) {
  return useQuery({
    queryKey: queryKeys.projects.members(projectId),
    queryFn: () => api.projects.members.list(projectId),
    enabled: options?.enabled ?? !!projectId,
    retry: false,
    staleTime: TIMING.STALE_TIME,
  });
}

export function useAddProjectMember() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({
      projectId,
      userId,
      role,
    }: {
      projectId: string;
      userId: string;
      role: import('./api').ProjectRole;
    }) => api.projects.members.add(projectId, userId, role),
    onSuccess: (_data, variables) => {
      queryClient.invalidateQueries({ queryKey: queryKeys.projects.members(variables.projectId) });
    },
  });
}

export function useUpdateProjectMemberRole() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({
      projectId,
      userId,
      role,
    }: {
      projectId: string;
      userId: string;
      role: import('./api').ProjectRole;
    }) => api.projects.members.updateRole(projectId, userId, role),
    onSuccess: (_data, variables) => {
      queryClient.invalidateQueries({ queryKey: queryKeys.projects.members(variables.projectId) });
    },
  });
}

export function useRemoveProjectMember() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({ projectId, userId }: { projectId: string; userId: string }) =>
      api.projects.members.remove(projectId, userId),
    onSuccess: (_data, variables) => {
      queryClient.invalidateQueries({ queryKey: queryKeys.projects.members(variables.projectId) });
    },
  });
}

// =============================================================================
// Epic Hooks
// =============================================================================

export function useEpics(params?: {
  project?: string;
  project_ids?: string[];
  status?: EpicStatus;
}) {
  const normalized =
    params && (params.project || params.project_ids?.length || params.status)
      ? {
          ...(params.project ? { project: params.project } : {}),
          ...(params.project_ids?.length ? { project_ids: [...params.project_ids] } : {}),
          ...(params.status ? { status: params.status } : {}),
        }
      : undefined;

  return useQuery({
    queryKey: queryKeys.epics.list(normalized),
    queryFn: () => api.epics.list(normalized),
    staleTime: TIMING.STALE_TIME,
  });
}

export function useEpic(id: string) {
  return useQuery({
    queryKey: queryKeys.epics.detail(id),
    queryFn: () => api.epics.get(id),
    enabled: !!id,
    staleTime: TIMING.STALE_TIME,
  });
}

export function useEpicTasks(epicId: string) {
  return useQuery({
    queryKey: queryKeys.epics.tasks(epicId),
    queryFn: () => api.epics.tasks(epicId),
    enabled: !!epicId,
    staleTime: TIMING.STALE_TIME,
  });
}

export function useEpicManage() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({
      action,
      entity_id,
      params,
    }: {
      action: 'start_epic' | 'complete_epic' | 'archive_epic' | 'update_epic';
      entity_id: string;
      params?: {
        learnings?: string;
        reason?: string;
        status?: EpicStatus;
        priority?: TaskPriority;
        title?: string;
        description?: string;
        assignees?: string[];
        tags?: string[];
      };
    }) => {
      // Route to RESTful endpoints based on action
      switch (action) {
        case 'start_epic':
          return api.epics.start(entity_id);
        case 'complete_epic':
          return api.epics.complete(
            entity_id,
            params?.learnings ? { learnings: params.learnings } : undefined
          );
        case 'archive_epic':
          return api.epics.archive(
            entity_id,
            params?.reason ? { reason: params.reason } : undefined
          );
        case 'update_epic':
          return api.epics.update(entity_id, {
            status: params?.status,
            priority: params?.priority,
            title: params?.title,
            description: params?.description,
            assignees: params?.assignees,
            tags: params?.tags,
          });
        default:
          throw new Error(`Unknown action: ${action}`);
      }
    },
    onSuccess: () => {
      // Invalidate epics list and related queries
      queryClient.invalidateQueries({ queryKey: queryKeys.epics.all });
      queryClient.invalidateQueries({ queryKey: queryKeys.tasks.all });
    },
  });
}

// =============================================================================
// Explore Hooks
// =============================================================================

// =============================================================================
// Source Hooks
// =============================================================================

export function useSources() {
  return useQuery({
    queryKey: queryKeys.sources.list,
    queryFn: () => api.sources.list(),
  });
}

export function useSource(id: string) {
  return useQuery({
    queryKey: queryKeys.sources.detail(id),
    queryFn: () => api.sources.get(id),
    enabled: !!id,
  });
}

export function useCreateSource() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (source: Parameters<typeof api.sources.create>[0]) => api.sources.create(source),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.sources.all });
    },
  });
}

export function useDeleteSource() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (id: string) => api.sources.delete(id),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.sources.all });
    },
  });
}

export function useCrawlSource() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (id: string) => api.sources.crawl(id),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.sources.all });
    },
  });
}

export function useSyncSource() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: async (id: string) => {
      const res = await fetch(`/api/sources/${id}/sync`, { method: 'POST' });
      if (!res.ok) throw new Error('Failed to sync source');
      return res.json();
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.sources.all });
    },
  });
}

export function useUpdateSource() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({
      id,
      updates,
    }: {
      id: string;
      updates: Parameters<typeof api.sources.update>[1];
    }) => api.sources.update(id, updates),
    onSuccess: (data, { id }) => {
      queryClient.setQueryData(queryKeys.sources.detail(id), data);
      queryClient.invalidateQueries({ queryKey: queryKeys.sources.all });
    },
  });
}

export function useCancelCrawl() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: async (id: string) => {
      const res = await fetch(`/api/sources/${id}/cancel`, { method: 'POST' });
      if (!res.ok) throw new Error('Failed to cancel crawl');
      return res.json();
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.sources.all });
    },
  });
}

export interface CrawlProgressData {
  source_id: string;
  source_name?: string;
  // Page-level progress (from CrawlerService)
  pages_crawled: number;
  max_pages: number;
  current_url: string;
  percentage: number;
  // Document-level stats (from Worker on_progress)
  documents_crawled?: number;
  documents_stored?: number;
  chunks_created?: number;
  chunks_added?: number;
  errors?: number;
}

export function useCrawlProgress(sourceId: string): CrawlProgressData | undefined {
  const queryClient = useQueryClient();
  const [progress, setProgress] = useState<CrawlProgressData | undefined>(
    queryClient.getQueryData(['crawl_progress', sourceId])
  );

  useEffect(() => {
    // Subscribe to query cache changes
    const unsubscribe = queryClient.getQueryCache().subscribe(event => {
      if (
        event.type === 'updated' &&
        event.query.queryKey[0] === 'crawl_progress' &&
        event.query.queryKey[1] === sourceId
      ) {
        setProgress(event.query.state.data as CrawlProgressData | undefined);
      }
    });

    return unsubscribe;
  }, [queryClient, sourceId]);

  return progress;
}

/**
 * Track crawl progress for all sources.
 * Returns a map of source_id -> progress data.
 */
export function useAllCrawlProgress(): Map<string, CrawlProgressData> {
  const queryClient = useQueryClient();
  const [progressMap, setProgressMap] = useState<Map<string, CrawlProgressData>>(new Map());

  useEffect(() => {
    // Subscribe to all crawl_progress updates
    const unsubscribe = queryClient.getQueryCache().subscribe(event => {
      if (event.type === 'updated' && event.query.queryKey[0] === 'crawl_progress') {
        const sourceId = event.query.queryKey[1] as string;
        const data = event.query.state.data as CrawlProgressData | undefined;

        setProgressMap(prev => {
          const next = new Map(prev);
          if (data) {
            next.set(sourceId, data);
          } else {
            next.delete(sourceId);
          }
          return next;
        });
      }
    });

    return unsubscribe;
  }, [queryClient]);

  return progressMap;
}

// =============================================================================
// Metrics Hooks
// =============================================================================

/**
 * Fetch org-level metrics (aggregated across all projects).
 */
export function useOrgMetrics(initialData?: import('./api').OrgMetricsResponse) {
  return useQuery({
    queryKey: queryKeys.metrics.org,
    queryFn: api.metrics.org,
    initialData,
    staleTime: TIMING.STALE_TIME,
  });
}

/** Fetch lean project summaries for the projects page. */
export function useProjectSummaries(initialData?: import('./api').ProjectSummariesResponse) {
  return useQuery({
    queryKey: queryKeys.metrics.projectsSummary,
    queryFn: api.metrics.projectsSummary,
    initialData,
    staleTime: TIMING.STALE_TIME,
  });
}

/**
 * Fetch project-level metrics.
 */
export function useProjectMetrics(
  projectId: string,
  initialData?: import('./api').ProjectMetricsResponse
) {
  return useQuery({
    queryKey: queryKeys.metrics.project(projectId),
    queryFn: () => api.metrics.project(projectId),
    initialData,
    enabled: Boolean(projectId),
    staleTime: TIMING.STALE_TIME,
  });
}

// =============================================================================
// Setup Wizard Hooks
// =============================================================================

/**
 * Check setup status - whether this is a fresh install needing configuration.
 * This is a public endpoint that works before any users exist.
 */
export function useSetupStatus(options?: { validateKeys?: boolean; enabled?: boolean }) {
  return useQuery({
    queryKey: queryKeys.setup.status,
    queryFn: () => api.setup.status(options?.validateKeys),
    enabled: options?.enabled ?? true,
    staleTime: 30000, // Cache for 30 seconds
    retry: false, // Don't retry on failure (server might be down)
  });
}

/**
 * Validate API keys are configured and working.
 */
export function useValidateApiKeys(options?: { enabled?: boolean }) {
  return useQuery({
    queryKey: queryKeys.setup.validation,
    queryFn: () => api.setup.validateKeys(),
    enabled: options?.enabled ?? true,
    staleTime: 60000, // Cache for 1 minute
    retry: 1, // One retry on timeout
  });
}

/**
 * Get the integration payload for connecting Sibyl to a CLI or MCP client.
 *
 * Returns the CLI install command, per-client MCP configs, and the agent
 * prompt snippet. Single source of truth behind the connect surfaces.
 */
export function useIntegration(options?: { enabled?: boolean }) {
  return useQuery({
    queryKey: queryKeys.setup.integration,
    queryFn: () => api.setup.integration(),
    enabled: options?.enabled ?? true,
    staleTime: Infinity, // Server URL and snippets are stable
  });
}

// =============================================================================
// Settings Hooks
// =============================================================================

/**
 * Get system settings (API key configuration status).
 */
export function useSettings(options?: { enabled?: boolean }) {
  return useQuery({
    queryKey: queryKeys.settings.all,
    queryFn: () => api.settings.get(),
    enabled: options?.enabled ?? true,
    staleTime: 30000, // Cache for 30 seconds
  });
}

/**
 * Update system settings (save API keys to database).
 */
export function useUpdateSettings() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (request: UpdateSettingsRequest) => api.settings.update(request),
    onSuccess: () => {
      // Invalidate settings and setup status queries
      queryClient.invalidateQueries({ queryKey: queryKeys.settings.all });
      queryClient.invalidateQueries({ queryKey: queryKeys.setup.status });
      queryClient.invalidateQueries({ queryKey: queryKeys.setup.validation });
    },
  });
}

/**
 * Delete a system setting from the database.
 */
export function useDeleteSetting() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (key: string) => api.settings.delete(key),
    onSuccess: () => {
      // Invalidate settings and setup status queries
      queryClient.invalidateQueries({ queryKey: queryKeys.settings.all });
      queryClient.invalidateQueries({ queryKey: queryKeys.setup.status });
      queryClient.invalidateQueries({ queryKey: queryKeys.setup.validation });
    },
  });
}

export function useLLMSettings(options?: { enabled?: boolean }) {
  return useQuery({
    queryKey: queryKeys.settings.llm,
    queryFn: () => api.settings.ai.getLLMSettings(),
    enabled: options?.enabled ?? true,
    staleTime: 30000,
  });
}

export function useLLMRegistry(kind: AIModelKind = 'llm', options?: { enabled?: boolean }) {
  return useQuery({
    queryKey: queryKeys.settings.registry(kind),
    queryFn: () => api.settings.ai.getRegistry(kind),
    enabled: options?.enabled ?? true,
    staleTime: 300000,
  });
}

export function useUpdateLLMSurface() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({ surface, request }: { surface: LLMSurface; request: UpdateLLMSurfaceRequest }) =>
      api.settings.ai.updateLLMSurface(surface, request),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.settings.llm });
      queryClient.invalidateQueries({ queryKey: queryKeys.settings.all });
    },
  });
}

export function useTestLLMSurface() {
  return useMutation({
    mutationFn: (surface: LLMSurface) => api.settings.ai.testLLMSurface(surface),
  });
}

export function useTestProviderKey() {
  return useMutation({
    mutationFn: (provider: LLMProviderName) => api.settings.ai.testProviderKey(provider),
  });
}

export function useTestAIModel() {
  return useMutation({
    mutationFn: (modelAlias: string) => api.settings.ai.testModel(modelAlias),
  });
}

// =============================================================================
// Backup Management Hooks
// =============================================================================

/**
 * Get backup settings for the current organization.
 */
export function useBackupSettings(options?: { enabled?: boolean }) {
  return useQuery({
    queryKey: queryKeys.backups.settings,
    queryFn: () => api.backups.settings.get(),
    enabled: options?.enabled ?? true,
    staleTime: 30000,
  });
}

/**
 * Update backup settings for the current organization.
 */
export function useUpdateBackupSettings() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (data: Parameters<typeof api.backups.settings.update>[0]) =>
      api.backups.settings.update(data),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.backups.settings });
    },
  });
}

/**
 * List all backups for the current organization.
 */
export function useBackups(options?: { enabled?: boolean; limit?: number; offset?: number }) {
  const enabled = options?.enabled ?? true;
  const wsStatus = useWebSocketStatus(enabled);

  return useQuery({
    queryKey: queryKeys.backups.list,
    queryFn: () => api.backups.list(options?.limit ?? 50, options?.offset ?? 0),
    enabled,
    staleTime: 10000,
    refetchInterval: wsStatus === 'connected' ? false : 30000,
  });
}

export function useJobs(options?: { enabled?: boolean; function?: string; limit?: number }) {
  return useQuery({
    queryKey: queryKeys.jobs.list({
      function: options?.function,
      limit: options?.limit ?? 25,
    }),
    queryFn: () =>
      api.jobs.list({
        function: options?.function,
        limit: options?.limit ?? 25,
      }),
    enabled: options?.enabled ?? true,
    staleTime: 5000,
    refetchInterval: 15000,
  });
}

export function useRunMaintenanceJob() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({ action }: { action: 'consolidate' | 'forget' | 'reflect' }) => {
      if (action === 'consolidate') return api.jobs.runConsolidation();
      if (action === 'reflect') return api.jobs.runReflectionDream({ dry_run: true });
      return api.jobs.runPriorityDecay();
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.jobs.all });
    },
  });
}

/**
 * Get details of a specific backup.
 */
export function useBackup(backupId: string, options?: { enabled?: boolean }) {
  return useQuery({
    queryKey: queryKeys.backups.detail(backupId),
    queryFn: () => api.backups.get(backupId),
    enabled: (options?.enabled ?? true) && !!backupId,
    staleTime: 10000,
  });
}

/**
 * Create a new backup.
 */
export function useCreateBackup() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (data?: Parameters<typeof api.backups.create>[0]) => api.backups.create(data),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.backups.list });
    },
  });
}

/**
 * Delete a backup.
 */
export function useDeleteBackup() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (backupId: string) => api.backups.delete(backupId),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.backups.list });
    },
  });
}

/**
 * Trigger backup cleanup.
 */
export function useBackupCleanup() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (retentionDays?: number) => api.backups.cleanup(retentionDays),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.backups.list });
    },
  });
}

/**
 * Get status of a backup job.
 */
export function useBackupJobStatus(jobId: string, options?: { enabled?: boolean }) {
  const enabled = (options?.enabled ?? true) && !!jobId;
  const wsStatus = useWebSocketStatus(enabled);

  return useQuery({
    queryKey: queryKeys.backups.jobStatus(jobId),
    queryFn: () => api.backups.jobStatus(jobId),
    enabled,
    staleTime: 2000,
    refetchInterval: query => {
      if (wsStatus === 'connected') {
        return false;
      }
      const status = query.state.data?.status;
      if (status === 'complete' || status === 'not_found') {
        return false;
      }
      return 3000;
    },
  });
}

// =============================================================================
// Media Query Hook
// =============================================================================

/**
 * Subscribe to a CSS media query and return whether it matches.
 * SSR-safe: returns false until hydrated.
 */
export function useMediaQuery(query: string): boolean {
  const [matches, setMatches] = useState(false);

  useEffect(() => {
    const mql = window.matchMedia(query);
    setMatches(mql.matches);

    const handler = (e: MediaQueryListEvent) => setMatches(e.matches);
    mql.addEventListener('change', handler);
    return () => mql.removeEventListener('change', handler);
  }, [query]);

  return matches;
}
