import { useEffect, useRef } from 'react'
import {
  useFilterStore,
  type FilterState,
  type MediaType,
  type ColorLabel,
  type FlagFilter,
  type SortField,
  type SortOrder,
} from '../store/filterStore'

const ALLOWED_MEDIA: MediaType[] = ['photo', 'video', 'raw', 'heic']
const ALLOWED_COLORS: ColorLabel[] = [
  'red',
  'orange',
  'yellow',
  'green',
  'blue',
  'purple',
]
const ALLOWED_FLAGS: FlagFilter[] = ['any', 'discarded', 'date_warning']
const ALLOWED_SORT_FIELDS: SortField[] = [
  'taken_at',
  'added_at',
  'filename',
  'file_size',
  'rating',
]
const ALLOWED_SORT_ORDERS: SortOrder[] = ['asc', 'desc']

// What parseUrl returns: a partial filter state, plus the optional
// section id (which lives on the store but isn't part of FilterState
// itself). The hydrate action accepts this exact shape.
type HydratePayload = Partial<FilterState> & { currentSection?: string }

function parseUrl(): HydratePayload {
  const sp = new URLSearchParams(window.location.search)
  const out: HydratePayload = {}

  const q = sp.get('q')
  if (q) out.q = q

  const df = sp.get('date_from')
  if (df) out.dateFrom = df

  const dt = sp.get('date_to')
  if (dt) out.dateTo = dt

  const mt = sp.get('media_type')
  if (mt) {
    const types = mt
      .split(',')
      .filter((t): t is MediaType => ALLOWED_MEDIA.includes(t as MediaType))
    if (types.length > 0) out.mediaTypes = types
  }

  const rm = sp.get('rating_min')
  if (rm) {
    const n = parseInt(rm, 10)
    if (Number.isFinite(n) && n >= 0 && n <= 5) out.ratingMin = n
  }

  const rx = sp.get('rating_max')
  if (rx) {
    const n = parseInt(rx, 10)
    if (Number.isFinite(n) && n >= 0 && n <= 5) out.ratingMax = n
  }

  const cl = sp.get('color_label')
  if (cl && ALLOWED_COLORS.includes(cl as ColorLabel)) {
    out.colorLabel = cl as ColorLabel
  }

  const flag = sp.get('flag')
  if (flag && ALLOWED_FLAGS.includes(flag as FlagFilter)) {
    out.flag = flag as FlagFilter
  }

  const heapId = sp.get('heap_id')
  if (heapId) out.heapId = heapId

  const folderId = sp.get('folder_id')
  if (folderId) out.folderId = folderId

  const tagIds = sp.get('tag_ids')
  if (tagIds) {
    const ids = tagIds.split(',').map((t) => t.trim()).filter(Boolean)
    if (ids.length > 0) out.tagIds = ids
  }

  if (sp.get('duplicates') === 'true') out.duplicates = true
  if (sp.get('needs_review') === 'true') out.needsReview = true

  const groupBy = sp.get('group')
  if (groupBy === 'date' || groupBy === 'tag') out.groupBy = groupBy

  const section = sp.get('section')
  if (section) out.currentSection = section

  const sortBy = sp.get('sort')
  if (sortBy && ALLOWED_SORT_FIELDS.includes(sortBy as SortField)) {
    out.sortBy = sortBy as SortField
  }

  const sortOrder = sp.get('order')
  if (sortOrder && ALLOWED_SORT_ORDERS.includes(sortOrder as SortOrder)) {
    out.sortOrder = sortOrder as SortOrder
  }

  return out
}

function writeUrl(f: FilterState & { currentSection?: string }) {
  const sp = new URLSearchParams()
  if (f.q.trim()) sp.set('q', f.q.trim())
  if (f.dateFrom) sp.set('date_from', f.dateFrom)
  if (f.dateTo) sp.set('date_to', f.dateTo)
  if (f.mediaTypes.length > 0) sp.set('media_type', f.mediaTypes.join(','))
  if (f.ratingMin > 0) sp.set('rating_min', String(f.ratingMin))
  if (f.ratingMax > 0) sp.set('rating_max', String(f.ratingMax))
  if (f.colorLabel) sp.set('color_label', f.colorLabel)
  if (f.flag !== 'any') sp.set('flag', f.flag)
  if (f.heapId) sp.set('heap_id', f.heapId)
  if (f.folderId) sp.set('folder_id', f.folderId)
  if (f.tagIds.length > 0) sp.set('tag_ids', f.tagIds.join(','))
  if (f.duplicates) sp.set('duplicates', 'true')
  if (f.needsReview) sp.set('needs_review', 'true')
  if (f.groupBy !== 'date') sp.set('group', f.groupBy)
  if (f.currentSection && f.currentSection !== 'all-photos')
    sp.set('section', f.currentSection)
  if (f.sortBy !== 'taken_at') sp.set('sort', f.sortBy)
  if (f.sortOrder !== 'desc') sp.set('order', f.sortOrder)

  const search = sp.toString()
  const next = search ? `?${search}` : window.location.pathname
  if (next !== window.location.search && next !== window.location.pathname + window.location.search) {
    window.history.replaceState(null, '', next)
  }
}

/**
 * Bidirectional URL <-> filterStore sync. Hydrates the store from the URL on
 * mount, then writes any subsequent store changes back to the URL via
 * history.replaceState (no navigation).
 */
export function useFilterUrlSync() {
  const hydrated = useRef(false)

  // Hydrate once on mount.
  useEffect(() => {
    const fromUrl = parseUrl()
    if (Object.keys(fromUrl).length > 0) {
      useFilterStore.getState().hydrate(fromUrl)
    }
    hydrated.current = true
  }, [])

  // Mirror store -> URL on every change (after hydration).
  useEffect(() => {
    return useFilterStore.subscribe((state) => {
      if (!hydrated.current) return
      writeUrl(state)
    })
  }, [])
}
