"use client";

import Image from "next/image";
import { useRouter } from "next/navigation";
import { useInfiniteQuery } from "@tanstack/react-query";
import { fetchPosts } from "@/api/fetchPosts";
import {
  AcademyExploreItem,
  fetchAcademyExplore,
} from "@/api/fetchAcademyExplore";
import { Post } from "@/types/types";
import Cookies from "js-cookie";
import { trackExploreInteraction } from "@/api/trackExploreInteraction";
import { useCallback, useEffect, useMemo, useRef } from "react";
import { navigateWithSavedScroll } from "@/lib/listScrollRestoration";
import { useListScrollRestoration } from "@/hooks/useListScrollRestoration";
import BoldIcon from "@/components/ui/BoldIcon";
import { formatLikeDisplay } from "@/lib/formatLikeDisplay";
import ExploreAuthor from "@/components/explore/ExploreAuthor";
import ExploreAcademyCard from "@/components/explore/ExploreAcademyCard";
import { ExploreFilterId } from "@/constants/exploreFilters";
import { buildExplorePostFilters } from "@/lib/explore/buildPostFilters";
import {
  isPublicAcademyStatus,
  isPublicPostStatus,
} from "@/lib/moderationStatus";
import { getPostMedia } from "@/lib/explore/postMedia";
import ExploreVideoThumb from "@/components/explore/ExploreVideoThumb";
import { cacheReelsSeedPost } from "@/lib/reelsSeedPost";
import { buildPostPath } from "@/lib/postSlug";
import { useTranslation } from "react-i18next";
import { useAppLanguage } from "@/contexts/LanguageProvider";

function formatViewCount(count: number, locale: string): string {
  const n = Math.max(0, Math.floor(Number(count) || 0));
  if (n >= 1_000_000) {
    const v = n / 1_000_000;
    return `${v >= 10 ? Math.round(v) : v.toFixed(1).replace(/\.0$/, "")}M`;
  }
  if (n >= 10_000) {
    return `${Math.round(n / 1000)}K`;
  }
  if (n >= 1000) {
    const v = n / 1000;
    return `${v.toFixed(1).replace(/\.0$/, "")}K`;
  }
  return n.toLocaleString(locale);
}

const ASPECT_RATIOS = [
  "aspect-[2/3]",
  "aspect-[3/4]",
  "aspect-[4/5]",
  "aspect-[5/6]",
  "aspect-square",
  "aspect-[3/5]",
] as const;

const SKELETON_COUNT = 8;

type ExploreFeedItem =
  | { kind: "post"; id: string; sortAt: number; post: Post }
  | { kind: "academy"; id: string; sortAt: number; academy: AcademyExploreItem };

type ExploreGridProps = {
  activeFilter: ExploreFilterId;
  coords?: { lat: number; lng: number } | null;
  searchQuery?: { q?: string; hashtag?: string };
};

function hashAspect(postId: string): (typeof ASPECT_RATIOS)[number] {
  let hash = 0;
  for (let i = 0; i < postId.length; i++) {
    hash = (hash + postId.charCodeAt(i)) % ASPECT_RATIOS.length;
  }
  return ASPECT_RATIOS[hash];
}

function toSortTime(value?: string): number {
  if (!value) return 0;
  const time = Date.parse(value);
  return Number.isNaN(time) ? 0 : time;
}

function interleaveAcademyGrid<T extends { sortAt: number }>(
  posts: T[],
  academy: T[],
  page: number
): T[] {
  if (!academy.length) return posts;

  const result: T[] = [];
  let academyIdx = 0;
  const interval = 4;

  posts.forEach((post, index) => {
    result.push(post);
    const globalIndex = (page - 1) * posts.length + index;
    if ((globalIndex + 1) % interval === 0 && academyIdx < academy.length) {
      result.push(academy[academyIdx]);
      academyIdx += 1;
    }
  });

  while (academyIdx < academy.length) {
    result.push(academy[academyIdx]);
    academyIdx += 1;
  }

  return result;
}

