import { useCallback, useEffect, useState } from 'react'
import {
  parseConsent,
  type ConsentCategory,
  type ConsentState,
} from '@platform-modules/content/privacy'

/**
 * Persistence seam for consent. Sync (localStorage/cookie are sync) — an async seam would force
 * loading-state complexity for an adapter that does not exist (YAGNI). Inject a custom impl for
 * cookie/server persistence; the default below uses localStorage.
 */
export interface ConsentStorage {
  load(): string | null
  save(value: string): void
}

const DEFAULT_KEY = 'mod-consent'

/**
 * Default localStorage-backed storage. LAZY: constructs with no `localStorage` access (safe to evaluate
 * as a default during SSR); touches `window.localStorage` only inside load()/save(), each guarded so a
 * SecurityError (storage disabled / private mode) degrades to null / no-op, never a throw.
 */
export function createLocalStorageConsentStorage(key: string = DEFAULT_KEY): ConsentStorage {
  return {
    load() {
      try {
        return window.localStorage.getItem(key)
      } catch {
        return null
      }
    },
    save(value: string) {
      try {
        window.localStorage.setItem(key, value)
      } catch {
        // storage unavailable — consent simply not persisted; never throw
      }
    },
  }
}

export interface UseConsentOptions {
  /** The CONFIGURED policy version. Stored consent whose version ≠ this is treated as absent (re-prompt). */
  version: string
  /** Defaults to a localStorage-backed store. */
  storage?: ConsentStorage
}

export interface UseConsentResult {
  /** false during SSR + first client render; true after the mount effect (prevents hydration mismatch). */
  ready: boolean
  /** version-matched stored consent, else null. */
  consent: ConsentState | null
  save(categories: Record<ConsentCategory, boolean>): ConsentState
  acceptAll(): ConsentState
  rejectAll(): ConsentState
}

/** Read + parse + version-gate. Fail-safe: any throw or mismatch → null (no-consent), never a crash. */
function readStored(storage: ConsentStorage, version: string): ConsentState | null {
  let raw: string | null
  try {
    raw = storage.load()
  } catch {
    return null
  }
  if (raw == null) return null
  let parsed: unknown
  try {
    parsed = JSON.parse(raw)
  } catch {
    return null
  }
  const state = parseConsent(parsed)
  if (state == null) return null
  // Version gate lives HERE — parseConsent returns whatever version is stored, it does not compare.
  return state.version === version ? state : null
}

export function useConsent(options: UseConsentOptions): UseConsentResult {
  const { version } = options
  // Stable storage instance; lazy initializer runs once, no localStorage touch at construction.
  const [storage] = useState<ConsentStorage>(
    () => options.storage ?? createLocalStorageConsentStorage(),
  )
  const [ready, setReady] = useState(false)
  const [consent, setConsent] = useState<ConsentState | null>(null)

  // SSR-safe: never read storage during render. First SSR + first client render are byte-identical
  // (ready=false, consent=null). Hydrate after mount.
  useEffect(() => {
    setConsent(readStored(storage, version))
    setReady(true)
  }, [storage, version])

  const persist = useCallback(
    (categories: Record<ConsentCategory, boolean>): ConsentState => {
      const state: ConsentState = {
        categories: { ...categories, necessary: true },
        recordedAt: new Date().toISOString(),
        version,
      }
      storage.save(JSON.stringify(state))
      setConsent(state)
      return state
    },
    [storage, version],
  )

  const save = useCallback(
    (categories: Record<ConsentCategory, boolean>) => persist(categories),
    [persist],
  )
  const acceptAll = useCallback(
    () => persist({ necessary: true, analytics: true, marketing: true, preferences: true }),
    [persist],
  )
  const rejectAll = useCallback(
    () => persist({ necessary: true, analytics: false, marketing: false, preferences: false }),
    [persist],
  )

  return { ready, consent, save, acceptAll, rejectAll }
}
