import { useQuery, queryOptions } from '@tanstack/react-query'
import axios from 'axios'
import {
  apiClient,
  type AuditFilters,
  type AuditPage,
  type CascadePlan,
  type ChangelogDiff,
  type ChangelogResponse,
  type SchemaDraft,
  type SchemaReviewQueuePage,
  type DictionaryDefinition,
  type DictionaryDetail,
  type DlqPage,
  type DraftResponse,
  type FlattenedRecord,
  type OutboxStats,
  type RecordDependentsPage,
  type RecordResponse,
  type ReviewQueuePage,
  type SchemaDependent,
  type SearchResponse,
  type WebhookDeliveryPage,
  type WebhookDeliveryStats,
  type WebhookSubscription,
} from './client'

export const dictionariesQuery = queryOptions({
  queryKey: ['dictionaries'] as const,
  queryFn: async (): Promise<DictionaryDefinition[]> => {
    // Backend по default возвращает только PUBLIC scope (~user feedback:
    // «куда опять internal словари делись»). Передаём весь triplet —
    // server-side всё равно отфильтрует по реальным scope permissions
    // юзера, и invisible scopes просто не вернутся. Это match'ит то что
    // useRecords / catalog row делает per-record (см. строку ~65).
    const { data } = await apiClient.get<DictionaryDefinition[]>('/dictionaries', {
      params: { as_scope: 'PUBLIC,INTERNAL,RESTRICTED' },
    })
    return data
  },
})

export const dictionaryDetailQuery = (name: string) =>
  queryOptions({
    queryKey: ['dictionary', name] as const,
    queryFn: async (): Promise<DictionaryDetail> => {
      // Same as listing — pass all scopes; server-side filter применит
      // user permissions. Без этого INTERNAL dict вернёт 404 даже для
      // авторизованного юзера с INTERNAL access.
      const { data } = await apiClient.get<DictionaryDetail>(`/dictionaries/${name}`, {
        params: { as_scope: 'PUBLIC,INTERNAL,RESTRICTED' },
      })
      return data
    },
  })

export type RecordsFilter = {
  /** CSV bbox: west,south,east,north */
  bbox?: string
  /** GeoJSON Polygon as raw object — serialized as JSON в query string. */
  polygon?: GeoJSON.Polygon
  /** ISO datetime для time-travel запроса (CEO plan time-travel UI).
   *  Backend (read-api) findActiveAt(dict, key, at) возвращает active version
   *  на этот момент. Без этого param'а — now(). */
  at?: string
}

export const recordsQuery = (
  dictionaryName: string,
  scopeCsv: string,
  filter?: RecordsFilter,
) =>
  queryOptions({
    queryKey: [
      'records',
      dictionaryName,
      scopeCsv,
      filter?.bbox ?? null,
      filter?.polygon ? JSON.stringify(filter.polygon) : null,
      filter?.at ?? null,
    ] as const,
    queryFn: async (): Promise<FlattenedRecord[]> => {
      const params: Record<string, string> = { as_scope: scopeCsv }
      if (filter?.bbox) params.bbox = filter.bbox
      if (filter?.polygon) params.polygon = JSON.stringify(filter.polygon)
      if (filter?.at) params.at = filter.at
      const { data } = await apiClient.get<FlattenedRecord[]>(
        `/${dictionaryName}/records`,
        { params },
      )
      return data
    },
  })

/**
 * Fetch records from a target dictionary for use as FK reference options.
 * Used by SchemaDrivenForm when a property has `x-references: dict.field`.
 *
 * Returns 404 → empty array (scope-hide: target dict not accessible).
 * 5min staleTime — FK targets are typically slow-moving reference data.
 */
export const referencedRecordsQuery = (dictionaryName: string) =>
  queryOptions({
    queryKey: ['fk-options', dictionaryName] as const,
    queryFn: async (): Promise<FlattenedRecord[]> => {
      try {
        const { data } = await apiClient.get<FlattenedRecord[]>(
          `/${dictionaryName}/records`,
          { params: { as_scope: 'PUBLIC,INTERNAL,RESTRICTED' } },
        )
        return data
      } catch (e: unknown) {
        const status = (e as { response?: { status?: number } })?.response?.status
        if (status === 404 || status === 403) return []
        throw e
      }
    },
    staleTime: 5 * 60_000,
  })

