'use client'

// ── Sync hooks ───────────────────────────────────────────────────────────────
// React hooks that expose sync state and trigger sync operations.
// All hooks are client-only (they access IndexedDB + the sync engine).

import { useState, useEffect, useCallback, useRef } from 'react'
import { useLiveQuery } from 'dexie-react-hooks'
import { useOnlineStatus } from './connection'
import { fullSync, startSync, isSyncing } from './sync-engine'
import { getPendingCount, getFailedCount, retryFailed } from './queue'
import { getDB } from './db'
import type { SyncResult } from './types'

// ── Auto-sync intervals ───────────────────────────────────────────────────────

const BACKGROUND_PULL_INTERVAL_MS = 5 * 60 * 1000   // 5 minutes
const VISIBILITY_TRIGGER_AFTER_MS = 60 * 1000        // 1 minute away from tab

// ── useSyncStatus ─────────────────────────────────────────────────────────────

export interface SyncStatusState {
  pendingCount:    number
  failedCount:     number
  isSyncing:       boolean
  lastSyncResult:  SyncResult | null
  lastSyncAt:      string | null
  triggerSync:     () => Promise<void>
  triggerRetry:    () => Promise<void>
}

/**
 * Central hook for sync state.
 * Returns counts, sync status, last result, and manual trigger functions.
 */
export function useSyncStatus(): SyncStatusState {
  const [syncing,         setSyncing]         = useState(false)
  const [lastSyncResult,  setLastSyncResult]  = useState<SyncResult | null>(null)
  const [lastSyncAt,      setLastSyncAt]      = useState<string | null>(null)
  const [mounted,         setMounted]         = useState(false)

  useEffect(() => setMounted(true), [])

  const pendingCount = useLiveQuery(
    () => mounted ? getPendingCount() : Promise.resolve(0),
    [mounted],
    0,
  ) ?? 0

  const failedCount = useLiveQuery(
    () => mounted ? getFailedCount() : Promise.resolve(0),
    [mounted],
    0,
  ) ?? 0

  const triggerSync = useCallback(async () => {
    if (syncing || isSyncing()) return
    setSyncing(true)
    try {
      const result = await fullSync()
      setLastSyncResult(result)
      setLastSyncAt(new Date().toISOString())
    } finally {
      setSyncing(false)
    }
  }, [syncing])

  const triggerRetry = useCallback(async () => {
    await retryFailed()
    await triggerSync()
  }, [triggerSync])

  return {
    pendingCount,
    failedCount,
    isSyncing: syncing,
    lastSyncResult,
    lastSyncAt,
    triggerSync,
    triggerRetry,
  }
}

// ── useSyncOnReconnect ────────────────────────────────────────────────────────

/**
 * Automatically triggers a full sync when the app comes back online,
 * on a 5-minute background interval, and on tab focus after 1+ minute away.
 *
 * Place this hook once in a top-level layout component (e.g. AppShell).
 */
export function useSyncOnReconnect() {
  const { isOnline } = useOnlineStatus()
  const { triggerSync } = useSyncStatus()
  const wasOffline   = useRef(false)
  const hiddenAt     = useRef<number | null>(null)

  // Trigger on reconnect
  useEffect(() => {
    if (!isOnline) {
      wasOffline.current = true
    } else if (wasOffline.current) {
      wasOffline.current = false
      triggerSync()
    }
  }, [isOnline, triggerSync])

  // 5-minute background pull while online
  useEffect(() => {
    if (!isOnline) return
    const interval = setInterval(() => { triggerSync() }, BACKGROUND_PULL_INTERVAL_MS)
    return () => clearInterval(interval)
  }, [isOnline, triggerSync])

  // Tab focus after >1 minute away
  useEffect(() => {
    function handleVisibilityChange() {
      if (document.hidden) {
        hiddenAt.current = Date.now()
      } else {
        const away = hiddenAt.current ? Date.now() - hiddenAt.current : 0
        if (away >= VISIBILITY_TRIGGER_AFTER_MS && isOnline) {
          triggerSync()
        }
        hiddenAt.current = null
      }
    }
    document.addEventListener('visibilitychange', handleVisibilityChange)
    return () => document.removeEventListener('visibilitychange', handleVisibilityChange)
  }, [isOnline, triggerSync])
}

// ── useEntitySync ─────────────────────────────────────────────────────────────

export interface EntitySyncState {
  pendingCount: number
  conflictCount: number
  isSyncing: boolean
  triggerSync: () => Promise<void>
}

/**
 * Per-entity sync state hook.
 * Returns the pending and conflict counts for a specific module/entityType combination.
 *
 * @example
 * const { pendingCount } = useEntitySync('assessment', 'responses')
 */
export function useEntitySync(module: string, entityType: string): EntitySyncState {
  const [mounted, setMounted] = useState(false)
  const [syncing, setSyncing] = useState(false)

  // Map module+entity → table name (must match sync-engine MODULE_ENTITY_TABLE)
  const tableMap: Record<string, Record<string, string>> = {
    assessment: { responses: 'assessment_responses', list: 'assessments' },
    planning:   { assignments: 'assignments' },
  }
  const table = tableMap[module]?.[entityType] ?? ''

  useEffect(() => setMounted(true), [])

  const pendingCount = useLiveQuery(
    () => mounted && table
      ? getDB().syncQueue.where('[table+status]').equals([table, 'pending']).count().catch(() =>
          // Fallback: compound index may not exist — filter manually
          getDB().syncQueue.where('status').equals('pending').toArray()
            .then(rows => rows.filter(r => r.table === table).length)
        )
      : Promise.resolve(0),
    [mounted, table],
    0,
  ) ?? 0

  const conflictCount = useLiveQuery(
    () => mounted && table
      ? getDB().syncConflicts.where('table').equals(table).count()
      : Promise.resolve(0),
    [mounted, table],
    0,
  ) ?? 0

  const triggerSync = useCallback(async () => {
    if (syncing) return
    setSyncing(true)
    try {
      await startSync()
    } finally {
      setSyncing(false)
    }
  }, [syncing])

  return { pendingCount, conflictCount, isSyncing: syncing, triggerSync }
}
