"use client";

import Link from "next/link";
import { Fragment } from "react";
import { useEffect, useEffectEvent, useRef, useState } from "react";
import { AdsenseContentAd } from "@/components/adsense-content-ad";
import { translateStaticUiText } from "@/lib/i18n-dictionaries";
import { getLocaleOption, type SupportedLocale } from "@/lib/i18n-config";
import { getLocalizedPublicPath } from "@/lib/i18n-urls";
import type { MarketingBlogPostListItem } from "@/lib/marketing-blog";

type BlogFeedResponse = {
  hasMore: boolean;
  posts: MarketingBlogPostListItem[];
};

const PAGE_SIZE = 6;

function formatDate(value: string | null, locale: SupportedLocale) {
  if (!value) return "";

  return new Intl.DateTimeFormat(getLocaleOption(locale).htmlLang, {
    year: "numeric",
    month: "long",
    day: "numeric",
  }).format(new Date(value));
}

export function PublicBlogFeed({
  adSlot = null,
  initialHasMore,
  initialPosts,
  locale = "ko",
}: {
  adSlot?: string | null;
  initialHasMore: boolean;
  initialPosts: MarketingBlogPostListItem[];
  locale?: SupportedLocale;
}) {
  const [posts, setPosts] = useState(initialPosts);
  const [hasMore, setHasMore] = useState(initialHasMore);
  const [loadError, setLoadError] = useState(false);
  const [loading, setLoading] = useState(false);
  const postsRef = useRef(initialPosts);
  const hasMoreRef = useRef(initialHasMore);
  const loadingRef = useRef(false);
  const sentinelRef = useRef<HTMLDivElement | null>(null);

  async function requestNextPage() {
    if (loadingRef.current || !hasMoreRef.current) return;

    loadingRef.current = true;
    setLoading(true);
    setLoadError(false);

    try {
      const response = await fetch(`/api/blog?offset=${postsRef.current.length}&limit=${PAGE_SIZE}`);

      if (!response.ok) {
        throw new Error("blog-feed-fetch-failed");
      }

      const page = (await response.json()) as BlogFeedResponse;
      hasMoreRef.current = page.hasMore;
      setHasMore(page.hasMore);
      setPosts((current) => {
        const knownIds = new Set(current.map((post) => post.id));
        const next = [...current, ...page.posts.filter((post) => !knownIds.has(post.id))];
        postsRef.current = next;
        return next;
      });
    } catch {
      setLoadError(true);
    } finally {
      loadingRef.current = false;
      setLoading(false);
    }
  }

  const loadMoreFromIntersection = useEffectEvent(() => {
    void requestNextPage();
  });

  useEffect(() => {
    const sentinel = sentinelRef.current;

    if (!sentinel || !hasMore) return;

    const observer = new IntersectionObserver(
      (entries) => {
        if (entries.some((entry) => entry.isIntersecting)) {
          loadMoreFromIntersection();
        }
      },
      { rootMargin: "420px 0px" },
    );

    observer.observe(sentinel);
    return () => observer.disconnect();
  }, [hasMore]);

  return (
    <div className="border-t border-[#dfe8f6]">
      {posts.map((post, index) => (
        <Fragment key={post.id}>
          <article className="border-b border-[#edf1f6]">
            <Link
              href={getLocalizedPublicPath(`/blog/${post.slug}`, locale)}
              className="group grid gap-3 py-6 transition-colors hover:bg-[#fafcff] sm:grid-cols-[minmax(132px,180px)_minmax(0,1fr)_auto] sm:items-center sm:gap-7 sm:py-8"
            >
              <div className="flex items-center justify-between gap-3 sm:block">
                <p className="truncate text-[12px] font-semibold text-[#2f6df6]">{post.keywords[0] || translateStaticUiText(locale, "기업메일")}</p>
                <time className="shrink-0 text-[12px] text-[#8492a6] sm:mt-2 sm:block" dateTime={post.publishedAt ?? post.updatedAt}>
                  {formatDate(post.publishedAt ?? post.updatedAt, locale)}
                </time>
              </div>
              <div className="min-w-0">
                <h2 className="text-[21px] font-semibold leading-[1.35] tracking-[-0.05em] text-[#243555] transition-colors group-hover:text-[#2f6df6] sm:text-[27px]">
                  {post.title}
                </h2>
                <p className="mt-2 line-clamp-2 max-w-[820px] text-[14px] leading-6 text-[#70819d] sm:mt-3 sm:text-[15px] sm:leading-7">{post.excerpt}</p>
              </div>
              <span className="inline-flex items-center self-start pt-1 text-[13px] font-semibold text-[#62738d] transition-colors group-hover:text-[#2f6df6] sm:self-center sm:pt-0">
                {translateStaticUiText(locale, "읽기")} <span aria-hidden="true" className="ml-1.5 transition-transform group-hover:translate-x-1">→</span>
              </span>
            </Link>
          </article>
          {(index === 2 || index === 8) && adSlot ? (
            <AdsenseContentAd className="mx-0" format="in-feed" locale={locale} slot={adSlot} />
          ) : null}
        </Fragment>
      ))}

      <div ref={sentinelRef} className="flex min-h-20 items-center justify-center" aria-live="polite">
        {loading ? <span className="text-[13px] text-[#8190a6]">{translateStaticUiText(locale, "최신 글을 불러오는 중입니다.")}</span> : null}
        {!loading && loadError ? (
          <button type="button" onClick={() => void requestNextPage()} className="text-[13px] font-semibold text-[#2f6df6] hover:text-[#174fca]">
            {translateStaticUiText(locale, "글을 다시 불러오기")}
          </button>
        ) : null}
        {!loading && !hasMore && posts.length > 0 ? <span className="text-[12px] text-[#9aa7b8]">{translateStaticUiText(locale, "모든 글을 확인했습니다.")}</span> : null}
      </div>
    </div>
  );
}