export const useReferencedRecords = (dictionaryName: string | undefined) =>
  useQuery({
    ...referencedRecordsQuery(dictionaryName ?? ''),
    enabled: Boolean(dictionaryName),
  })

export const recordHistoryQuery = (dictionaryName: string, businessKey: string) =>
  queryOptions({
    queryKey: ['record-history', dictionaryName, businessKey] as const,
    queryFn: async (): Promise<RecordResponse[]> => {
      const { data } = await apiClient.get<RecordResponse[]>(
        `/dictionaries/${dictionaryName}/records/${encodeURIComponent(businessKey)}/history`,
      )
      return data
    },
  })

export const useRecordHistory = (
  dictionaryName: string,
  businessKey: string | undefined,
) =>
  useQuery({
    ...recordHistoryQuery(dictionaryName, businessKey ?? ''),
    enabled: Boolean(businessKey),
  })

// === Changelog (schema versions timeline) ============================

/**
 * Per-dict changelog. Backend default limit=50; покрывает типичные ~5-30
 * schema versions без pagination. Cursor можно добавить позже без
 * breaking change.
 */
export const changelogQuery = (dictionaryName: string, limit = 50) =>
  queryOptions({
    queryKey: ['changelog', dictionaryName, limit] as const,
    queryFn: async (): Promise<ChangelogResponse> => {
      const { data } = await apiClient.get<ChangelogResponse>(
        `/dictionaries/${dictionaryName}/changelog`,
        { params: { limit } },
      )
      return data
    },
  })

export const useChangelog = (dictionaryName: string | undefined, limit = 50) =>
  useQuery({
    ...changelogQuery(dictionaryName ?? '', limit),
    enabled: Boolean(dictionaryName),
  })

/**
 * Full before/after diff payload для одного changelog entry'я. Lazy —
 * fetch только когда юзер раскрывает Compare modal. Кешируем по
 * (dict, entryId) — diff immutable, можно держать stale infinitely
 * (audit_log row не меняется).
 */
export const changelogDiffQuery = (dictionaryName: string, entryId: number) =>
  queryOptions({
    queryKey: ['changelog-diff', dictionaryName, entryId] as const,
    queryFn: async (): Promise<ChangelogDiff> => {
      const { data } = await apiClient.get<ChangelogDiff>(
        `/dictionaries/${dictionaryName}/changelog/${entryId}/diff`,
      )
      return data
    },
    staleTime: Infinity,
  })

export const useChangelogDiff = (
  dictionaryName: string | undefined,
  entryId: number | undefined,
) =>
  useQuery({
    ...changelogDiffQuery(dictionaryName ?? '', entryId ?? 0),
    enabled: Boolean(dictionaryName) && Boolean(entryId),
  })

// === Schema workflow drafts (Phase 1a) ================================

/**
 * Active schema draft на словаре, если есть. Backend возвращает 404
 * {@code no_active_draft} когда нет non-terminal draft'а — мы маппим
 * это в null чтобы UI рендерил «no draft» state без error toast'а.
 *
 * <p>refetchInterval=20s — banner должен быстро отражать «отправили
 * на ревью» / «approved» transition от другого пользователя.
 */
export const dictActiveSchemaDraftQuery = (dictName: string) =>
  queryOptions({
    queryKey: ['schema-draft-active', dictName] as const,
    queryFn: async (): Promise<SchemaDraft | null> => {
      try {
        const { data } = await apiClient.get<SchemaDraft>(
          `/dictionaries/${dictName}/drafts/active`,
        )
        return data
      } catch (err) {
        // 404 no_active_draft — это not-an-error, отдаём null.
        if (axios.isAxiosError(err) && err.response?.status === 404) {
          return null
        }
        throw err
      }
    },
    refetchInterval: 20_000,
  })

