import { Heart, RotateCcw } from "lucide-react";
import { useEffect, useMemo, useState } from "react";
import { Link } from "react-router-dom";
import {
  assetUrl,
  getJSON,
  itemsOrEmpty,
  readJSONCache,
  type Category,
  type Resource,
} from "../../api";
import { FavoriteButton } from "../../favorites/FavoriteButton";
import { isFavoritesAuthError, listFavorites } from "../../favorites/api";
import { useFavorites } from "../../favorites/FavoritesProvider";
import { langQuery, useI18n, type Lang } from "../../i18n";
import { Reveal } from "../../motion";
import { useSetPageTitle } from "../../components/PageTitleContext";
import { Skeleton } from "../../components/Skeleton";
import { useWallet } from "../../wallet/WalletProvider";
import { useLocalizedPath } from "../../useLocalizedPath";
import { cleanCategoryDisplayName } from "../../utils/categoryDisplay";
import { formatDateYmd } from "../../utils/format";
import { resourceTypeLabel } from "../../resourceTypeLabels";

const pageSize = 50;

function useCategoryNameBySlug(lang: Lang): Map<string, string> {
  const [categories, setCategories] = useState<Category[]>([]);

  useEffect(() => {
    const url = `/api/categories?lang=${encodeURIComponent(langQuery(lang))}`;
    const cached = readJSONCache<Category[]>(url);
    if (cached) setCategories(itemsOrEmpty(cached));

    let cancelled = false;
    getJSON<Category[]>(url)
      .then((items) => {
        if (!cancelled) setCategories(itemsOrEmpty(items));
      })
      .catch(() => {
        if (!cancelled && !cached) setCategories([]);
      });

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

  return useMemo(() => {
    const map = new Map<string, string>();
    categories.forEach((category) => map.set(category.slug, category.name));
    return map;
  }, [categories]);
}

type FavoriteAttachment = {
  thumbnailUrl?: string;
  thumbUrl?: string;
  posterUrl?: string;
  url?: string;
};

type FavoriteLocalization = {
  title?: string;
  text?: string;
  description?: string;
};

type FavoriteResource = Resource & {
  postType?: string;
  sourceLanguage?: string;
  createdAt?: string;
  attachments?: FavoriteAttachment[];
  localizations?: Record<string, FavoriteLocalization>;
};

function localizationKeys(lang: Lang): string[] {
  if (lang === "zh-CN") return ["zh", "zh-CN", "zh-Hans"];
  return [lang];
}

function localizedResourceText(
  resource: FavoriteResource,
  lang: Lang,
  field: "title" | "description",
): string {
  for (const key of localizationKeys(lang)) {
    const localized = resource.localizations?.[key];
    if (!localized) continue;
    if (field === "title" && localized.title?.trim()) return localized.title;
    if (field === "description") {
      const text = localized.description || localized.text;
      if (text?.trim()) return text;
    }
  }
  if (field === "title") return resource.title;
  return resource.description || resource.bodyText || "";
}

function firstAttachmentUrl(resource: FavoriteResource): string {
  const attachment = resource.attachments?.[0];
  return (
    attachment?.thumbnailUrl ||
    attachment?.thumbUrl ||
    attachment?.posterUrl ||
    attachment?.url ||
    ""
  );
}

function FavoriteResourceCard({
  categoryNameBySlug,
  resource,
}: {
  categoryNameBySlug: Map<string, string>;
  resource: FavoriteResource;
}) {
  const { lang, t } = useI18n();
  const lp = useLocalizedPath();
  const unavailable = resource.availability === "unavailable";
  const cover =
    resource.coverImage || resource.previewUrl || firstAttachmentUrl(resource);
  const categoryLabel =
    (resource.categorySlug && categoryNameBySlug.get(resource.categorySlug)) ||
    resource.categoryName ||
    resource.categorySlug ||
    "ARK";
  const typeLabel = resourceTypeLabel(
    t,
    resource.type || resource.postType || "resource",
  );
  const date =
    resource.updatedAt || resource.publishedAt || resource.createdAt || "";
  const title = localizedResourceText(resource, lang, "title");
  const description = localizedResourceText(resource, lang, "description");

  return (
    <article
      className={`group relative flex min-h-[132px] gap-4 rounded-2xl border bg-[#272632] p-3 transition md:p-4 ${
        unavailable
          ? "border-yellow-500/25 opacity-80"
          : "border-[#27292E] hover:border-ark-gold/50"
      }`}
    >
      {!unavailable ? (
        <Link
          to={lp(`/resource/${resource.id}`)}
          aria-label={title}
          className="absolute inset-0 z-0 rounded-2xl outline-none focus-visible:ring-2 focus-visible:ring-ark-gold/80"
        />
      ) : null}
      <div className="relative z-10 h-[96px] w-[112px] shrink-0 overflow-hidden rounded-xl bg-[#111116] md:h-[116px] md:w-[150px]">
        {cover && !unavailable ? (
          <img
            src={assetUrl(cover)}
            alt=""
            className="h-full w-full object-cover transition group-hover:scale-[1.02]"
            loading="lazy"
            decoding="async"
          />
        ) : (
          <div className="flex h-full w-full items-center justify-center bg-gradient-to-br from-neutral-900 to-neutral-950">
            <Heart className="h-8 w-8 text-ark-gold/50" strokeWidth={1.6} />
          </div>
        )}
        {unavailable ? (
          <span className="absolute left-2 top-2 rounded-full bg-yellow-500 px-2 py-0.5 text-[11px] font-bold text-black">
            {t("favoritesUnavailable")}
          </span>
        ) : null}
      </div>

      <div className="pointer-events-none relative z-10 flex min-w-0 flex-1 flex-col gap-2 pr-11">
        <h2 className="line-clamp-2 text-base font-bold leading-snug text-white md:text-lg">
          {title}
        </h2>
        {description ? (
          <p className="line-clamp-2 text-sm leading-6 text-neutral-400">
            {description}
          </p>
        ) : null}
        <div className="mt-auto flex flex-wrap items-center gap-2 text-xs text-neutral-400">
          <span className="rounded-full bg-[#1f2028] px-2.5 py-1 text-neutral-200">
            {cleanCategoryDisplayName(categoryLabel)}
          </span>
          <span>{typeLabel}</span>
          {date ? (
            <>
              <span>·</span>
              <time dateTime={date}>{formatDateYmd(date)}</time>
            </>
          ) : null}
          {typeof resource.favoriteCount === "number" ? (
            <span>· ♥ {resource.favoriteCount}</span>
          ) : null}
        </div>
      </div>

      <FavoriteButton
        resourceId={resource.id}
        className="absolute right-3 top-3 z-20"
      />
    </article>
  );
}

export default function Favorites() {
  const { lang, t } = useI18n();
  const wallet = useWallet();
  const { markFavorite } = useFavorites();
  const categoryNameBySlug = useCategoryNameBySlug(lang);
  const [items, setItems] = useState<FavoriteResource[]>([]);
  const [loading, setLoading] = useState(false);
  const [loaded, setLoaded] = useState(false);
  const [error, setError] = useState("");
  const [reloadKey, setReloadKey] = useState(0);

  useSetPageTitle(t("favorites"));

  useEffect(() => {
    if (!wallet.token || wallet.status !== "loggedIn") {
      setItems([]);
      setLoading(false);
      setLoaded(false);
      setError("");
      return;
    }

    let cancelled = false;
    setLoading(true);
    setLoaded(false);
    setError("");

    listFavorites(wallet.token, {
      limit: pageSize,
      includeUnavailable: true,
    })
      .then((data) => {
        if (cancelled) return;
        const resources = itemsOrEmpty(data.items) as FavoriteResource[];
        setItems(resources);
        resources.forEach((resource) => markFavorite(resource.id, true));
        setLoaded(true);
      })
      .catch((err) => {
        if (cancelled) return;
        if (isFavoritesAuthError(err)) {
          wallet.logout();
          wallet.openLoginModal();
          return;
        }
        setError(err instanceof Error ? err.message : t("loadFailed"));
        setLoaded(true);
      })
      .finally(() => {
        if (!cancelled) setLoading(false);
      });

    return () => {
      cancelled = true;
    };
  }, [markFavorite, reloadKey, t, wallet]);

  if (wallet.status === "loading") {
    return (
      <Reveal className="mx-auto grid max-w-[980px] gap-3 px-0 py-2 md:py-4">
        {Array.from({ length: 4 }).map((_, index) => (
          <Skeleton key={index} className="h-[132px] rounded-2xl" />
        ))}
      </Reveal>
    );
  }

  if (wallet.status !== "loggedIn") {
    return (
      <Reveal className="flex min-h-[60vh] flex-col items-center justify-center gap-5 px-4 py-12 text-center">
        <div className="flex h-20 w-20 items-center justify-center rounded-full border border-ark-gold/30 bg-ark-gold/5">
          <Heart className="h-10 w-10 text-ark-gold/70" strokeWidth={1.8} />
        </div>
        <h1 className="text-2xl font-semibold text-neutral-100 md:text-3xl">
          {t("favorites")}
        </h1>
        <p className="max-w-md text-sm leading-relaxed text-neutral-400 md:text-base">
          {t("favoritesLoginDesc")}
        </p>
        <button
          type="button"
          onClick={wallet.openLoginModal}
          className="mt-2 inline-flex h-11 items-center justify-center rounded-full bg-ark-gold px-6 text-sm font-bold text-black transition hover:bg-ark-gold2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-ark-bg"
        >
          {t("walletConnect")}
        </button>
      </Reveal>
    );
  }

  return (
    <div className="mx-auto grid max-w-[980px] gap-3 px-0 py-2 md:py-4">
      {loading || !loaded ? (
        Array.from({ length: 4 }).map((_, index) => (
          <Skeleton key={index} className="h-[132px] rounded-2xl" />
        ))
      ) : error ? (
        <div className="flex flex-col items-start gap-3 rounded-2xl border border-red-500/30 bg-red-500/10 p-4 text-sm text-red-200">
          <p>{error}</p>
          <button
            type="button"
            onClick={() => setReloadKey((value) => value + 1)}
            className="inline-flex items-center gap-1.5 rounded-full border border-red-400/40 px-3 py-1.5 text-xs font-semibold text-red-100 transition hover:bg-red-500/20"
          >
            <RotateCcw className="h-3.5 w-3.5" />
            {t("walletRetry")}
          </button>
        </div>
      ) : items.length === 0 ? (
        <div className="flex min-h-[280px] flex-col items-center justify-center gap-4 rounded-3xl border border-white/10 bg-[#17171d] p-8 text-center">
          <Heart className="h-10 w-10 text-ark-gold/60" strokeWidth={1.8} />
          <h2 className="text-xl font-semibold text-white">
            {t("favoritesEmptyTitle")}
          </h2>
          <p className="max-w-md text-sm leading-6 text-neutral-400">
            {t("favoritesEmptyDesc")}
          </p>
        </div>
      ) : (
        items.map((resource) => (
          <FavoriteResourceCard
            key={resource.id}
            categoryNameBySlug={categoryNameBySlug}
            resource={resource}
          />
        ))
      )}
    </div>
  );
}
