"use client";

import Image from "next/image";
import Link from "next/link";
import { useInfiniteQuery } from "@tanstack/react-query";
import { fetchPosts } from "@/api/fetchPosts";
import {
  AcademyExploreItem,
  fetchAcademyExplore,
} from "@/api/fetchAcademyExplore";
import { Post } from "@/types/types";
import { buildStorageUrl } from "@/components/main/BaseUrl";
import Cookies from "js-cookie";
import { useCallback, useEffect, useMemo } from "react";
import PageLoader from "@/components/ui/PageLoader";
import BoldIcon from "@/components/ui/BoldIcon";
import ExploreAuthor from "@/components/explore/ExploreAuthor";
import ExploreAcademyCard from "@/components/explore/ExploreAcademyCard";
import { ExploreFilterId } from "@/constants/exploreFilters";
import { buildExplorePostFilters } from "@/lib/explore/buildPostFilters";

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

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;
};

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 getPostMedia(post: Post): {
  kind: "video" | "image";
  src: string;
} | null {
  const file = post.files?.[0];
  if (!file?.path) return null;

  const src = buildStorageUrl(file.path);
  const isVideo = post.type === "video" || file.type === "video";

  return { kind: isVideo ? "video" : "image", src };
}

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 ExploreCard({
  post,
  activeFilter,
  coords,
}: {
  post: Post;
  activeFilter: ExploreFilterId;
  coords?: { lat: number; lng: number } | null;
}) {
  const media = getPostMedia(post);
  if (!media) return null;

  const aspect = hashAspect(post._id);
  const displayName =
    [post.first_name, post.last_name].filter(Boolean).join(" ") ||
    post.user_name ||
    "کاربر";

  const params = new URLSearchParams();
  params.set("filter", activeFilter);
  if (coords) {
    params.set("lat", String(coords.lat));
    params.set("lng", String(coords.lng));
  }

  return (
    <article className="mb-2 break-inside-avoid">
      <Link
        href={`/explore/${post._id}?${params.toString()}`}
        className="gentle-transition block active:scale-[0.98]"
      >
        <div
          className={`relative w-full overflow-hidden rounded-2xl bg-neutral-900 ${aspect}`}
        >
          {media.kind === "video" ? (
            <video
              src={media.src}
              preload="metadata"
              muted
              playsInline
              className="absolute inset-0 h-full w-full object-cover"
            />
          ) : (
            <Image
              src={media.src}
              alt=""
              fill
              className="object-cover"
              sizes="50vw"
              unoptimized
            />
          )}
          {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>
      </Link>

      <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">
          <BoldIcon name="heart" size={14} tinted className="text-[#FC8EAC]" />
          {post.likesCount ?? 0}
        </span>
      </div>
    </article>
  );
}

const EMPTY_MESSAGES: Partial<Record<ExploreFilterId, string>> = {
  academy: "آموزش رایگانی برای نمایش وجود ندارد.",
  near_me: "پستی در فاصله ۲ کیلومتری شما یافت نشد.",
  makeup: "پست میکاپی برای نمایش وجود ندارد.",
  trending: "پست ترندی برای نمایش وجود ندارد.",
  best_month: "پست برتر این ماه یافت نشد.",
};

export default function ExploreGrid({
  activeFilter,
  coords = null,
}: ExploreGridProps) {
  const token = Cookies.get("token") || "";
  const postFilters = buildExplorePostFilters(activeFilter, coords);
  const academyOnly = activeFilter === "academy";
  const includeAcademy = activeFilter === "all" || academyOnly;
  const needsLocation = activeFilter === "near_me" && !coords;

  const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } =
    useInfiniteQuery({
      queryKey: [
        "explore-feed",
        activeFilter,
        coords?.lat,
        coords?.lng,
        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,
        ]);

        const resolvedPosts = postsResult.status === "fulfilled" ? postsResult.value.posts ?? [] : [];
        const resolvedPostsHasMore = postsResult.status === "fulfilled" ? (postsResult.value.posts?.length ?? 0) > 0 : false;

        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 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 (post.status !== "accept" || !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 && academy.status !== "accept") 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">
        برای مشاهده پست‌های نزدیک، روی «نزدیک من» بزنید و دسترسی موقعیت را
        فعال کنید.
      </p>
    );
  }

  if (isLoading) return <PageLoader className="min-h-[40vh]" />;

  if (!items.length) {
    return (
      <p className="py-16 text-center text-sm text-neutral-500">
        {EMPTY_MESSAGES[activeFilter] ?? "محتوایی برای نمایش وجود ندارد."}
      </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}
            />
          ) : (
            <ExploreAcademyCard
              key={item.id}
              item={item.academy}
              activeFilter={activeFilter}
              coords={coords}
            />
          )
        )}
      </div>
      {isFetchingNextPage && (
        <div className="py-4">
          <PageLoader className="min-h-[80px]" />
        </div>
      )}
    </div>
  );
}