export const useDictActiveSchemaDraft = (dictName: string | undefined) =>
  useQuery({
    ...dictActiveSchemaDraftQuery(dictName ?? ''),
    enabled: Boolean(dictName),
  })

/** History всех schema drafts на словаре (для admin audit / settings tab). */
export const dictSchemaDraftsQuery = (dictName: string) =>
  queryOptions({
    queryKey: ['schema-drafts', dictName] as const,
    queryFn: async (): Promise<SchemaDraft[]> => {
      const { data } = await apiClient.get<SchemaDraft[]>(
        `/dictionaries/${dictName}/drafts`,
      )
      return data
    },
  })

export const useDictSchemaDrafts = (dictName: string | undefined) =>
  useQuery({
    ...dictSchemaDraftsQuery(dictName ?? ''),
    enabled: Boolean(dictName),
  })

/** Single draft drill-in (decision modal, deep-link). */
export const schemaDraftQuery = (dictName: string, draftId: string) =>
  queryOptions({
    queryKey: ['schema-draft', dictName, draftId] as const,
    queryFn: async (): Promise<SchemaDraft> => {
      const { data } = await apiClient.get<SchemaDraft>(
        `/dictionaries/${dictName}/drafts/${draftId}`,
      )
      return data
    },
  })

export const useSchemaDraft = (
  dictName: string | undefined,
  draftId: string | undefined,
) =>
  useQuery({
    ...schemaDraftQuery(dictName ?? '', draftId ?? ''),
    enabled: Boolean(dictName) && Boolean(draftId),
  })

/**
 * Global pool queue для approvers — все review_pending drafts.
 * Backend response: {items, page, size, total}.
 */
export const schemaReviewQueueQuery = (page = 0, size = 50) =>
  queryOptions({
    queryKey: ['schema-review-queue', page, size] as const,
    queryFn: async (): Promise<SchemaReviewQueuePage> => {
      const { data } = await apiClient.get<SchemaReviewQueuePage>(
        '/admin/schema-reviews/pending',
        { params: { page, size } },
      )
      return data
    },
  })

export const useSchemaReviewQueue = (page = 0, size = 50) =>
  useQuery(schemaReviewQueueQuery(page, size))

/**
 * Maker self-tracking — все мои schema drafts (любой статус). Зеркало
 * `myDraftsQuery` для record drafts. Frontend объединяет обе очереди
 * на /my-drafts, чтобы пользователь не запоминал какой тип где.
 */
export const mySchemaDraftsQuery = (page = 0, size = 50) =>
  queryOptions({
    queryKey: ['schema-drafts', 'me', page, size] as const,
    queryFn: async (): Promise<SchemaReviewQueuePage> => {
      const { data } = await apiClient.get<SchemaReviewQueuePage>(
        '/admin/schema-drafts/me',
        { params: { page, size } },
      )
      return data
    },
    refetchInterval: 30_000,
  })

export const useMySchemaDrafts = (page = 0, size = 50) =>
  useQuery(mySchemaDraftsQuery(page, size))

export const recordRawQuery = (dictionaryName: string, businessKey: string) =>
  queryOptions({
    queryKey: ['record-raw', dictionaryName, businessKey] as const,
    queryFn: async (): Promise<RecordResponse> => {
      const { data } = await apiClient.get<RecordResponse>(
        `/dictionaries/${dictionaryName}/records/${encodeURIComponent(businessKey)}`,
      )
      return data
    },
  })

const auditParams = (f: AuditFilters): Record<string, string | number> => {
  const out: Record<string, string | number> = {
    page: f.page ?? 0,
    size: f.size ?? 50,
  }
  if (f.dictionaryName) out.dictionaryName = f.dictionaryName
  if (f.userId) out.userId = f.userId
  if (f.action) out.action = f.action
  if (f.businessKey) out.businessKey = f.businessKey
  if (f.from) out.from = f.from
  if (f.to) out.to = f.to
  return out
}

