import {
  Archive,
  Download,
  File,
  FileText,
  Image as ImageIcon,
  Link as LinkIcon,
  LoaderCircle,
  Music,
  Presentation,
  Video,
  type LucideIcon,
} from "lucide-react";
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { assetUrl, type Category } from "../api";
import { useI18n } from "../i18n";
import { resourceTypeLabel } from "../resourceTypeLabels";
import { cleanCategoryDisplayName } from "../utils/categoryDisplay";
import { formatDateYmd } from "../utils/format";
import { postToResource } from "../utils/postResourceAdapter";
import type { Post } from "../types/post";
import { downloadAttachment } from "./messageStream/utils/downloadFile";
import { useToast } from "./Toast";

const MEDALS = ["🥇", "🥈", "🥉"];
const MAX_ITEMS = 5;

/**
 * Fallback cover shown when a resource has no real thumbnail. A gradient tinted
 * by resource type + the matching type icon — pure CSS, no asset needed. The
 * backend is expected to supply real cover/thumbnail images; this is graceful
 * degradation until it does.
 */
const TYPE_FALLBACK: Record<string, { gradient: string; Icon: LucideIcon }> = {
  ppt: { gradient: "from-[#7a5a22] to-[#3a2c12]", Icon: Presentation },
  video: { gradient: "from-[#27506a] to-[#16293a]", Icon: Video },
  pdf: { gradient: "from-[#2f5a3f] to-[#16291d]", Icon: FileText },
  text: { gradient: "from-[#2f5a3f] to-[#16291d]", Icon: FileText },
  image: { gradient: "from-[#463340] to-[#241a22]", Icon: ImageIcon },
  music: { gradient: "from-[#5a2f5a] to-[#291629]", Icon: Music },
  link: { gradient: "from-[#2f5a55] to-[#162926]", Icon: LinkIcon },
  archive: { gradient: "from-[#6a4a22] to-[#2f2012]", Icon: Archive },
};
const FALLBACK_DEFAULT = {
  gradient: "from-[#2a2b35] to-[#191a20]",
  Icon: File,
};

function FallbackCover({ type }: { type: string }) {
  const { gradient, Icon } = TYPE_FALLBACK[type] ?? FALLBACK_DEFAULT;
  return (
    <div
      className={`flex h-full w-full items-center justify-center bg-gradient-to-br ${gradient}`}
    >
      <Icon className="h-6 w-6 text-white/85" strokeWidth={1.8} />
    </div>
  );
}

/** Rank badge: medals for the top 3, muted tabular numbers afterwards. */
function RankBadge({ index }: { index: number }) {
  if (index < MEDALS.length) {
    return (
      <span
        className="flex w-7 shrink-0 justify-center text-[22px] leading-none"
        aria-label={`No.${index + 1}`}
      >
        {MEDALS[index]}
      </span>
    );
  }
  return (
    <span
      className="flex w-7 shrink-0 justify-center text-lg font-extrabold tabular-nums text-[#7d7e87]"
      aria-label={`No.${index + 1}`}
    >
      {index + 1}
    </span>
  );
}

