import { ChevronLeft, ChevronRight } from "lucide-react";
import { Link, useLocation } from "react-router-dom";
import { useEffect, useRef, useState } from "react";
import { getJSON, itemsOrEmpty, readJSONCache, type Category } from "../../api";
import { CategoryIcon } from "../../components/CategoryIcon";
import { FigmaBanner } from "../../components/FigmaBanner";
import { PopularRankList } from "../../components/PopularRankList";
import { RecommendedCard } from "../../components/RecommendedCard";
import { SectionHeader } from "../../components/SectionHeader";
import { MessageBubble } from "../../components/messageStream/MessageBubble";
import { langQuery, useI18n } from "../../i18n";
import { sourceLanguageQuery } from "../../i18nLanguages";
import { cleanCategoryDisplayName } from "../../utils/categoryDisplay";
import {
  postToResource,
  type PostBackedResource,
} from "../../utils/postResourceAdapter";
import type { Post } from "../../types/post";
import { Reveal } from "../../motion";

const FIGMA_CATEGORY_ORDER = [
  "project-ppt",
  "daily-class",
  "official-announcement",
  "academy-materials",
  "global-evangelism",
  "daily-poster",
  "community-tweets",
  "video-hub",
  "subsidy-policy",
  "how-to",
  "official-assets",
  "media-coverage",
  "academy-video",
  "general",
];

function figmaCategoryRank(category: Category): number {
  const index = FIGMA_CATEGORY_ORDER.indexOf(category.slug);
  return index === -1 ? FIGMA_CATEGORY_ORDER.length : index;
}