export const auditQuery = (f: AuditFilters) =>
  queryOptions({
    queryKey: ['audit', f] as const,
    queryFn: async (): Promise<AuditPage> => {
      const { data } = await apiClient.get<AuditPage>('/admin/audit', {
        params: auditParams(f),
      })
      return data
    },
  })

export const useAudit = (f: AuditFilters) => useQuery(auditQuery(f))

export const buildAuditExportUrl = (f: AuditFilters): string => {
  const base = (apiClient.defaults.baseURL ?? '/api/v1').replace(/\/$/, '')
  const sp = new URLSearchParams()
  if (f.dictionaryName) sp.set('dictionaryName', f.dictionaryName)
  if (f.userId) sp.set('userId', f.userId)
  if (f.action) sp.set('action', f.action)
  if (f.businessKey) sp.set('businessKey', f.businessKey)
  if (f.from) sp.set('from', f.from)
  if (f.to) sp.set('to', f.to)
  const qs = sp.toString()
  return `${base}/admin/audit/export.csv${qs ? `?${qs}` : ''}`
}

export const outboxStatsQuery = queryOptions({
  queryKey: ['outbox', 'stats'] as const,
  queryFn: async (): Promise<OutboxStats> => {
    const { data } = await apiClient.get<OutboxStats>('/admin/outbox/stats')
    return data
  },
  refetchInterval: 30_000,
})

export const useOutboxStats = () => useQuery(outboxStatsQuery)

export const dlqQuery = (page: number, size: number) =>
  queryOptions({
    queryKey: ['outbox', 'dlq', page, size] as const,
    queryFn: async (): Promise<DlqPage> => {
      const { data } = await apiClient.get<DlqPage>('/admin/outbox/dlq', {
        params: { page, size },
      })
      return data
    },
  })

export const useDlq = (page: number, size: number) => useQuery(dlqQuery(page, size))

// === Webhooks (v2) ===

export const webhookSubscriptionsQuery = queryOptions({
  queryKey: ['webhooks', 'subscriptions'] as const,
  queryFn: async (): Promise<WebhookSubscription[]> => {
    const { data } = await apiClient.get<WebhookSubscription[]>(
      '/admin/webhooks/subscriptions',
    )
    return data
  },
})

export const useWebhookSubscriptions = () => useQuery(webhookSubscriptionsQuery)

export const webhookSubscriptionQuery = (id: string) =>
  queryOptions({
    queryKey: ['webhooks', 'subscriptions', id] as const,
    queryFn: async (): Promise<WebhookSubscription> => {
      const { data } = await apiClient.get<WebhookSubscription>(
        `/admin/webhooks/subscriptions/${encodeURIComponent(id)}`,
      )
      return data
    },
  })

export const useWebhookSubscription = (id: string) =>
  useQuery({
    ...webhookSubscriptionQuery(id),
    enabled: Boolean(id),
  })

export const webhookDeliveriesQuery = (
  id: string,
  page: number,
  size: number,
  statusFilter?: string,
) =>
  queryOptions({
    queryKey: ['webhooks', 'deliveries', id, page, size, statusFilter ?? null] as const,
    queryFn: async (): Promise<WebhookDeliveryPage> => {
      const params: Record<string, string | number> = { page, size }
      if (statusFilter) params.status = statusFilter
      const { data } = await apiClient.get<WebhookDeliveryPage>(
        `/admin/webhooks/subscriptions/${encodeURIComponent(id)}/deliveries`,
        { params },
      )
      return data
    },
  })

export const useWebhookDeliveries = (
  id: string,
  page: number,
  size: number,
  statusFilter?: string,
) =>
  useQuery({
    ...webhookDeliveriesQuery(id, page, size, statusFilter),
    enabled: Boolean(id),
  })

export const webhookDlqQuery = (page: number, size: number) =>
  queryOptions({
    queryKey: ['webhooks', 'dlq', page, size] as const,
    queryFn: async (): Promise<WebhookDeliveryPage> => {
      const { data } = await apiClient.get<WebhookDeliveryPage>(
        '/admin/webhooks/deliveries/dlq',
        { params: { page, size } },
      )
      return data
    },
  })