function PopularRankRow({
  post,
  index,
  categories,
}: {
  post: Post;
  index: number;
  categories: Category[];
}) {
  const { t, lang } = useI18n();
  const navigate = useNavigate();
  const { showToast } = useToast();
  const [isDownloading, setIsDownloading] = useState(false);
  const [coverFailed, setCoverFailed] = useState(false);

  const r = postToResource(post, lang, categories);
  const cover = r.coverImage && !coverFailed ? assetUrl(r.coverImage) : "";
  const isTop3 = index < MEDALS.length;

  const handleDownload = async () => {
    if (isDownloading || !r.downloadPostId || !r.downloadAttachmentId) return;
    setIsDownloading(true);
    try {
      await downloadAttachment(
        r.downloadPostId,
        r.downloadAttachmentId,
        r.title,
      );
    } catch {
      showToast(t("downloadFail"), "error");
    } finally {
      setIsDownloading(false);
    }
  };

  return (
    <article className="relative flex items-center gap-3 rounded-2xl border border-ark-line bg-ark-panel p-3 transition hover:border-ark-gold/45 md:gap-4 md:p-4">
      <button
        type="button"
        onClick={() => navigate(`/resource/${post.id}`)}
        aria-label={r.title}
        className="absolute inset-0 z-0 rounded-2xl outline-none focus-visible:ring-2 focus-visible:ring-ark-gold/70"
      />

      <RankBadge index={index} />

      <div
        className={`relative z-10 flex h-[52px] w-[64px] shrink-0 items-center justify-center overflow-hidden rounded-lg bg-[#111116] md:h-[58px] md:w-[72px] ${
          isTop3 ? "ring-1 ring-ark-gold/45" : ""
        }`}
      >
        {cover ? (
          <img
            src={cover}
            alt=""
            loading="lazy"
            decoding="async"
            className="h-full w-full object-fill"
            onError={() => setCoverFailed(true)}
          />
        ) : (
          <FallbackCover type={r.type} />
        )}
      </div>

      <div className="pointer-events-none relative z-10 flex min-w-0 flex-1 flex-col gap-1">
        <div className="line-clamp-2 text-sm font-bold leading-snug text-white md:text-base">
          {r.title}
        </div>
        <div className="flex items-center gap-2 text-xs text-[#9b9ca6]">
          <span className="rounded-full bg-[#2a2b33] px-2 py-0.5 text-[#b9bac3]">
            {resourceTypeLabel(t, r.type)}
          </span>
          <span className="truncate">
            {cleanCategoryDisplayName(r.categoryName)}
          </span>
          <span className="text-[#55565e]">·</span>
          <time dateTime={r.updatedAt} className="shrink-0 text-ark-muted">
            {formatDateYmd(r.updatedAt)}
          </time>
        </div>
      </div>

      <div className="relative z-10 flex shrink-0 items-center gap-1">
        {r.isDownloadable ? (
          <button
            type="button"
            onClick={handleDownload}
            disabled={isDownloading}
            aria-label={t("download")}
            title={t("download")}
            className="flex h-9 w-9 items-center justify-center rounded-lg text-neutral-300 outline-none transition hover:bg-white/5 hover:text-ark-gold focus-visible:ring-2 focus-visible:ring-ark-gold/70 disabled:opacity-60"
          >
            {isDownloading ? (
              <LoaderCircle className="h-[18px] w-[18px] animate-spin" />
            ) : (
              <Download className="h-[18px] w-[18px]" />
            )}
          </button>
        ) : null}
      </div>
    </article>
  );
}

function ComingSoonRankRow({ index }: { index: number }) {
  const { lang } = useI18n();
  const label = lang === "zh-CN" ? "即将到来" : "Coming soon";
  return (
    <article
      className="flex items-center gap-3 rounded-2xl border border-ark-line bg-ark-panel p-3 opacity-70 md:gap-4 md:p-4"
      aria-hidden="true"
    >
      <RankBadge index={index} />
      <div className="flex h-[52px] w-[64px] shrink-0 items-center justify-center overflow-hidden rounded-lg md:h-[58px] md:w-[72px]">
        <FallbackCover type="" />
      </div>
      <div className="flex min-w-0 flex-1 flex-col gap-1">
        <div className="text-sm font-bold text-white/80 md:text-base">
          {label}
        </div>
      </div>
    </article>
  );
}

/**
 * "社群常用资料" ranking list for the home page. Items arrive pre-sorted by the
 * backend popularity score (downloads / favorites / shares / admin weight); the
 * frontend only ever shows rank position, never any raw counts.
 */
export function PopularRankList({
  posts,
  categories,
}: {
  posts: Post[];
  categories: Category[];
}) {
  const items = posts.slice(0, MAX_ITEMS);
  const placeholderCount = Math.max(0, MAX_ITEMS - items.length);

  return (
    <div className="mx-auto flex max-w-[760px] flex-col gap-2.5 px-4 md:gap-3 md:px-0">
      {items.map((post, index) => (
        <PopularRankRow
          key={post.id}
          post={post}
          index={index}
          categories={categories}
        />
      ))}
      {Array.from({ length: placeholderCount }).map((_, i) => (
        <ComingSoonRankRow
          key={`popular-coming-soon-${i}`}
          index={items.length + i}
        />
      ))}
    </div>
  );
}