function ExploreCardSkeleton({ seed }: { seed: number }) {
  const aspect = ASPECT_RATIOS[seed % ASPECT_RATIOS.length];
  return (
    <article className="mb-2 break-inside-avoid">
      <div
        className={`relative w-full overflow-hidden rounded-2xl bg-neutral-200 dark:bg-neutral-800 ${aspect} animate-pulse`}
      />
      <div className="flex items-center justify-between gap-2 px-0.5 py-1.5">
        <div className="h-4 w-24 animate-pulse rounded-full bg-neutral-200 dark:bg-neutral-800" />
        <div className="h-3 w-10 animate-pulse rounded-full bg-neutral-200 dark:bg-neutral-800" />
      </div>
    </article>
  );
}

function ExploreCard({
  post,
  activeFilter,
  coords,
  searchQuery,
  fallbackUserLabel,
  numberLocale,
  scrollKey,
}: {
  post: Post;
  activeFilter: ExploreFilterId;
  coords?: { lat: number; lng: number } | null;
  searchQuery?: { q?: string; hashtag?: string };
  fallbackUserLabel: string;
  numberLocale: string;
  scrollKey: string;
}) {
  const router = useRouter();
  const media = getPostMedia(post);
  const articleRef = useRef<HTMLElement | null>(null);
  const trackedRef = useRef(false);

  useEffect(() => {
    const el = articleRef.current;
    if (!el || !media) return;
    trackedRef.current = false;
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry?.isIntersecting && !trackedRef.current) {
          trackedRef.current = true;
          const token = Cookies.get("token") || "";
          if (token) {
            trackExploreInteraction(
              {
                targetType: "post",
                targetId: post._id,
                authorId: post.user_id,
                contentType: media.kind === "video" ? "video" : "image",
                action: "view",
              },
              token
            );
          }
          observer.disconnect();
        }
      },
      { rootMargin: "80px 0px", threshold: 0.35 }
    );
    observer.observe(el);
    return () => observer.disconnect();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [post._id]);

  if (!media) return null;

  const aspect = hashAspect(post._id);
  const displayName =
    [post.first_name, post.last_name].filter(Boolean).join(" ") ||
    post.user_name ||
    fallbackUserLabel;
  const isLiked = post.is_liked === true;

  const openPost = () => {
    cacheReelsSeedPost(post);
    const params = new URLSearchParams();
    params.set("feed", "explore");
    params.set("filter", activeFilter);
    if (coords) {
      params.set("lat", String(coords.lat));
      params.set("lng", String(coords.lng));
    }
    if (searchQuery?.hashtag) params.set("hashtag", searchQuery.hashtag);
    if (searchQuery?.q) params.set("q", searchQuery.q);
    navigateWithSavedScroll(
      router,
      scrollKey,
      `${buildPostPath(post._id, post)}?${params.toString()}`
    );
  };

  return (
    <article ref={articleRef} className="mb-2 break-inside-avoid">
      <button
        type="button"
        onClick={openPost}
        className="gentle-transition block w-full text-right active:scale-[0.98]"
      >
        <div
          className={`relative w-full overflow-hidden rounded-2xl bg-neutral-200 dark:bg-neutral-900 ${aspect}`}
        >
          {media.kind === "video" ? (
            <ExploreVideoThumb src={media.src} poster={media.poster} />
          ) : (
            <Image
              src={media.src}
              alt=""
              fill
              className="object-cover"
              sizes="50vw"
              unoptimized
            />
          )}
          <span className="absolute left-2 top-2 flex items-center gap-1 rounded-md bg-black/50 px-1.5 py-0.5 text-[10px] font-semibold text-white backdrop-blur-sm">
            <BoldIcon name="eye" size={12} tinted className="text-white" />
            {formatViewCount(post.viewsCount ?? 0, numberLocale)}
          </span>
          {media.kind === "video" && (
            <span className="absolute bottom-2 left-2 flex h-7 w-7 items-center justify-center rounded-full bg-black/55 text-[10px] text-white backdrop-blur-sm">
              ▶
            </span>
          )}
        </div>
      </button>

      <div className="flex items-center justify-between gap-2 px-0.5 py-1.5">
        <ExploreAuthor
          userId={post.user_id}
          userName={post.user_name}
          displayName={displayName}
          profileImage={post.profile_image}
        />
        <span className="flex shrink-0 items-center gap-1 text-[10px] text-neutral-500">
          <Image
            src={isLiked ? "/images/icons/red-heart.svg" : "/images/icons/heart.svg"}
            alt=""
            width={14}
            height={14}
            className={isLiked ? "" : "dark:invert"}
          />
          {formatLikeDisplay(post.likesCount ?? 0)}
        </span>
      </div>
    </article>
  );
}