export const useWebhookDlq = (page: number, size: number) =>
  useQuery(webhookDlqQuery(page, size))

export const webhookStatsQuery = (
  id: string,
  bucketBy: 'hour' | 'day',
  fromIso?: string,
  toIso?: string,
) =>
  queryOptions({
    queryKey: [
      'webhooks',
      'stats',
      id,
      bucketBy,
      fromIso ?? null,
      toIso ?? null,
    ] as const,
    queryFn: async (): Promise<WebhookDeliveryStats> => {
      const params: Record<string, string> = { bucketBy }
      if (fromIso) params.from = fromIso
      if (toIso) params.to = toIso
      const { data } = await apiClient.get<WebhookDeliveryStats>(
        `/admin/webhooks/subscriptions/${encodeURIComponent(id)}/stats`,
        { params },
      )
      return data
    },
    // Stats для admin dashboard — fresh data важна, но автополлинг мы
    // отключаем (юзер сам refresh'ит / меняет range).
    staleTime: 30 * 1000,
  })

export const useWebhookStats = (
  id: string,
  bucketBy: 'hour' | 'day',
  fromIso?: string,
  toIso?: string,
) =>
  useQuery({
    ...webhookStatsQuery(id, bucketBy, fromIso, toIso),
    enabled: Boolean(id),
  })

// === Dependents (Phase 1 dict-relationships-v2) ===

/**
 * Schema-level reverse FK list для target dict — какие dict.field ссылаются.
 * 5 min staleTime: schema-level changes идут через bundle import (редко).
 *
 * 404 / 403 → пустой list (consistent с serverside scope-hide).
 */
export const dictionaryDependentsQuery = (name: string) =>
  queryOptions({
    queryKey: ['dictionary-dependents', name] as const,
    queryFn: async (): Promise<SchemaDependent[]> => {
      try {
        const { data } = await apiClient.get<SchemaDependent[]>(
          `/dictionaries/${encodeURIComponent(name)}/dependents`,
        )
        return data
      } catch (e: unknown) {
        const status = (e as { response?: { status?: number } })?.response?.status
        if (status === 404 || status === 403) return []
        throw e
      }
    },
    staleTime: 5 * 60_000,
  })

export const useDictionaryDependents = (name: string | undefined) =>
  useQuery({
    ...dictionaryDependentsQuery(name ?? ''),
    enabled: Boolean(name),
  })

/**
 * Record-level dependents — paginated. Disabled пока businessKey пустой
 * (closed drawer / no selection).
 */
export const recordDependentsQuery = (
  dict: string,
  businessKey: string,
  page: number,
  size: number,
) =>
  queryOptions({
    queryKey: ['record-dependents', dict, businessKey, page, size] as const,
    queryFn: async (): Promise<RecordDependentsPage> => {
      const { data } = await apiClient.get<RecordDependentsPage>(
        `/records/${encodeURIComponent(dict)}/${encodeURIComponent(businessKey)}/dependents`,
        { params: { page, size } },
      )
      return data
    },
  })

export const useRecordDependents = (
  dict: string,
  businessKey: string | undefined,
  page = 0,
  size = 50,
) =>
  useQuery({
    ...recordDependentsQuery(dict, businessKey ?? '', page, size),
    enabled: Boolean(businessKey),
  })

/**
 * Cascade preview — read-only план: blockers / warnings / cascade entries.
 * Используется UI'ем перед открытием cascade confirmation dialog.
 *
 * <p>Не cache'ится агрессивно (staleTime=0), потому что dependents
 * быстро меняются — preview всегда должен быть fresh.
 */
export const cascadePreviewQuery = (dict: string, businessKey: string) =>
  queryOptions({
    queryKey: ['cascade-preview', dict, businessKey] as const,
    queryFn: async (): Promise<CascadePlan> => {
      const { data } = await apiClient.get<CascadePlan>(
        `/dictionaries/${encodeURIComponent(dict)}/records/${encodeURIComponent(businessKey)}/cascade-preview`,
      )
      return data
    },
    staleTime: 0,
  })

