import { useEffect, useMemo, useRef, useState } from 'react'
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import {
  fetchCollectorDigest,
  fetchCollectorItems,
  fetchCollectorState,
  fetchFactoryRun,
  fetchIncident,
  fetchIncidentOptions,
  fetchIncidents,
  fileIncident,
  CollectorHttpError,
  fetchHookInventory,
  fetchProjectColors,
  fetchActivity,
  fetchActivitySourceEntries,
  fetchRoutingConfig,
  fetchCluster,
  fetchClusterNode,
  type RoutingProvider,
  fetchHookControls,
  setHookControl,
  repairHookControls,
} from './collector-client'
import type { HookControlId } from './collector-types'
import { normalizeCollectorStateResponse } from './collector-state'
import type { ActivityQuery, ActivitySourceEntriesQuery } from './activity-types'
import type { ListIncidentsParams } from './incident-types'
import type { FileIncidentRequest } from './incident-types'

export const collectorQueryKeys = {
  state: ['collector-state'] as const,
  items: (kind?: string) => (kind ? ['collector-items', kind] as const : ['collector-items'] as const),
  projectColors: ['project-colors'] as const,
  routingConfig: (provider: RoutingProvider) => ['routing-config', provider] as const,
  hooks: ['collector-hooks'] as const,
  hookControls: ['collector-hook-controls'] as const,
  digest: ['collector-digest'] as const,
  activity: (query: ActivityQuery) => ['collector-activity', query] as const,
  activitySourceEntries: (query: ActivitySourceEntriesQuery) => ['collector-activity-source-entries', query] as const,
  incidents: (params: ListIncidentsParams) =>
    ['collector-incidents', params.scope, params.query ?? '', params.priority ?? ''] as const,
  incident: (incidentId: string) => ['collector-incident', incidentId] as const,
  incidentOptions: ['collector-incident-options'] as const,
  factoryRun: (adwId: string) => ['collector-factory-run', adwId] as const,
  cluster: ['collector-cluster'] as const,
  clusterNode: (node: string) => ['collector-cluster-node', node] as const,
}

export function useFactoryRun(adwId: string, enabled: boolean, live: boolean) {
  const queryClient = useQueryClient()
  const queryKey = collectorQueryKeys.factoryRun(adwId)
  useEffect(() => {
    if (enabled) return
    void queryClient.cancelQueries({ queryKey })
    queryClient.removeQueries({ queryKey })
  }, [adwId, enabled, queryClient])

  return useQuery({
    queryKey,
    queryFn: ({ signal }) => fetchFactoryRun(adwId, signal),
    enabled,
    gcTime: 0,
    refetchInterval: enabled && live ? COLLECTOR_POLL_MS : false,
    refetchIntervalInBackground: false,
  })
}

export function useCluster() {
  return useQuery({
    queryKey: collectorQueryKeys.cluster,
    queryFn: fetchCluster,
    refetchInterval: COLLECTOR_POLL_MS,
    refetchIntervalInBackground: false,
    refetchOnWindowFocus: true,
    refetchOnReconnect: true,
  })
}

export function useClusterNode(node: string | null) {
  return useQuery({
    queryKey: collectorQueryKeys.clusterNode(node ?? ''),
    queryFn: () => fetchClusterNode(node!),
    enabled: node !== null,
  })
}

/**
 * Polls independently of the SSE bridge. The stream is the fast path, but a dropped
 * stream is silent — a suspended laptop or a restarted collector leaves the cache
 * frozen, and a frozen deck renders finished runs as `running` indefinitely.
 */
export const COLLECTOR_POLL_MS = 15_000

export function useCollectorState() {
  return useQuery({
    queryKey: collectorQueryKeys.state,
    queryFn: fetchCollectorState,
    select: normalizeCollectorStateResponse,
    refetchInterval: COLLECTOR_POLL_MS,
    refetchIntervalInBackground: false,
    refetchOnWindowFocus: true,
    refetchOnReconnect: true,
  })
}

export function useCollectorItems(kind?: string) {
  return useQuery({
    queryKey: collectorQueryKeys.items(kind),
    queryFn: () => fetchCollectorItems(kind),
    refetchInterval: COLLECTOR_POLL_MS,
    refetchIntervalInBackground: false,
    refetchOnWindowFocus: true,
    refetchOnReconnect: true,
  })
}

export function useActivity(query: ActivityQuery) {
  return useQuery({
    queryKey: collectorQueryKeys.activity(query),
    queryFn: () => fetchActivity(query),
    refetchInterval: COLLECTOR_POLL_MS,
    refetchIntervalInBackground: false,
    refetchOnWindowFocus: true,
    refetchOnReconnect: true,
    placeholderData: (previous) => previous,
  })
}

export function useActivitySourceEntries(query: ActivitySourceEntriesQuery) {
  return useQuery({
    queryKey: collectorQueryKeys.activitySourceEntries(query),
    queryFn: () => fetchActivitySourceEntries(query),
    refetchInterval: COLLECTOR_POLL_MS,
    refetchIntervalInBackground: false,
    refetchOnWindowFocus: true,
    refetchOnReconnect: true,
    placeholderData: (previous) => previous,
  })
}

