import { createContext, useCallback, useContext, useEffect, useState } from 'react'
import { useAuth } from '~/lib/auth-context'
import {
  backfillPosts,
  backfillPublications,
  getUserBlogPosts,
  listPublications,
  listSubscriptions,
  SessionExpiredError,
  type Post,
  type Publication,
  type ResolvedPub,
  type Subscription,
  type SuggestionItem,
} from '~/lib/api-client'

type DataContextValue = {
  publications: Publication[] | undefined
  posts: Post[] | undefined
  subscriptions: Subscription[] | undefined
  resolvedPubs: ResolvedPub[] | undefined
  resolvedPubsOffset: number
  resolvedPubsHasMore: boolean
  setPublications: (pubs: Publication[]) => void
  invalidatePosts: () => void
  removePost: (rkey: string) => void
  removeSubscription: (rkey: string) => void
  setResolvedPubs: (pubs: ResolvedPub[], offset: number, hasMore: boolean) => void
  appendResolvedPubs: (pubs: ResolvedPub[], offset: number, hasMore: boolean) => void
  removeResolvedPub: (atUri: string) => void
  suggestions: SuggestionItem[] | undefined
  setSuggestions: (items: SuggestionItem[]) => void
  removeSuggestion: (rkey: string) => void
}

const DataContext = createContext<DataContextValue | null>(null)

export function useData() {
  const ctx = useContext(DataContext)
  if (!ctx) throw new Error('useData must be used within DataProvider')
  return ctx
}

// Returns publications for the current site's origin, or null while loading.
export function usePublications(): Publication[] | null {
  const { publications } = useData()
  return publications === undefined
    ? null
    : publications.filter((p) => { try { return new URL(p.url).origin === window.location.origin } catch { return false } })
}

export function DataProvider({ children }: { children: React.ReactNode }) {
  const { session, signOut } = useAuth()
  const [publications, setPublications] = useState<Publication[] | undefined>(undefined)
  const [posts, setPosts] = useState<Post[] | undefined>(undefined)
  const [postsRevision, setPostsRevision] = useState(0)
  const [subscriptions, setSubscriptions] = useState<Subscription[] | undefined>(undefined)
  const [resolvedPubs, setResolvedPubsState] = useState<ResolvedPub[] | undefined>(undefined)
  const [resolvedPubsOffset, setResolvedPubsOffset] = useState(0)
  const [resolvedPubsHasMore, setResolvedPubsHasMore] = useState(false)
  const [suggestions, setSuggestionsState] = useState<SuggestionItem[] | undefined>(undefined)

  useEffect(() => {
    if (!session) {
      setPublications(undefined)
      return
    }
    let cancelled = false
    ;(async () => {
      let pubs: Publication[] = []
      try {
        pubs = await listPublications(session)
      } catch (err) {
        if (err instanceof SessionExpiredError) { signOut(); return }
        console.error('Failed to load publications:', err)
      }
      if (pubs.length === 0) {
        try {
          await backfillPublications(session)
          pubs = await listPublications(session)
        } catch (err) {
          if (err instanceof SessionExpiredError) { signOut(); return }
          console.error('Failed to backfill publications:', err)
        }
      }
      if (!cancelled) setPublications(pubs)
    })()
    return () => { cancelled = true }
  }, [session, signOut])

  useEffect(() => {
    if (!session) {
      setPosts(undefined)
      return
    }
    let cancelled = false
    ;(async () => {
      let loaded: Post[] = []
      try {
        loaded = await getUserBlogPosts(session)
      } catch (err) {
        if (err instanceof SessionExpiredError) { signOut(); return }
        console.error('Failed to load posts:', err)
      }
      if (loaded.length === 0) {
        try {
          await backfillPosts(session)
          loaded = await getUserBlogPosts(session)
        } catch (err) {
          if (err instanceof SessionExpiredError) { signOut(); return }
          console.error('Failed to backfill posts:', err)
        }
      }
      if (!cancelled) setPosts(loaded)
    })()
    return () => { cancelled = true }
  }, [session, postsRevision, signOut])

  useEffect(() => {
    if (!session) {
      setSubscriptions(undefined)
      setResolvedPubsState(undefined)
      setResolvedPubsOffset(0)
      setResolvedPubsHasMore(false)
      setSuggestionsState(undefined)
      return
    }
    let cancelled = false
    ;(async () => {
      try {
        const subs = await listSubscriptions(session)
        if (!cancelled) setSubscriptions(subs)
      } catch (err) {
        if (err instanceof SessionExpiredError) { signOut(); return }
        console.error('Failed to load subscriptions:', err)
        if (!cancelled) setSubscriptions([])
      }
    })()
    return () => { cancelled = true }
  }, [session, signOut])

  const handleSetPublications = useCallback((pubs: Publication[]) => setPublications(pubs), [])
  const invalidatePosts = useCallback(() => setPostsRevision((r) => r + 1), [])
  const removePost = useCallback((rkey: string) => {
    setPosts((prev) => prev?.filter((p) => p.atUri.split('/').pop() !== rkey) ?? prev)
  }, [])
  const removeSubscription = useCallback((rkey: string) => {
    setSubscriptions((prev) => prev?.filter((s) => s.rkey !== rkey) ?? prev)
  }, [])
  const setResolvedPubs = useCallback((pubs: ResolvedPub[], offset: number, hasMore: boolean) => {
    setResolvedPubsState(pubs)
    setResolvedPubsOffset(offset)
    setResolvedPubsHasMore(hasMore)
  }, [])
  const appendResolvedPubs = useCallback((pubs: ResolvedPub[], offset: number, hasMore: boolean) => {
    setResolvedPubsState((prev) => [...(prev ?? []), ...pubs])
    setResolvedPubsOffset(offset)
    setResolvedPubsHasMore(hasMore)
  }, [])
  const removeResolvedPub = useCallback((atUri: string) => {
    setResolvedPubsState((prev) => prev?.filter((p) => p.atUri !== atUri) ?? prev)
  }, [])
  const setSuggestions = useCallback((items: SuggestionItem[]) => setSuggestionsState(items), [])
  const removeSuggestion = useCallback((rkey: string) => {
    setSuggestionsState((prev) => prev?.filter((s) => s.rkey !== rkey) ?? prev)
  }, [])

  return (
    <DataContext.Provider value={{ publications, posts, subscriptions, resolvedPubs, resolvedPubsOffset, resolvedPubsHasMore, setPublications: handleSetPublications, invalidatePosts, removePost, removeSubscription, setResolvedPubs, appendResolvedPubs, removeResolvedPub, suggestions, setSuggestions, removeSuggestion }}>
      {children}
    </DataContext.Provider>
  )
}