export const useCascadePreview = (dict: string, businessKey: string | undefined) =>
  useQuery({
    ...cascadePreviewQuery(dict, businessKey ?? ''),
    enabled: Boolean(businessKey),
  })

// === Smart search (CEO plan v1) ===

/**
 * Free-form ILIKE search across all dictionaries (active records only).
 * Min query length = 3 (backend требует для использования trigram index).
 *
 * Min-length guard: throw в queryFn — защищает от prefetch (router loader)
 * вызовов с q="ab" → backend 400. queryFn enforces invariant; useQuery
 * `enabled` gating опциональный для UI shortcut, но не source of truth.
 */
export const SEARCH_MIN_LEN = 3

export const searchQuery = (q: string, size = 100, perDict = 10) =>
  queryOptions({
    queryKey: ['search', q, size, perDict] as const,
    queryFn: async (): Promise<SearchResponse> => {
      if (q.length < SEARCH_MIN_LEN) {
        throw new Error(`search query too short (min ${SEARCH_MIN_LEN} chars)`)
      }
      const { data } = await apiClient.get<SearchResponse>('/search', {
        params: { q, size, perDict },
      })
      return data
    },
    staleTime: 60_000,
  })

export const useSearch = (q: string | undefined, size = 100, perDict = 10) =>
  useQuery({
    ...searchQuery(q ?? '', size, perDict),
    enabled: Boolean(q && q.length >= SEARCH_MIN_LEN),
  })

// === Approval Workflow v2 ===

/** Global reviewer queue — pool model (D3=A). All pending drafts FIFO. */
export const reviewQueueQuery = (page = 0, size = 50) =>
  queryOptions({
    queryKey: ['review-queue', page, size] as const,
    queryFn: async (): Promise<ReviewQueuePage> => {
      const { data } = await apiClient.get<ReviewQueuePage>('/admin/reviews/pending', {
        params: { page, size },
      })
      return data
    },
    refetchInterval: 30_000,
  })

export const useReviewQueue = (page = 0, size = 50) =>
  useQuery(reviewQueueQuery(page, size))

/**
 * Lightweight count для sidebar badge — fetch only metadata (size=1).
 *
 * <p>Combines record-reviews + schema-reviews queues. Возвращает сумму pending
 * drafts обоих видов. Поллится каждые 30s в синк с queue queries — reviewer
 * увидит свежий counter при возврате на app даже если /reviews не открыт.
 *
 * <p>Gated на authenticated — anonymous не должен дёргать admin endpoint'ы
 * (вернут 401, шум в логах). Когда Phase 1d Keycloak roles подключатся,
 * этот gate можно сузить до approver-role (useCanReviewSchema).
 */
export const useReviewsBadgeCount = (enabled = true) => {
  const recordQ = useQuery({
    ...reviewQueueQuery(0, 1),
    enabled,
    // Override interval: badge ОК с 60s — это не deep view, lower frequency
    // снижает background load когда tab idle.
    refetchInterval: 60_000,
  })
  const schemaQ = useQuery({
    ...schemaReviewQueueQuery(0, 1),
    enabled,
    refetchInterval: 60_000,
  })
  const recordCount = recordQ.data?.totalElements ?? 0
  const schemaCount = schemaQ.data?.totalElements ?? 0
  return {
    total: recordCount + schemaCount,
    recordCount,
    schemaCount,
    isLoading: recordQ.isLoading || schemaQ.isLoading,
  }
}

/**
 * Pending drafts на конкретный dict — для бейджа "На review" в records list.
 * Лёгкий poll каждые 30s. Backend возвращает только PENDING (фильтрация в SQL).
 */
export const dictPendingDraftsQuery = (dictName: string) =>
  queryOptions({
    queryKey: ['drafts', 'by-dict', dictName] as const,
    queryFn: async (): Promise<DraftResponse[]> => {
      const { data } = await apiClient.get<DraftResponse[]>(
        `/dictionaries/${encodeURIComponent(dictName)}/records/drafts`,
      )
      return data
    },
    refetchInterval: 30_000,
  })