export function Home() {
  const { t, lang } = useI18n();
  const { hash } = useLocation();
  const [cats, setCats] = useState<Category[]>([]);
  const [rec, setRec] = useState<PostBackedResource[]>([]);
  const [latestPosts, setLatestPosts] = useState<Post[]>([]);
  const [popular, setPopular] = useState<PostBackedResource[]>([]);
  const [popularPosts, setPopularPosts] = useState<Post[]>([]);
  const [err, setErr] = useState<string | null>(null);
  const recRowRef = useRef<HTMLDivElement>(null);
  const categoryRowRef = useRef<HTMLDivElement>(null);
  const [activeCategoryPage, setActiveCategoryPage] = useState(0);
  const [canScrollRec, setCanScrollRec] = useState(false);
  const [recScroll, setRecScroll] = useState({ ratio: 1, progress: 0 });

  useEffect(() => {
    let cancelled = false;
    const langParam = encodeURIComponent(langQuery(lang));
    const languageParam = encodeURIComponent(sourceLanguageQuery(lang));
    const catQ = `?lang=${langParam}`;
    const postQ = `?lang=${langParam}&language=${languageParam}`;
    const categoriesUrl = `/api/categories${catQ}`;
    const recommendedUrl = `/api/posts/recommended${postQ}&limit=12`;
    const latestUrl = `/api/posts${postQ}&sort=latest&limit=12`;
    const popularUrl = `/api/posts${postQ}&sort=popular&limit=5`;

    const applyHomeData = (
      c: Category[],
      r: { items: Post[] },
      l: { items: Post[] },
      p: { items: Post[] },
    ) => {
      const categoryItems = itemsOrEmpty(c);
      setCats(categoryItems);
      setRec(
        itemsOrEmpty(r.items).map((post) =>
          postToResource(post, lang, categoryItems),
        ),
      );
      const latestItems = itemsOrEmpty(l.items);
      setLatestPosts(latestItems);
      const popularItems = itemsOrEmpty<Post>(p.items);
      setPopularPosts(popularItems);
      setPopular(
        popularItems.map((post) => postToResource(post, lang, categoryItems)),
      );
    };

    setErr(null);
    const cachedCategories = readJSONCache<Category[]>(categoriesUrl);
    const cachedRecommended = readJSONCache<{ items: Post[] }>(recommendedUrl);
    const cachedLatest = readJSONCache<{ items: Post[] }>(latestUrl);
    const cachedPopular = readJSONCache<{ items: Post[] }>(popularUrl);
    const showedCached = !!(
      cachedCategories &&
      cachedRecommended &&
      cachedLatest
    );

    if (showedCached) {
      applyHomeData(
        cachedCategories,
        cachedRecommended,
        cachedLatest,
        cachedPopular ?? { items: [] },
      );
    }

    Promise.all([
      getJSON<Category[]>(categoriesUrl),
      getJSON<{ items: Post[] }>(recommendedUrl),
      getJSON<{ items: Post[] }>(latestUrl),
      getJSON<{ items: Post[] }>(popularUrl).catch(
        (): { items: Post[] } => cachedPopular ?? { items: [] },
      ),
    ])
      .then(([c, r, l, p]) => {
        if (cancelled) return;
        applyHomeData(c, r, l, p);
      })
      .catch((e) => {
        if (!cancelled && !showedCached) setErr(String(e));
      });

    return () => {
      cancelled = true;
    };
  }, [lang]);

  const figmaOrderedCategories = [...cats].sort(
    (a, b) => figmaCategoryRank(a) - figmaCategoryRank(b),
  );

  const categoryPages: Category[][] = [];
  for (let index = 0; index < figmaOrderedCategories.length; index += 9) {
    categoryPages.push(figmaOrderedCategories.slice(index, index + 9));
  }
  // Use the tallest page so the carousel height doesn't shrink between
  // pages — otherwise the section below jumps up when swiping to a page
  // with fewer categories.
  const maxCategoryRows = categoryPages.reduce(
    (max, page) => Math.max(max, Math.ceil(page.length / 3)),
    0,
  );
  const mobileCategoryHeight =
    maxCategoryRows * 88 + Math.max(0, maxCategoryRows - 1) * 8;

  useEffect(() => {
    const row = categoryRowRef.current;
    if (!row) return;

    const update = () => {
      const width = row.clientWidth || 1;
      const next = Math.round(row.scrollLeft / width);
      setActiveCategoryPage((prev) => (prev === next ? prev : next));
    };

    update();
    row.addEventListener("scroll", update, { passive: true });
    return () => row.removeEventListener("scroll", update);
  }, [cats.length]);

  useEffect(() => {
    setActiveCategoryPage(0);
    categoryRowRef.current?.scrollTo({ left: 0 });
  }, [lang]);

  useEffect(() => {
    const row = recRowRef.current;
    if (!row) {
      setCanScrollRec(false);
      return;
    }

    const update = () => {
      const overflow = row.scrollWidth > row.clientWidth + 1;
      setCanScrollRec(overflow);
      const ratio = overflow ? row.clientWidth / row.scrollWidth : 1;
      const maxScroll = Math.max(1, row.scrollWidth - row.clientWidth);
      const progress = overflow ? row.scrollLeft / maxScroll : 0;
      setRecScroll({
        ratio: Math.min(1, Math.max(0.15, ratio)),
        progress: Math.min(1, Math.max(0, progress)),
      });
    };

    update();
    const resizeObserver = new ResizeObserver(update);
    resizeObserver.observe(row);
    row.addEventListener("scroll", update, { passive: true });
    return () => {
      resizeObserver.disconnect();
      row.removeEventListener("scroll", update);
    };
  }, [rec.length]);

  const scrollRec = (dir: 1 | -1) => {
    recRowRef.current?.scrollBy({ left: dir * 280, behavior: "smooth" });
  };

  useEffect(() => {
    if (!hash) return;
    const id = hash.slice(1);
    if (!id) return;
    const frame = window.requestAnimationFrame(() => {
      document.getElementById(id)?.scrollIntoView({
        block: id === "latest" ? "center" : "start",
      });
    });
    return () => window.cancelAnimationFrame(frame);
  }, [hash, cats.length, rec.length, latestPosts.length, popular.length]);

  const hasPopular = popular.length > 0 || popularPosts.length > 0;
  const recommendedDotCount = rec.length;
  const activeRecommendedDot =
    recommendedDotCount > 0
      ? Math.min(
          recommendedDotCount - 1,
          Math.round(recScroll.progress * (recommendedDotCount - 1)),
        )
      : 0;

  if (err) {
    return (
      <div className="mt-6 rounded-xl border border-red-900 bg-red-950/40 p-4 text-red-200 md:mt-0">
        {err}
      </div>
    );
  }

  return (
    <div className="pb-4 md:space-y-10 md:pb-16 xl:space-y-[34px]">
      <section className="md:mt-0">
        <FigmaBanner />
      </section>

      <Reveal delay={0}>
        <section id="categories" className="scroll-mt-16 md:scroll-mt-24">
          <div className="px-4 md:px-0">
            <SectionHeader
              title={t("categorySection")}
              viewAllTo="/categories"
              viewAllLabel={t("viewAll")}
            />
          </div>

          <div className="md:hidden">
            <div
              ref={categoryRowRef}
              className="flex snap-x snap-mandatory items-start overflow-x-auto overflow-y-hidden scroll-smooth [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
              style={{ height: `${mobileCategoryHeight}px` }}
              aria-label={t("categorySection")}
            >
              {categoryPages.map((page, pageIndex) => (
                <div
                  key={`category-page-${pageIndex}`}
                  className="grid w-full shrink-0 snap-start grid-cols-3 gap-2 px-4"
                >
                  {page.map((c) => (
                    <Link
                      key={c.id}
                      to={`/category/${encodeURIComponent(c.slug)}`}
                      className="flex h-[88px] min-w-0 flex-col items-center justify-center gap-2 rounded-xl border border-[#27292E] bg-[#1D1E23] px-4 py-3 text-center outline-none transition hover:border-ark-gold/55 hover:bg-[#252630] focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-ark-bg"
                    >
                      <CategoryIcon
                        iconKey={c.iconKey}
                        categorySlug={c.slug}
                        className="h-9 w-9 shrink-0 text-ark-gold"
                      />
                      <div className="w-full truncate text-[13px] font-medium leading-[19.5px] text-white">
                        {cleanCategoryDisplayName(c.name)}
                      </div>
                    </Link>
                  ))}
                </div>
              ))}
            </div>

            {categoryPages.length > 1 ? (
              <div
                className="flex h-[30px] items-center justify-center gap-1.5"
                aria-label="Category pagination"
              >
                {categoryPages.map((_, index) => (
                  <button
                    key={`category-dot-${index}`}
                    type="button"
                    aria-label={`Go to category page ${index + 1}`}
                    aria-current={activeCategoryPage === index}
                    onClick={() => {
                      const row = categoryRowRef.current;
                      if (!row) return;
                      row.scrollTo({
                        left: row.clientWidth * index,
                        behavior: "smooth",
                      });
                      setActiveCategoryPage(index);
                    }}
                    className={`h-1.5 rounded-full transition-all ${
                      activeCategoryPage === index
                        ? "w-6 bg-ark-gold"
                        : "w-1.5 bg-[#7C7C7C]"
                    }`}
                  />
                ))}
              </div>
            ) : null}
          </div>

          <div className="mt-7 hidden grid-cols-3 gap-3 min-[440px]:gap-3.5 md:grid md:grid-cols-5 md:gap-3 lg:grid-cols-6 xl:grid-cols-7 xl:gap-4">
            {figmaOrderedCategories.map((c, index) => (
              <Reveal key={c.id} delay={Math.min(index, 8) * 0.05}>
                <Link
                  to={`/category/${encodeURIComponent(c.slug)}`}
                  className="flex h-[88px] min-w-0 flex-col items-center justify-center gap-2 rounded-xl border border-[#27292E] bg-[#1D1E23] px-4 py-3 text-center outline-none transition hover:border-ark-gold/55 hover:bg-[#252630] focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-ark-bg"
                >
                  <CategoryIcon
                    iconKey={c.iconKey}
                    categorySlug={c.slug}
                    className="h-9 w-9 shrink-0 text-ark-gold"
                  />
                  <div className="w-full text-center text-[13px] font-medium leading-[19.5px] text-white line-clamp-2">
                    {cleanCategoryDisplayName(c.name)}
                  </div>
                </Link>
              </Reveal>
            ))}
          </div>
        </section>
      </Reveal>

      <Reveal>
        <section id="official" className="scroll-mt-16 md:scroll-mt-24">
          <div className="px-4 md:px-0">
            <SectionHeader
              title={t("officialSection")}
              viewAllTo="/official-recommendations"
              viewAllLabel={t("viewAll")}
            />
          </div>
          <div className="relative">
            <div
              ref={recRowRef}
              className="flex gap-3 overflow-x-auto overflow-y-hidden px-4 pb-0 pr-4 scroll-smooth [-ms-overflow-style:none] [scrollbar-width:none] md:mt-7 md:gap-4 md:px-0 md:pr-0 md:pb-5 [&::-webkit-scrollbar]:hidden"
            >
              {rec.map((r, index) => (
                <div key={r.id}>
                  <Reveal delay={Math.min(index, 8) * 0.05}>
                    <RecommendedCard r={r} visualIndex={index} useFigmaDesign />
                  </Reveal>
                </div>
              ))}
            </div>
            <div
              className="flex h-[30px] items-center justify-center gap-1.5 md:hidden"
              aria-label="Recommended pagination"
            >
              {Array.from({ length: recommendedDotCount }).map((_, index) => (
                <button
                  key={`recommended-dot-${index}`}
                  type="button"
                  aria-label={`Go to recommendation page ${index + 1}`}
                  aria-current={activeRecommendedDot === index}
                  onClick={() => {
                    const row = recRowRef.current;
                    if (!row) return;
                    const maxScroll = Math.max(
                      0,
                      row.scrollWidth - row.clientWidth,
                    );
                    row.scrollTo({
                      left:
                        recommendedDotCount === 1
                          ? 0
                          : (maxScroll * index) / (recommendedDotCount - 1),
                      behavior: "smooth",
                    });
                  }}
                  className={`h-1.5 rounded-full transition-all ${
                    activeRecommendedDot === index
                      ? "w-6 bg-ark-gold"
                      : "w-1.5 bg-[#7C7C7C]"
                  }`}
                />
              ))}
            </div>
            {canScrollRec ? (
              <>
                <button
                  type="button"
                  onClick={() => scrollRec(-1)}
                  className="absolute left-0 top-[45%] hidden h-9 w-9 -translate-y-1/2 items-center justify-center rounded-lg border border-ark-line bg-[#292a31]/95 text-neutral-200 shadow-lg backdrop-blur transition hover:border-ark-gold hover:text-ark-gold md:flex"
                  aria-label="Previous recommendations"
                >
                  <ChevronLeft className="h-5 w-5" />
                </button>
                <button
                  type="button"
                  onClick={() => scrollRec(1)}
                  className="absolute right-0 top-[45%] hidden h-9 w-9 -translate-y-1/2 items-center justify-center rounded-lg border border-ark-line bg-[#292a31]/95 text-neutral-200 shadow-lg backdrop-blur transition hover:border-ark-gold hover:text-ark-gold md:flex"
                  aria-label="Next recommendations"
                >
                  <ChevronRight className="h-5 w-5" />
                </button>
              </>
            ) : null}
          </div>
        </section>
      </Reveal>

      <Reveal>
        <section id="latest" className="scroll-mt-16 md:scroll-mt-24">
          <div className="mx-auto max-w-full md:max-w-[820px] lg:max-w-[1080px] xl:max-w-[1180px]">
            <div className="px-4 md:px-0">
              <SectionHeader
                title={t("latestSection")}
                viewAllTo="/browse"
                viewAllLabel={t("viewAll")}
              />
            </div>
            <div className="mt-4 flex flex-col gap-3 md:mt-7">
              {latestPosts.slice(0, 5).map((post, index) => (
                <Reveal key={post.id} delay={Math.min(index, 8) * 0.05}>
                  <MessageBubble post={post} />
                </Reveal>
              ))}
            </div>
          </div>
        </section>
      </Reveal>

      {hasPopular ? (
        <Reveal>
          <section id="popular" className="scroll-mt-16 md:scroll-mt-24">
            <div className="px-4 md:px-0">
              <SectionHeader
                title={t("popularSection")}
                viewAllTo="/browse?sort=popular"
                viewAllLabel={t("viewAll")}
              />
            </div>
            <div className="mt-4 md:mt-7">
              <PopularRankList posts={popularPosts} categories={cats} />
            </div>
          </section>
        </Reveal>
      ) : null}
    </div>
  );
}
