import { useEffect, useRef, useState } from 'react'
import { useAuth } from '~/lib/auth-context'
import { useData } from '~/lib/data-context'
import { listPublicPublications, unsubscribe, type ResolvedPub, type Subscription } from '~/lib/api-client'
import CenteredMessage from '~/components/CenteredMessage'
import EmptyState from '~/components/EmptyState'
import PageLayout from '~/components/PageLayout'
import Spinner from '~/components/Spinner'
import PublicationCard from '~/components/PublicationCard'
import { SITE_NAME } from '~/lib/site-config'

export function meta() {
  return [{ title: `Subscriptions — ${SITE_NAME}` }]
}

const PAGE_SIZE = 20
const BATCH = 5

function buildByAuthor(subs: Subscription[]) {
  const map = new Map<string, { rkey: string; publicationAtUri: string }[]>()
  for (const s of subs) {
    const list = map.get(s.authorDid) ?? []
    list.push({ rkey: s.rkey, publicationAtUri: s.publicationAtUri })
    map.set(s.authorDid, list)
  }
  return map
}

async function resolvePubs(
  byAuthor: Map<string, { rkey: string; publicationAtUri: string }[]>,
  cancelled: { current: boolean },
): Promise<ResolvedPub[]> {
  const authors = [...byAuthor.entries()]
  const result: ResolvedPub[] = []
  for (let i = 0; i < authors.length; i += BATCH) {
    if (cancelled.current) return result
    const settled = await Promise.allSettled(
      authors.slice(i, i + BATCH).map(async ([did, entries]) => {
        const pubList = await listPublicPublications(did)
        return entries.flatMap(({ rkey, publicationAtUri }) => {
          const pub = pubList.find((p) => p.atUri === publicationAtUri)
          return pub ? [{ ...pub, rkey }] : []
        })
      }),
    )
    for (const r of settled) {
      if (r.status === 'fulfilled') result.push(...r.value)
    }
  }
  return result
}

export default function Subscriptions() {
  const { session, loading: authLoading } = useAuth()
  const {
    subscriptions,
    resolvedPubs,
    resolvedPubsOffset,
    resolvedPubsHasMore,
    removeSubscription,
    setResolvedPubs,
    appendResolvedPubs,
    removeResolvedPub,
  } = useData()
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)
  const [loadingPubs, setLoadingPubs] = useState<Record<string, boolean>>({})
  const sentinelRef = useRef<HTMLDivElement>(null)

  // First page — fires once when subscriptions become available and resolved pubs are not yet cached
  useEffect(() => {
    if (subscriptions === undefined || resolvedPubs !== undefined) return
    if (subscriptions.length === 0) {
      setResolvedPubs([], 0, false)
      return
    }

    const cancelled = { current: false }
    setLoading(true)
    const slice = subscriptions.slice(0, PAGE_SIZE)

    resolvePubs(buildByAuthor(slice), cancelled)
      .then((newPubs) => {
        if (cancelled.current) return
        setResolvedPubs(newPubs, slice.length, slice.length < subscriptions.length)
      })
      .catch((err) => { if (!cancelled.current) setError(err instanceof Error ? err.message : 'Failed to load') })
      .finally(() => { if (!cancelled.current) setLoading(false) })

    return () => { cancelled.current = true }
  }, [subscriptions, resolvedPubs, setResolvedPubs])

  // Subsequent pages — IntersectionObserver on the sentinel
  useEffect(() => {
    if (!resolvedPubsHasMore || loading || !sentinelRef.current || !subscriptions) return
    const sentinel = sentinelRef.current
    const currentOffset = resolvedPubsOffset
    const currentSubs = subscriptions

    const observer = new IntersectionObserver((entries) => {
      if (!entries[0].isIntersecting) return
      observer.disconnect()

      const slice = currentSubs.slice(currentOffset, currentOffset + PAGE_SIZE)
      if (slice.length === 0) {
        appendResolvedPubs([], currentOffset, false)
        return
      }

      const cancelled = { current: false }
      setLoading(true)

      resolvePubs(buildByAuthor(slice), cancelled)
        .then((newPubs) => {
          if (cancelled.current) return
          const nextOffset = currentOffset + slice.length
          appendResolvedPubs(newPubs, nextOffset, nextOffset < currentSubs.length)
        })
        .catch((err) => { if (!cancelled.current) setError(err instanceof Error ? err.message : 'Failed to load more') })
        .finally(() => { if (!cancelled.current) setLoading(false) })
    })

    observer.observe(sentinel)
    return () => observer.disconnect()
  }, [resolvedPubsHasMore, loading, resolvedPubsOffset, subscriptions, appendResolvedPubs])

  async function handleUnsubscribe(pub: ResolvedPub) {
    if (!session) return
    setLoadingPubs((prev) => ({ ...prev, [pub.atUri]: true }))
    try {
      await unsubscribe(session, pub.rkey)
      removeSubscription(pub.rkey)
      removeResolvedPub(pub.atUri)
    } catch {
      // non-fatal
    } finally {
      setLoadingPubs((prev) => ({ ...prev, [pub.atUri]: false }))
    }
  }

  if (authLoading || (session && subscriptions === undefined)) {
    return (
      <div className="flex-1 flex items-center justify-center">
        <Spinner />
      </div>
    )
  }

  if (!session) {
    return <CenteredMessage message="Sign in to view your subscriptions." action={{ label: 'Sign in', to: '/' }} />
  }

  return (
    <PageLayout spacing="8">
      <div>
        <h1 className="font-display text-2xl font-semibold text-stone-900 dark:text-white">Subscriptions</h1>
        <p className="text-sm text-stone-500 dark:text-stone-400 mt-1">Publications you follow</p>
      </div>

      {error && <p className="text-sm text-red-500">{error}</p>}

      {loading && !resolvedPubs && (
        <div className="flex justify-center py-12">
          <Spinner />
        </div>
      )}

      {subscriptions !== undefined && subscriptions.length === 0 && (
        <EmptyState
          message="No subscriptions yet."
          detail="Visit an author's profile and subscribe to their publication."
        />
      )}

      {resolvedPubs && resolvedPubs.length > 0 && (
        <ul className="space-y-2">
          {resolvedPubs.map((pub) => {
            const isLoading = !!loadingPubs[pub.atUri]
            return (
              <PublicationCard
                key={pub.atUri}
                pub={pub}
                action={
                  <button
                    onClick={() => handleUnsubscribe(pub)}
                    disabled={isLoading}
                    className="text-sm px-3 py-1 rounded-full border border-stone-300 dark:border-stone-600 text-stone-600 dark:text-stone-300 hover:border-stone-400 dark:hover:border-stone-500 hover:bg-stone-50 dark:hover:bg-stone-800/50 transition-colors disabled:opacity-50"
                  >
                    {isLoading ? 'Unsubscribing…' : 'Subscribed'}
                  </button>
                }
              />
            )
          })}
        </ul>
      )}

      <div ref={sentinelRef} />

      {loading && resolvedPubs && resolvedPubs.length > 0 && (
        <div className="flex justify-center py-4">
          <Spinner size="md" />
        </div>
      )}

      {!resolvedPubsHasMore && !loading && resolvedPubs && resolvedPubs.length > 0 && (
        <p className="text-center text-sm text-stone-400 dark:text-stone-600 py-4">
          You've reached the end.
        </p>
      )}
    </PageLayout>
  )
}
