// BEGIN CHANGE: Admin - catalogo looktypes a partir do Tibia.dat (+ xml/monstros/web)
"use client";

import { useEffect, useMemo, useState } from "react";
import { Outfit } from "@/components/Outfit";
import { apiUrl, type Look } from "@/lib/api";

type Tab = "dat" | "uncatalogued" | "outfits" | "monsters" | "sprites";

type OutfitRow = {
  outfitId: number;
  looktype: number;
  name: string;
  gender: number;
  genderLabel: string;
};

type MonsterRow = {
  looktype: number;
  lookhead: number;
  lookbody: number;
  looklegs: number;
  lookfeet: number;
  lookaddons: number;
  name: string;
  nameSample: string[];
  count: number;
};

type DatInfo = {
  ok: boolean;
  path: string;
  outfitCount: number;
  // BEGIN CHANGE: teto util 1780 (header DAT pode ser maior)
  headerOutfitCount?: number;
  looktypeMax?: number;
  // END CHANGE
  itemMax: number;
  effectCount: number;
  missileCount: number;
  signature: string;
  error?: string;
};

type SpritesInfo = {
  path: string;
  count: number;
  looktypes: number[];
  max?: number;
};

type Stats = {
  datOutfitCount: number;
  uncatalogued: number;
  missingWebSprite: number;
  webSpriteCount: number;
};

function defaultLook(looktype: number, partial?: Partial<Look>): Look {
  return {
    looktype,
    lookhead: partial?.lookhead ?? 0,
    lookbody: partial?.lookbody ?? 0,
    looklegs: partial?.looklegs ?? 0,
    lookfeet: partial?.lookfeet ?? 0,
    lookaddons: partial?.lookaddons ?? 0,
    lookmount: 0,
  };
}

function LookCard({
  look,
  title,
  subtitle,
  looktype,
  badges,
  showImage,
}: {
  look: Look;
  title: string;
  subtitle?: string;
  looktype: number;
  badges?: string[];
  showImage: boolean;
}) {
  return (
    <div className="flex flex-col items-center gap-1 rounded-md border border-border bg-background/60 p-2 text-center">
      {showImage ? (
        <Outfit look={look} size="grid" alt={title} />
      ) : (
        <div
          className="flex items-center justify-center rounded bg-muted/40 text-[10px] text-muted"
          style={{ width: 112, height: 112 }}
          title="Sem pasta no animatedOutfits (GIF web)"
        >
          sem GIF
        </div>
      )}
      <div className="font-mono text-sm font-semibold text-brand">{looktype}</div>
      <div className="line-clamp-2 text-xs leading-tight">{title}</div>
      {subtitle ? <div className="line-clamp-2 text-[11px] text-muted">{subtitle}</div> : null}
      {badges && badges.length > 0 ? (
        <div className="flex flex-wrap justify-center gap-1">
          {badges.map((b) => (
            <span
              key={b}
              className="rounded bg-muted/50 px-1 text-[10px] uppercase tracking-wide text-muted"
            >
              {b}
            </span>
          ))}
        </div>
      ) : null}
    </div>
  );
}

