import { useCallback, useEffect, useRef, useState } from 'react'
import { useSettings } from '../store/settings'
// Pure URL helpers extracted to a standalone module so they can be tested
// without pulling in the Zustand store (L40). Re-exported here for existing
// consumers (DownloadBar, LibraryView) without an import-path change.
export {
  looksLikeUrl,
  looksLikeSingleVideo,
  looksLikeChannelOrPlaylist,
  firstUrlInText
} from './urlHelpers'
import { looksLikeUrl } from './urlHelpers'

/** Where an offered link came from — drives the banner's wording. */
export type SuggestionSource = 'clipboard' | 'external'

interface ClipboardLink {
  /** the link currently being offered, or null */
  suggestion: string | null
  /** where `suggestion` came from: a clipboard guess vs. a link handed in from outside */
  source: SuggestionSource
  /** accept the suggestion: clears it and returns the link (or null if none) */
  accept: () => string | null
  /** dismiss the suggestion without using it */
  dismiss: () => void
  /**
   * Offer a link from outside the clipboard — e.g. the aerofetch:// protocol or a
   * "Send to" .url file. Always takes priority over a clipboard guess and is shown
   * even when clipboard-watch is off (it's a direct request, not a guess) and even
   * while the field has text (the banner only fills the field once accepted).
   * Stable identity, so it's safe to use as an effect dependency.
   */
  offer: (url: string, source?: SuggestionSource) => void
}

/**
 * Watches the clipboard on window focus and offers a freshly-copied http(s) link.
 * Shared by the download bar and the library's add-source field so both fields
 * can auto-suggest a copied URL. Respects the `watchClipboard` setting and never
 * interrupts text the user is already typing — pass the field's current value as
 * `currentValue` and the watcher stays quiet while it's non-empty.
 *
 * Pass `filter` to narrow what the clipboard offers — e.g. the library skips
 * single-video links so it only suggests channels/playlists. It gates only the
 * clipboard guess; links pushed via `offer()` bypass it (and the other guards),
 * since they're explicit requests rather than guesses.
 */
// Module-level (not a per-instance ref) so a dismissed/accepted link stays
// suppressed across hook remounts — switching Downloads↔Library remounts the hook,
// and the same clipboard link shouldn't be re-offered after being dismissed (L145).
let lastSeen: string | null = null

export function useClipboardLink(
  currentValue: string,
  filter?: (url: string) => boolean
): ClipboardLink {
  const [suggestion, setSuggestion] = useState<string | null>(null)
  const [source, setSource] = useState<SuggestionSource>('clipboard')
  const valueRef = useRef('')
  valueRef.current = currentValue
  // Held in a ref so an inline `filter` doesn't have to be memoized to keep the
  // focus listener from re-subscribing every render.
  const filterRef = useRef(filter)
  filterRef.current = filter
  // Mirror the live suggestion so accept/dismiss can read it without depending on
  // it — keeps their identity stable across renders.
  const suggestionRef = useRef<string | null>(null)
  suggestionRef.current = suggestion
  // Re-run the check once persisted settings arrive, so a link copied before launch
  // is still offered (the mount check is a no-op until `loaded`, L138).
  const loaded = useSettings((s) => s.loaded)

  useEffect(() => {
    let active = true
    async function check(): Promise<void> {
      const s = useSettings.getState()
      // Don't touch the clipboard until real settings have loaded (L138): before
      // that we'd be acting on the pre-load fallback for watchClipboard instead of
      // the user's actual choice — and we never read it when the watcher is off.
      if (!s.loaded || !s.watchClipboard || valueRef.current.trim()) return
      let text = ''
      try {
        text = (await window.api.readClipboard()) ?? ''
      } catch {
        return
      }
      if (!active) return
      text = text.trim()
      const wanted = looksLikeUrl(text) && (filterRef.current?.(text) ?? true)
      if (wanted && text !== lastSeen) {
        setSource('clipboard')
        setSuggestion(text)
      }
    }
    check()
    window.addEventListener('focus', check)
    return () => {
      active = false
      window.removeEventListener('focus', check)
    }
  }, [loaded])

  const accept = useCallback((): string | null => {
    const link = suggestionRef.current
    if (!link) return null
    lastSeen = link
    setSuggestion(null)
    return link
  }, [])

  const dismiss = useCallback((): void => {
    lastSeen = suggestionRef.current
    setSuggestion(null)
  }, [])

  const offer = useCallback((url: string, src: SuggestionSource = 'external'): void => {
    setSource(src)
    setSuggestion(url)
  }, [])

  return { suggestion, source, accept, dismiss, offer }
}