export function useProjectColors() {
  return useQuery({
    queryKey: collectorQueryKeys.projectColors,
    queryFn: fetchProjectColors,
  })
}

export function useRoutingConfig(provider: RoutingProvider) {
  return useQuery({
    queryKey: collectorQueryKeys.routingConfig(provider),
    queryFn: () => fetchRoutingConfig(provider),
  })
}

export function useHookControls() {
  return useQuery({
    queryKey: collectorQueryKeys.hookControls,
    queryFn: ({ signal }) => fetchHookControls({ signal }),
    meta: { persist: false },
    refetchOnWindowFocus: true,
    refetchOnReconnect: true,
  })
}

async function applyHookControlsCache(queryClient: ReturnType<typeof useQueryClient>, data: Awaited<ReturnType<typeof fetchHookControls>>) {
  queryClient.setQueryData(collectorQueryKeys.hookControls, data)
}

async function cancelHookControlsQuery(queryClient: ReturnType<typeof useQueryClient>) {
  await queryClient.cancelQueries({ queryKey: collectorQueryKeys.hookControls })
}

export function useSetHookControl() {
  const queryClient = useQueryClient()
  return useMutation({
    mutationFn: ({ id, enabled }: { id: HookControlId; enabled: boolean }) => setHookControl(id, enabled),
    onMutate: async () => { await cancelHookControlsQuery(queryClient) },
    onSuccess: async (data) => {
      await cancelHookControlsQuery(queryClient)
      await applyHookControlsCache(queryClient, data)
    },
  })
}

export function useRepairHookControls() {
  const queryClient = useQueryClient()
  return useMutation({
    mutationFn: repairHookControls,
    onMutate: async () => { await cancelHookControlsQuery(queryClient) },
    onSuccess: async (data) => {
      await cancelHookControlsQuery(queryClient)
      await applyHookControlsCache(queryClient, data)
    },
  })
}

export function useHookInventory() {
  return useQuery({
    queryKey: collectorQueryKeys.hooks,
    queryFn: fetchHookInventory,
    refetchInterval: COLLECTOR_POLL_MS,
    refetchIntervalInBackground: false,
    refetchOnWindowFocus: true,
    refetchOnReconnect: true,
  })
}

export function useIncidents(params: ListIncidentsParams) {
  const queryClient = useQueryClient()
  const queryKey = useMemo(() => collectorQueryKeys.incidents(params), [params.scope, params.query, params.priority])
  const backoffUntil = useRef(0)
  const [backingOff, setBackingOff] = useState(false)
  const query = useInfiniteQuery({
    queryKey,
    initialPageParam: undefined as { cursor: number; highWater: number } | undefined,
    queryFn: async ({ pageParam, signal }) => {
      try { return await fetchIncidents({ ...params, ...(pageParam ?? {}) }, signal) }
      catch (error) {
        if (error instanceof CollectorHttpError && error.status === 429) {
          backoffUntil.current = Date.now() + Math.max(COLLECTOR_POLL_MS, error.retryAfterMs ?? COLLECTOR_POLL_MS)
          setBackingOff(true)
        }
        throw error
      }
    },
    getNextPageParam: (lastPage) => lastPage.page.exhausted || lastPage.page.nextCursor === null
      ? undefined
      : { cursor: lastPage.page.nextCursor, highWater: lastPage.page.highWater },
    refetchInterval: false,
    retry: false,
    refetchOnWindowFocus: !backingOff,
    refetchOnReconnect: !backingOff,
  })
  useEffect(() => {
    const delay = Math.max(COLLECTOR_POLL_MS, backoffUntil.current - Date.now())
    const timer = window.setTimeout(() => {
      if (Date.now() < backoffUntil.current) return
      if (backingOff) setBackingOff(false)
      void queryClient.resetQueries({ queryKey, exact: true, type: 'active' })
    }, delay)
    return () => window.clearTimeout(timer)
  }, [backingOff, queryClient, queryKey])
  return query
}

export function useIncident(incidentId: string | null) {
  return useQuery({
    queryKey: collectorQueryKeys.incident(incidentId ?? ''),
    queryFn: () => fetchIncident(incidentId!),
    enabled: incidentId !== null,
    refetchInterval: false,
    refetchOnWindowFocus: true,
    refetchOnReconnect: true,
  })
}

export function useIncidentOptions() {
  return useQuery({
    queryKey: collectorQueryKeys.incidentOptions,
    queryFn: fetchIncidentOptions,
    staleTime: 60_000,
    retry: false,
  })
}

export function useFileIncident() {
  const queryClient = useQueryClient()
  return useMutation({
    mutationFn: (request: FileIncidentRequest) => fileIncident(request),
    onSuccess: () => queryClient.invalidateQueries({ queryKey: ['collector-incidents'] }),
  })
}

export function useCollectorDigest() {
  return useQuery({
    queryKey: collectorQueryKeys.digest,
    queryFn: fetchCollectorDigest,
    staleTime: 60_000,
  })
}