export function AdminLooktypes({ token }: { token: string }) {
  const [tab, setTab] = useState<Tab>("dat");
  const [q, setQ] = useState("");
  const [status, setStatus] = useState<"loading" | "ok" | "error">("loading");
  const [err, setErr] = useState("");
  const [outfits, setOutfits] = useState<OutfitRow[]>([]);
  const [monsters, setMonsters] = useState<MonsterRow[]>([]);
  const [sprites, setSprites] = useState<SpritesInfo | null>(null);
  const [dat, setDat] = useState<DatInfo | null>(null);
  const [stats, setStats] = useState<Stats | null>(null);
  const [inXml, setInXml] = useState<Set<number>>(new Set());
  const [inMob, setInMob] = useState<Set<number>>(new Set());
  const [hasWeb, setHasWeb] = useState<Set<number>>(new Set());

  useEffect(() => {
    let cancelled = false;
    (async () => {
      setStatus("loading");
      setErr("");
      try {
        const r = await fetch(apiUrl("looktypes.php"), {
          headers: { Authorization: `Bearer ${token}` },
        });
        const json = await r.json().catch(() => ({}));
        if (cancelled) return;
        if (!r.ok) {
          setErr(
            typeof json.error === "string" && json.error
              ? `Falha ao carregar looktypes (${r.status}): ${json.error}`
              : `Falha ao carregar looktypes (HTTP ${r.status})`,
          );
          setStatus("error");
          return;
        }
        setOutfits(Array.isArray(json.outfits) ? json.outfits : []);
        setMonsters(Array.isArray(json.monsters) ? json.monsters : []);
        setSprites(json.sprites ?? null);
        setDat(json.dat ?? null);
        setStats(json.stats ?? null);
        setInXml(new Set((json.flags?.inOutfitsXml ?? []).map((n: number) => Number(n))));
        setInMob(new Set((json.flags?.inMonsters ?? []).map((n: number) => Number(n))));
        setHasWeb(new Set((json.flags?.hasWebSprite ?? []).map((n: number) => Number(n))));
        setStatus("ok");
      } catch {
        if (!cancelled) {
          setErr("Falha de rede");
          setStatus("error");
        }
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [token]);

  // BEGIN CHANGE: esconder looktypes sem GIF (nao exportados do DAT/SPR)
  const datIds = useMemo(() => {
    const n = dat?.ok ? dat.outfitCount : 0;
    if (n <= 0) return [] as number[];
    const needle = q.trim().toLowerCase();
    const out: number[] = [];
    for (let i = 1; i <= n; i++) {
      if (!hasWeb.has(i)) continue;
      if (tab === "uncatalogued" && (inXml.has(i) || inMob.has(i))) continue;
      if (needle && !String(i).includes(needle)) continue;
      out.push(i);
    }
    return out;
  }, [dat, q, tab, inXml, inMob, hasWeb]);
  // END CHANGE

  // BEGIN CHANGE: so outfits com GIF web
  const filteredOutfits = useMemo(() => {
    const needle = q.trim().toLowerCase();
    return outfits.filter((o) => {
      if (!hasWeb.has(o.looktype)) return false;
      if (!needle) return true;
      return (
        String(o.looktype).includes(needle) ||
        o.name.toLowerCase().includes(needle) ||
        String(o.outfitId).includes(needle) ||
        o.genderLabel.includes(needle)
      );
    });
  }, [outfits, q, hasWeb]);
  // END CHANGE

  // BEGIN CHANGE: so monstros com GIF web
  const filteredMonsters = useMemo(() => {
    const needle = q.trim().toLowerCase();
    return monsters.filter((m) => {
      if (!hasWeb.has(m.looktype)) return false;
      if (!needle) return true;
      return (
        String(m.looktype).includes(needle) ||
        m.name.toLowerCase().includes(needle) ||
        (m.nameSample || []).some((n) => n.toLowerCase().includes(needle))
      );
    });
  }, [monsters, q, hasWeb]);
  // END CHANGE

  const filteredSprites = useMemo(() => {
    const ids = sprites?.looktypes ?? [];
    const needle = q.trim().toLowerCase();
    if (!needle) return ids;
    return ids.filter((id) => String(id).includes(needle));
  }, [sprites, q]);

  // BEGIN CHANGE: contagens = so com GIF web
  const datWithGifCount = useMemo(() => {
    const n = dat?.ok ? dat.outfitCount : 0;
    let c = 0;
    for (let i = 1; i <= n; i++) {
      if (hasWeb.has(i)) c++;
    }
    return c;
  }, [dat, hasWeb]);

  const uncataloguedWithGifCount = useMemo(() => {
    const n = dat?.ok ? dat.outfitCount : 0;
    let c = 0;
    for (let i = 1; i <= n; i++) {
      if (!hasWeb.has(i)) continue;
      if (inXml.has(i) || inMob.has(i)) continue;
      c++;
    }
    return c;
  }, [dat, hasWeb, inXml, inMob]);

  const outfitsWithGifCount = useMemo(
    () => outfits.filter((o) => hasWeb.has(o.looktype)).length,
    [outfits, hasWeb],
  );

  const monstersWithGifCount = useMemo(
    () => monsters.filter((m) => hasWeb.has(m.looktype)).length,
    [monsters, hasWeb],
  );

  const tabs: { id: Tab; label: string; count: number }[] = [
    { id: "dat", label: "Client DAT", count: datWithGifCount },
    { id: "uncatalogued", label: "Nao catalogados", count: uncataloguedWithGifCount },
    { id: "outfits", label: "Outfits XML", count: outfitsWithGifCount },
    { id: "monsters", label: "Monstros DB", count: monstersWithGifCount },
    { id: "sprites", label: "GIF web", count: sprites?.count ?? 0 },
  ];
  // END CHANGE

  function badgesFor(id: number): string[] {
    const b: string[] = [];
    if (inXml.has(id)) b.push("xml");
    if (inMob.has(id)) b.push("mob");
    if (hasWeb.has(id)) b.push("gif");
    return b;
  }

  return (
    <div className="space-y-4">
      <div>
        <h2 className="text-xl font-semibold">Looktypes</h2>
        <p className="mt-1 text-sm text-muted">
          Fonte: header Tibia.dat (1..{dat?.looktypeMax ?? "..."}). Lista so looktypes com
          pasta em <code>animatedOutfits</code> (extraidos do DAT/SPR) - sem GIF ficam ocultos.
        </p>
        {dat && (
          <p className="mt-1 text-xs text-muted">
            {/* BEGIN CHANGE: mostrar teto util + header bruto do DAT */}
            DAT:{" "}
            {dat.ok
              ? `${dat.outfitCount} uteis` +
                (dat.headerOutfitCount && dat.headerOutfitCount !== dat.outfitCount
                  ? ` (header ${dat.headerOutfitCount})`
                  : "")
              : dat.error || "indisponivel"}
            {dat.path ? ` - ${dat.path}` : ""}
            {stats
              ? ` | sem catalogo: ${stats.uncatalogued} | sem GIF web: ${stats.missingWebSprite}`
              : ""}
            {/* END CHANGE */}
          </p>
        )}
      </div>

      <div className="flex flex-wrap items-center gap-2">
        {tabs.map((t) => (
          <button
            key={t.id}
            type="button"
            onClick={() => setTab(t.id)}
            className={
              tab === t.id
                ? "rounded-md bg-brand px-3 py-1.5 text-sm text-white"
                : "rounded-md border border-border px-3 py-1.5 text-sm hover:bg-muted/40"
            }
          >
            {t.label} ({t.count})
          </button>
        ))}
        <input
          className="min-w-[220px] flex-1 rounded-md border border-border bg-background px-3 py-2 text-sm"
          placeholder="Filtrar por looktype, nome..."
          value={q}
          onChange={(e) => setQ(e.target.value)}
        />
      </div>

      {status === "loading" && <p className="text-sm text-muted">Carregando...</p>}
      {status === "error" && <p className="text-sm text-red-600">{err}</p>}

      {status === "ok" && (tab === "dat" || tab === "uncatalogued") && (
        <div className="space-y-3">
          {!dat?.ok && (
            <p className="text-sm text-red-600">
              Tibia.dat nao encontrado no host. Coloque em files-extended/ ou files-otc/.
            </p>
          )}
          <p className="text-sm text-muted">{datIds.length} looktypes nesta lista (scroll)</p>
          <div className="max-h-[75vh] overflow-auto rounded-md border border-border p-2">
          <div className="grid grid-cols-2 gap-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
            {datIds.map((id) => (
              <LookCard
                key={id}
                look={defaultLook(id)}
                looktype={id}
                title={`looktype ${id}`}
                showImage={hasWeb.has(id)}
                badges={badgesFor(id)}
              />
            ))}
          </div>
          </div>
        </div>
      )}

      {status === "ok" && tab === "outfits" && (
        <div className="max-h-[75vh] overflow-auto rounded-md border border-border p-2">
        <div className="grid grid-cols-2 gap-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
          {filteredOutfits.map((o) => (
            <LookCard
              key={`${o.outfitId}-${o.gender}-${o.looktype}`}
              look={defaultLook(o.looktype, { lookaddons: 3 })}
              looktype={o.looktype}
              title={o.name}
              subtitle={`outfit #${o.outfitId} - ${o.genderLabel}`}
              showImage={hasWeb.has(o.looktype)}
              badges={badgesFor(o.looktype)}
            />
          ))}
        </div>
        </div>
      )}

      {status === "ok" && tab === "monsters" && (
        <div className="max-h-[75vh] overflow-auto rounded-md border border-border p-2">
        <div className="grid grid-cols-2 gap-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
          {filteredMonsters.map((m) => (
            <LookCard
              key={m.looktype}
              look={defaultLook(m.looktype, {
                lookhead: m.lookhead,
                lookbody: m.lookbody,
                looklegs: m.looklegs,
                lookfeet: m.lookfeet,
                lookaddons: m.lookaddons,
              })}
              looktype={m.looktype}
              title={m.name}
              subtitle={
                m.count > 1
                  ? `${m.count} monstros - ex: ${(m.nameSample || []).slice(0, 3).join(", ")}`
                  : undefined
              }
              showImage={hasWeb.has(m.looktype)}
              badges={badgesFor(m.looktype)}
            />
          ))}
        </div>
        </div>
      )}

      {status === "ok" && tab === "sprites" && (
        <div className="space-y-2">
          <p className="text-xs text-muted">
            Fonte GIF: {sprites?.path ? sprites.path : "pasta nao encontrada"} (max{" "}
            {sprites?.max ?? "-"})
          </p>
          <div className="max-h-[75vh] overflow-auto rounded-md border border-border p-2">
          <div className="grid grid-cols-2 gap-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
            {filteredSprites.map((id) => (
              <LookCard
                key={id}
                look={defaultLook(id)}
                looktype={id}
                title={`looktype ${id}`}
                showImage
                badges={badgesFor(id)}
              />
            ))}
          </div>
          </div>
        </div>
      )}
    </div>
  );
}
// END CHANGE
