import { useCallback, useRef, useState } from 'react'
import { PostsService } from '../posts/postsService.ts'
import { useUser } from '../../user/user.ts'
import { MediaService } from '../../media/mediaService.ts'
import NewPostWidget from '../../../components/NewPostWidget.tsx'
import SingleColumnLayout from '../../../layouts/SingleColumnLayout.tsx'
import NavBar from '../../../components/NavBar.tsx'
import AuthNavButtons from '../../auth/components/AuthNavButtons.tsx'
import { useSaveSignupCodeToLocalStorage } from '../../../hooks/useSaveSignupCodeToLocalStorage.ts'
import { Post } from '../posts/posts.ts'
import { produce, WritableDraft } from 'immer'
import PostItem from '../components/PostItem.tsx'
import { useIntersectionLoad } from '../../../hooks/useIntersectionLoad.ts'

interface HomePageProps {
  postsService: PostsService
  mediaService: MediaService
}

const PageSize = 20

export default function HomePage({ postsService, mediaService }: HomePageProps) {
  const user = useUser()
  useSaveSignupCodeToLocalStorage()
  const [isSubmitting, setIsSubmitting] = useState(false)

  const fetchPosts = useCallback(
    async (cursor: string | null, amount: number | null) => {
      return postsService.loadPublicFeed(cursor, amount)
    },
    [postsService],
  )

  const [posts, setPosts] = useState<Post[]>([])
  const [hasMore, setHasMore] = useState(true)
  const [error, setError] = useState<string | null>(null)

  const cursor = useRef<string | null>(null)
  const loading = useRef(false)

  const loadNextPage = useCallback(async () => {
    if (loading.current || !hasMore || error) return
    loading.current = true

    try {
      const delay = new Promise((resolve) => setTimeout(resolve, 500))
      const pagePromise = fetchPosts(cursor.current, PageSize)
      const [page] = await Promise.all([pagePromise, delay])
      setHasMore(page.length >= PageSize)
      cursor.current = page.at(-1)?.postId ?? null
      setPosts((prev) => [...prev, ...page])
    } catch (e: unknown) {
      const err = e as Error
      setError(err.message)
    } finally {
      loading.current = false
    }
  }, [fetchPosts, hasMore, error])

  const onCreatePost = useCallback(
    async (
      content: string,
      files: { file: File; width: number; height: number }[],
      isPublic: boolean,
    ) => {
      setIsSubmitting(true)
      if (user == null) throw new Error('Not logged in')
      try {
        const media = await Promise.all(
          files.map(async ({ file, width, height }) => {
            const { mediaId, url } = await mediaService.uploadImage(file)

            return {
              mediaId,
              url,
              width,
              height,
            }
          }),
        )
        const post = await postsService.createNew(user.id, content, media, isPublic)
        setPosts((pages) => [post, ...pages])
      } catch (error) {
        console.error('Failed to create post:', error)
      } finally {
        setIsSubmitting(false)
      }
    },
    [mediaService, postsService, setPosts, user],
  )

  const isLoggedIn = user != null

  const onAddReaction = async (postId: string, emoji: string) => {
    await postsService.addReaction(postId, emoji)

    setPosts((prev) =>
      produce(prev, (draft: WritableDraft<Post[]>) => {
        const post = draft.find((p) => p.postId === postId)
        if (!post) return

        const theReaction = post.reactions.find((r) => r.emoji === emoji)
        if (theReaction) {
          theReaction.count++
          theReaction.didReact = true
        } else {
          post.reactions.push({ emoji, count: 1, didReact: true })
        }
      }),
    )
  }

  const onClearReaction = async (postId: string, emoji: string) => {
    await postsService.removeReaction(postId, emoji)

    setPosts((prev) =>
      produce(prev, (draft: WritableDraft<Post[]>) => {
        const post = draft.find((p) => p.postId === postId)
        if (!post) return

        const theReaction = post.reactions.find((r) => r.emoji === emoji)
        if (theReaction) {
          theReaction.count = Math.max(theReaction.count - 1, 0)
          theReaction.didReact = false
        }
      }),
    )
  }

  const sentinelRef = useRef<HTMLDivElement | null>(null)
  useIntersectionLoad(loadNextPage, sentinelRef)

  return (
    <SingleColumnLayout
      navbar={
        <NavBar>
          <AuthNavButtons />
        </NavBar>
      }
    >
      <main className={`w-full max-w-3xl mx-auto`}>
        {isLoggedIn && <NewPostWidget onSubmit={onCreatePost} isSubmitting={isSubmitting} />}
        <div className="w-full">
          <div className="flex flex-col gap-6 w-full">
            <div className="flex flex-col gap-6 w-full">
              {posts.map((post) => (
                <PostItem
                  key={post.postId}
                  post={post}
                  addReaction={(emoji) => onAddReaction(post.postId, emoji)}
                  clearReaction={(emoji) => onClearReaction(post.postId, emoji)}
                />
              ))}
            </div>
          </div>
          <div ref={sentinelRef} className="h-1" />
        </div>
      </main>
    </SingleColumnLayout>
  )
}