const EMPTY_FILTER_KEYS: Partial<Record<ExploreFilterId, string>> = {
  academy: "explore.empty.academy",
  near_me: "explore.empty.near_me",
  makeup: "explore.empty.makeup",
  trending: "explore.empty.trending",
  predict_trends: "explore.empty.predict_trends",
  best_month: "explore.empty.best_month",
};

function emptyMessage(
  activeFilter: ExploreFilterId,
  searchQuery: { q?: string; hashtag?: string } | undefined,
  t: (key: string, options?: Record<string, string>) => string
): string {
  if (searchQuery?.hashtag) {
    return t("explore.empty.hashtag", { tag: searchQuery.hashtag });
  }
  if (searchQuery?.q) {
    return t("explore.empty.query", { query: searchQuery.q });
  }
  const key = EMPTY_FILTER_KEYS[activeFilter];
  return key ? t(key) : t("explore.empty.default");
}

export default function ExploreGrid({
  activeFilter,
  coords = null,
  searchQuery = {},
}: ExploreGridProps) {
  const { t } = useTranslation("common");
  const { language } = useAppLanguage();
  const numberLocale = language === "fa" ? "fa-IR" : "en-US";
  const token = Cookies.get("token") || "";
  const postFilters = {
    ...buildExplorePostFilters(activeFilter, coords),
    ...searchQuery,
  };
  const academyOnly = activeFilter === "academy";
  const includeAcademy = activeFilter === "all" || academyOnly;
  const needsLocation = activeFilter === "near_me" && !coords;

  const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, isError, refetch } =
    useInfiniteQuery({
      queryKey: [
        "explore-feed",
        activeFilter,
        coords?.lat,
        coords?.lng,
        searchQuery,
        postFilters,
      ],
      enabled: !needsLocation,
      queryFn: async ({ pageParam = 1 }) => {
        const page = pageParam as number;

        if (academyOnly) {
          const academyResult = await fetchAcademyExplore(page, 15, token);
          return {
            page,
            posts: [],
            postsHasMore: false,
            academyItems: academyResult.items,
            academyHasMore: academyResult.hasNextPage,
          };
        }

        const postsPromise = fetchPosts(
          page,
          15,
          { ...postFilters, feedMode: "grid" },
          token
        );
        const academyPromise = includeAcademy
          ? fetchAcademyExplore(page, 15, token)
          : Promise.resolve({ items: [], hasNextPage: false });

        const [postsResult, academyResult] = await Promise.allSettled([
          postsPromise,
          academyPromise,
        ]);

        if (postsResult.status === "rejected" && academyResult.status === "rejected") {
          throw postsResult.reason;
        }

        const resolvedPosts = postsResult.status === "fulfilled" ? postsResult.value.posts ?? [] : [];
        const resolvedPostsTotalPages =
          postsResult.status === "fulfilled" ? postsResult.value.totalPages ?? 1 : 1;
        const resolvedPostsHasMore = page < resolvedPostsTotalPages;

        const resolvedAcademyItems = academyResult.status === "fulfilled" ? academyResult.value.items : [];
        const resolvedAcademyHasMore = academyResult.status === "fulfilled" ? academyResult.value.hasNextPage : false;

        return {
          page,
          posts: resolvedPosts,
          postsHasMore: resolvedPostsHasMore,
          academyItems: resolvedAcademyItems,
          academyHasMore: resolvedAcademyHasMore,
        };
      },
      getNextPageParam: (lastPage, allPages) => {
        const hasMore = lastPage.postsHasMore || lastPage.academyHasMore;
        return hasMore ? allPages.length + 1 : undefined;
      },
      initialPageParam: 1,
    });

  const filterKey = useMemo(
    () =>
      JSON.stringify({
        activeFilter,
        lat: coords?.lat,
        lng: coords?.lng,
        searchQuery,
      }),
    [activeFilter, coords?.lat, coords?.lng, searchQuery]
  );
  const scrollKey = useListScrollRestoration(
    "explore",
    { f: filterKey },
    !isLoading
  );

  const handleScroll = useCallback(() => {
    if (
      window.innerHeight + window.scrollY >=
      document.body.offsetHeight - 800
    ) {
      if (hasNextPage && !isFetchingNextPage) fetchNextPage();
    }
  }, [hasNextPage, isFetchingNextPage, fetchNextPage]);

  useEffect(() => {
    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  }, [handleScroll]);

  const items = useMemo(() => {
    const seen = new Set<string>();
    const merged: ExploreFeedItem[] = [];

    for (const page of data?.pages ?? []) {
      const pagePosts: ExploreFeedItem[] = [];
      const pageAcademy: ExploreFeedItem[] = [];

      for (const post of page.posts) {
        if (!isPublicPostStatus(post.status) || !getPostMedia(post)) continue;
        const id = `post-${post._id}`;
        if (seen.has(id)) continue;
        seen.add(id);
        pagePosts.push({
          kind: "post",
          id,
          sortAt: toSortTime(post.createdAt),
          post,
        });
      }

      if (includeAcademy) {
        for (const academy of page.academyItems) {
          if (academy.status && !isPublicAcademyStatus(academy.status)) continue;
          const id = `academy-${academy._id}`;
          if (seen.has(id)) continue;
          seen.add(id);
          pageAcademy.push({
            kind: "academy",
            id,
            sortAt: toSortTime(academy.createdAt),
            academy,
          });
        }
      }

      const orderedPageItems =
        activeFilter === "all"
          ? interleaveAcademyGrid(pagePosts, pageAcademy, page.page)
          : activeFilter === "trending" || activeFilter === "best_month"
            ? [...pagePosts, ...pageAcademy].sort((a, b) => {
                const scoreA =
                  a.kind === "post"
                    ? (a.post.likesCount ?? 0) + (a.post.commentsCount ?? 0)
                    : a.academy.likesCount ?? 0;
                const scoreB =
                  b.kind === "post"
                    ? (b.post.likesCount ?? 0) + (b.post.commentsCount ?? 0)
                    : b.academy.likesCount ?? 0;
                return scoreB - scoreA;
              })
            : [...pagePosts, ...pageAcademy];

      merged.push(...orderedPageItems);
    }

    return merged;
  }, [data, activeFilter, includeAcademy]);

  if (needsLocation) {
    return (
      <p className="py-16 text-center text-sm text-neutral-500">
        {t("explore.nearMeHint")}
      </p>
    );
  }

  if (isLoading && !data?.pages?.length) {
    return (
      <div className="px-2 pb-2">
        <div className="columns-2 gap-2">
          {Array.from({ length: SKELETON_COUNT }).map((_, index) => (
            <ExploreCardSkeleton key={`sk-${index}`} seed={index} />
          ))}
        </div>
      </div>
    );
  }

  if (isError) {
    return (
      <div className="py-16 text-center">
        <p className="text-sm text-neutral-400">
          {t("explore.loadError")}
        </p>
        <button
          type="button"
          onClick={() => refetch()}
          className="mt-4 rounded-full border border-neutral-700 px-4 py-2 text-xs text-neutral-300"
        >
          {t("common.retry")}
        </button>
      </div>
    );
  }

  if (!items.length) {
    return (
      <p className="py-16 text-center text-sm text-neutral-500">
        {emptyMessage(activeFilter, searchQuery, t)}
      </p>
    );
  }

  return (
    <div className="px-2 pb-2">
      <div className="columns-2 gap-2">
        {items.map((item) =>
          item.kind === "post" ? (
            <ExploreCard
              key={item.id}
              post={item.post}
              activeFilter={activeFilter}
              coords={coords}
              searchQuery={searchQuery}
              fallbackUserLabel={t("common.user")}
              numberLocale={numberLocale}
              scrollKey={scrollKey}
            />
          ) : (
            <ExploreAcademyCard
              key={item.id}
              item={item.academy}
              activeFilter={activeFilter}
              coords={coords}
              searchQuery={searchQuery}
              scrollKey={scrollKey}
            />
          )
        )}
        {isFetchingNextPage
          ? Array.from({ length: 4 }).map((_, index) => (
              <ExploreCardSkeleton key={`next-sk-${index}`} seed={index + items.length} />
            ))
          : null}
      </div>
    </div>
  );
}
