import { useCallback, useEffect, useRef, useState } from 'react'
import type { StoredSubmission } from '@platform-modules/forms'
import { FormsClientError } from './errors.js'
import type { SubmissionClient } from './seams.js'

function toError(e: unknown): FormsClientError {
  if (e instanceof FormsClientError) return e
  if (e instanceof Error) return new FormsClientError(e.message)
  return new FormsClientError(String(e))
}

export interface UseSubmissionsOpts {
  limit?: number
  cursor?: string
  includeSpam?: boolean
}

export function useSubmissions(client: SubmissionClient, formId: string, opts?: UseSubmissionsOpts) {
  const [items, setItems] = useState<StoredSubmission[]>([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<FormsClientError | null>(null)
  const [hasMore, setHasMore] = useState(false)
  const [reloadToken, setReloadToken] = useState(0)

  const clientRef = useRef(client)
  clientRef.current = client
  const nextCursorRef = useRef<string | undefined>(undefined)
  const optsKey = JSON.stringify(opts ?? {})

  const reload = useCallback(() => {
    setReloadToken((t) => t + 1)
  }, [])

  useEffect(() => {
    let cancelled = false
    setLoading(true)
    setError(null)
    nextCursorRef.current = undefined

    void clientRef.current
      .list(formId, opts)
      .then((page) => {
        if (!cancelled) {
          setItems(page.items)
          nextCursorRef.current = page.nextCursor
          setHasMore(Boolean(page.nextCursor))
          setLoading(false)
        }
      })
      .catch((e: unknown) => {
        if (!cancelled) {
          setError(toError(e))
          setLoading(false)
        }
      })

    return () => {
      cancelled = true
    }
  }, [formId, optsKey, reloadToken])

  const loadMore = useCallback(async () => {
    const cursor = nextCursorRef.current
    if (!cursor) return

    try {
      const page = await clientRef.current.list(formId, { ...opts, cursor })
      nextCursorRef.current = page.nextCursor
      setHasMore(Boolean(page.nextCursor))
      setItems((prev) => [...prev, ...page.items])
    } catch (e: unknown) {
      setError(toError(e))
    }
  }, [formId, optsKey])

  const remove = useCallback(
    async (id: string) => {
      const snapshot = items

      setItems((prev) => prev.filter((item) => item.id !== id))

      try {
        await clientRef.current.delete(formId, id)
      } catch (e: unknown) {
        setItems(snapshot)
        const err = toError(e)
        setError(err)
        throw err
      }
    },
    [formId, items],
  )

  return { items, loading, error, hasMore, loadMore, remove, reload }
}