export const useDictPendingDrafts = (dictName: string | undefined) =>
  useQuery({
    ...dictPendingDraftsQuery(dictName ?? ''),
    enabled: Boolean(dictName),
  })

/**
 * Maker self-tracking — "что я отправил". Все статусы (PENDING/APPROVED/
 * REJECTED/WITHDRAWN), sorted submitted_at DESC. Refetch на 30s — maker
 * увидит когда reviewer approve/reject.
 */
export const myDraftsQuery = (page = 0, size = 50) =>
  queryOptions({
    queryKey: ['drafts', 'me', page, size] as const,
    queryFn: async (): Promise<ReviewQueuePage> => {
      const { data } = await apiClient.get<ReviewQueuePage>('/drafts/me', {
        params: { page, size },
      })
      return data
    },
    refetchInterval: 30_000,
  })

export const useMyDrafts = (page = 0, size = 50) =>
  useQuery(myDraftsQuery(page, size))

/** Single draft by id — for diff viewer drawer. */
export const draftQuery = (id: string) =>
  queryOptions({
    queryKey: ['draft', id] as const,
    queryFn: async (): Promise<DraftResponse> => {
      const { data } = await apiClient.get<DraftResponse>(
        `/drafts/${encodeURIComponent(id)}`,
      )
      return data
    },
  })

export const useDraft = (id: string | undefined) =>
  useQuery({
    ...draftQuery(id ?? ''),
    enabled: Boolean(id),
  })

/** Live record by businessKey — for diff comparison "current vs proposed". */
export const liveRecordQuery = (dict: string, businessKey: string) =>
  queryOptions({
    queryKey: ['live-record', dict, businessKey] as const,
    queryFn: async (): Promise<RecordResponse | null> => {
      try {
        const { data } = await apiClient.get<RecordResponse>(
          `/dictionaries/${encodeURIComponent(dict)}/records/${encodeURIComponent(businessKey)}`,
        )
        return data
      } catch (e: unknown) {
        const status = (e as { response?: { status?: number } })?.response?.status
        if (status === 404) return null
        throw e
      }
    },
  })

export const useLiveRecord = (dict: string | undefined, businessKey: string | undefined) =>
  useQuery({
    ...liveRecordQuery(dict ?? '', businessKey ?? ''),
    enabled: Boolean(dict && businessKey),
  })

export const useDictionaries = () => useQuery(dictionariesQuery)
export const useDictionaryDetail = (name: string | undefined) =>
  useQuery({ ...dictionaryDetailQuery(name ?? ''), enabled: Boolean(name) })
export const useRecords = (
  dictionaryName: string,
  scopeCsv: string,
  filter?: RecordsFilter,
) => useQuery(recordsQuery(dictionaryName, scopeCsv, filter))
export const useRecordRaw = (
  dictionaryName: string,
  businessKey: string | undefined,
) =>
  useQuery({
    ...recordRawQuery(dictionaryName, businessKey ?? ''),
    enabled: Boolean(businessKey),
  })

// ─────────────────────────────────────────────────────────────────────────────
// App version + update detection
// ─────────────────────────────────────────────────────────────────────────────

export type ServerVersion = {
  version: string
  commit: string
  branch: string
  tag: string
  builtAt: string
}

export const serverVersionQuery = queryOptions({
  queryKey: ['version'] as const,
  queryFn: async (): Promise<ServerVersion> => {
    const { data } = await apiClient.get<ServerVersion>('/version')
    return data
  },
  // Poll каждые 5 минут — обновление detection без excessive load.
  // Stale-while-revalidate: даже если нет focus, перезапрашиваем по таймеру.
  refetchInterval: 5 * 60 * 1000,
  refetchIntervalInBackground: true,
  staleTime: 60 * 1000,
})

export const useServerVersion = () => useQuery(serverVersionQuery)
