/**
 * useAppUpdate — detects whether a newer web version is available.
 *
 * Strategy:
 * 1. Fetch /version.json (a lightweight manifest written at build time) to get
 *    the current deployed version.
 * 2. Poll or re-check after visibility change / reconnect so returning users
 *    see the banner quickly.
 * 3. Cache the latest-known version in module state so the fetch only fires
 *    once per page load. Re-fires on focus if >1 h has passed.
 * 4. Fully offline-safe: any fetch failure leaves hasUpdate=false.
 *
 * The /version.json file is generated by the Vite build plugin below.
 * If the file is absent (local dev without the plugin), the hook no-ops.
 */
import { useCallback, useEffect, useRef, useState } from 'react'

const VERSION_URL = '/version.json'
const RECHECK_INTERVAL_MS = 60 * 60 * 1000 // 1 hour
const RELEASE_NOTES_BASE = 'https://docs.lenserfight.com/changelog'

interface VersionManifest {
  version: string
  buildId?: string
  channel?: string
}

interface AppUpdateState {
  hasUpdate: boolean
  latestVersion: string
  currentVersion: string
  releaseNotesUrl: string
}

// Module-level cache so multiple hook instances share the same fetch
let _cachedManifest: VersionManifest | null = null
let _lastCheckedAt = 0

async function fetchVersionManifest(): Promise<VersionManifest | null> {
  const now = Date.now()
  if (_cachedManifest && now - _lastCheckedAt < RECHECK_INTERVAL_MS) {
    return _cachedManifest
  }
  try {
    const res = await fetch(`${VERSION_URL}?t=${now}`, {
      cache: 'no-store',
      signal: AbortSignal.timeout(5000),
    })
    if (!res.ok) return null
    const data = (await res.json()) as VersionManifest
    if (typeof data.version !== 'string') return null
    _cachedManifest = data
    _lastCheckedAt = now
    return data
  } catch {
    return null
  }
}

function parseVersion(v: string): [number, number, number, string] {
  const clean = v.replace(/^v/, '')
  const [core, pre = ''] = clean.split('-', 2)
  const parts = (core ?? '').split('.').map(Number)
  return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0, pre]
}

function isNewer(installed: string, candidate: string): boolean {
  if (installed === candidate) return false
  const [iMaj, iMin, iPat, iPre] = parseVersion(installed)
  const [cMaj, cMin, cPat, cPre] = parseVersion(candidate)
  if (cMaj !== iMaj) return cMaj > iMaj
  if (cMin !== iMin) return cMin > iMin
  if (cPat !== iPat) return cPat > iPat
  if (iPre && !cPre) return true
  if (!iPre && cPre) return false
  return cPre > iPre
}

/** App version injected at build time via Vite define. Falls back gracefully. */
const CURRENT_VERSION: string =
  (typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : null) ?? '0.0.0-dev'

declare const __APP_VERSION__: string | undefined

export function useAppUpdate(): AppUpdateState {
  const [state, setState] = useState<AppUpdateState>({
    hasUpdate: false,
    latestVersion: CURRENT_VERSION,
    currentVersion: CURRENT_VERSION,
    releaseNotesUrl: RELEASE_NOTES_BASE,
  })

  const lastCheckedAtRef = useRef(0)

  const check = useCallback(async () => {
    const now = Date.now()
    if (now - lastCheckedAtRef.current < RECHECK_INTERVAL_MS) return
    lastCheckedAtRef.current = now

    const manifest = await fetchVersionManifest()
    if (!manifest) return

    const hasUpdate = isNewer(CURRENT_VERSION, manifest.version)
    setState({
      hasUpdate,
      latestVersion: manifest.version,
      currentVersion: CURRENT_VERSION,
      releaseNotesUrl: RELEASE_NOTES_BASE,
    })
  }, [])

  // Initial check on mount
  useEffect(() => {
    check()
  }, [check])

  // Re-check when the tab regains focus (user returns after time away)
  useEffect(() => {
    const onVisibilityChange = () => {
      if (document.visibilityState === 'visible') check()
    }
    document.addEventListener('visibilitychange', onVisibilityChange)
    return () => document.removeEventListener('visibilitychange', onVisibilityChange)
  }, [check])

  // Re-check when browser regains network connectivity
  useEffect(() => {
    window.addEventListener('online', check)
    return () => window.removeEventListener('online', check)
  }, [check])

  return state
}
