// BEGIN CHANGE: Admin Server Sync - grupos + novos alvos
"use client";

import { useCallback, useEffect, useState } from "react";
import { apiUrl } from "@/lib/api";

type SyncTarget =
  | "monsters"
  | "spells"
  | "items"
  | "otb_reload"
  | "outfits"
  | "addon_bonuses"
  | "mounts"
  | "loot"
  | "tasks"
  | "events"
  | "guides"
  | "prey"
  | "patents"
  | "npcs"
  | "shop"
  | "item_gifs"
  | "clear_caches"
  | "sync_all";

type SyncGroup = {
  id: string;
  title: string;
  targets: {
    id: SyncTarget;
    title: string;
    desc: string;
    adminOnly?: boolean;
  }[];
};

type FileInfo = {
  path: string;
  exists: boolean;
  mtime: number;
};

type StatusPayload = {
  ok: boolean;
  serverPath?: string;
  htmlRepo?: string;
  files?: Record<string, FileInfo>;
  note?: string;
};

type SyncResult = {
  ok: boolean;
  target: string;
  detail?: Record<string, unknown>;
  error?: string;
};

const GROUPS: SyncGroup[] = [
  {
    id: "encyclopedia",
    title: "1. Enciclopedia (MySQL)",
    targets: [
      {
        id: "monsters",
        title: "Monstros",
        desc: "Reimporta data/monster/*.xml para z_monsters.",
      },
      {
        id: "spells",
        title: "Spells",
        desc: "Reimporta spells.xml para z_spells.",
      },
    ],
  },
  {
    id: "visual",
    title: "2. Catalogo visual",
    targets: [
      {
        id: "items",
        title: "Items (XML + OTB)",
        desc: "Recarrega catalogo admin e caches de /items.",
      },
      {
        id: "otb_reload",
        title: "Recarregar items.otb",
        desc: "Rele items.otb do disco (apos patch CID no site ou scp).",
      },
      {
        id: "outfits",
        title: "Outfits / Addons",
        desc: "Varkhal, outfits.xml, classificacao Extended.",
      },
      {
        id: "addon_bonuses",
        title: "Bonus textuais (addons)",
        desc: "Reparsa stats/skills/absorb de outfits.xml para /addons.",
      },
      {
        id: "mounts",
        title: "Mounts",
        desc: "mounts.xml + mount_quest.lua para /mounts.",
      },
      {
        id: "item_gifs",
        title: "GIFs de itens (host)",
        desc: "Roda extract_item_gifs.py no servidor (scan visual_item_scan.json). Requer Playwright.",
        adminOnly: true,
      },
    ],
  },
  {
    id: "gameplay",
    title: "3. Gameplay / guias",
    targets: [
      {
        id: "loot",
        title: "Loot / drops",
        desc: "Reconstrui indice item->monstro (/addons, /mounts, /item-drops).",
      },
      {
        id: "tasks",
        title: "Tasks",
        desc: "task_func.lua para /tasks.",
      },
      {
        id: "events",
        title: "Eventos / jackpot",
        desc: "Raids, boss rooms, war event, online reward.",
      },
      {
        id: "guides",
        title: "Server info / reset / vocations",
        desc: "EXP bonuses, bless, reset, spell heals, potions.",
      },
      {
        id: "prey",
        title: "Prey",
        desc: "Pool de monstros e bonus de /prey.",
      },
      {
        id: "patents",
        title: "Patentes",
        desc: "ARMY ranks para /patents.",
      },
      {
        id: "npcs",
        title: "NPCs no mapa",
        desc: "imported-spawn.xml para /npcs.",
      },
      {
        id: "shop",
        title: "Shop (MySQL)",
        desc: "Recarrega z_shop_offer (nomes de item via items.xml).",
      },
    ],
  },
  {
    id: "utils",
    title: "4. Utilitarios",
    targets: [
      {
        id: "clear_caches",
        title: "Limpar todos os caches",
        desc: "Remove JSON em portal/cache/ + loot index (nao altera DB).",
      },
      {
        id: "sync_all",
        title: "Sync all",
        desc: "Roda enciclopedia + visual (exceto GIFs/OTB) + gameplay + shop.",
      },
    ],
  },
];

function fmtMtime(ts: number): string {
  if (!ts) return "-";
  try {
    return new Date(ts * 1000).toLocaleString();
  } catch {
    return String(ts);
  }
}

export function AdminServerSync({
  token,
  pageAccess = 0,
}: {
  token: string;
  pageAccess?: number;
}) {
  const isAdmin = pageAccess >= 6;
  const [status, setStatus] = useState<StatusPayload | null>(null);
  const [busy, setBusy] = useState<SyncTarget | null>(null);
  const [last, setLast] = useState<SyncResult | null>(null);
  const [err, setErr] = useState("");
  const [gifScope, setGifScope] = useState<"scan" | "pickable" | "range">(
    "scan",
  );
  const [gifRange, setGifRange] = useState("");

  const loadStatus = useCallback(async () => {
    setErr("");
    try {
      const r = await fetch(apiUrl("admin-sync.php"), {
        headers: { Authorization: `Bearer ${token}` },
      });
      const json = (await r.json().catch(() => ({}))) as StatusPayload & {
        error?: string;
      };
      if (!r.ok) {
        setErr(json.error || "Falha ao carregar status.");
        return;
      }
      setStatus(json);
    } catch {
      setErr("Falha ao carregar status.");
    }
  }, [token]);

  useEffect(() => {
    void loadStatus();
  }, [loadStatus]);

  async function runSync(target: SyncTarget) {
    setBusy(target);
    setErr("");
    setLast(null);
    const body: Record<string, unknown> = { target };
    if (target === "item_gifs") {
      body.scope = gifScope;
      if (gifScope === "range" && gifRange.trim()) {
        body.range = gifRange.trim();
      }
    }
    try {
      const r = await fetch(apiUrl("admin-sync.php"), {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${token}`,
        },
        body: JSON.stringify(body),
      });
      const json = (await r.json().catch(() => ({}))) as SyncResult & {
        error?: string;
      };
      if (!r.ok) {
        setErr(json.error || "Falha no sync.");
        setLast({ ok: false, target, error: json.error });
        return;
      }
      setLast(json);
      await loadStatus();
    } catch {
      setErr("Falha no sync.");
      setLast({ ok: false, target, error: "network" });
    } finally {
      setBusy(null);
    }
  }

  return (
    <div className="space-y-6">
      <div>
        <h2 className="text-xl font-semibold tracking-tight">Server Sync</h2>
        <p className="mt-1 text-sm text-muted">
          Forca o portal a reler XMLs/OTB/Lua do OT e recarregar caches. GIFs no
          host usam Playwright (lento; prefira scan ou range curto).
        </p>
        {status?.serverPath && (
          <p className="mt-2 text-xs text-muted break-all">
            OT_SERVER_PATH: {status.serverPath}
          </p>
        )}
        {status?.htmlRepo && (
          <p className="text-xs text-muted break-all">
            HTML repo: {status.htmlRepo}
          </p>
        )}
        {status?.note && (
          <p className="mt-1 text-xs text-muted">{status.note}</p>
        )}
      </div>

      {err && (
        <p className="rounded-md border border-red-500/40 bg-red-500/10 px-3 py-2 text-sm text-red-200">
          {err}
        </p>
      )}

      {GROUPS.map((group) => (
        <section key={group.id} className="space-y-3">
          <h3 className="text-sm font-semibold text-foreground">{group.title}</h3>
          <div className="grid gap-3">
            {group.targets.map((t) => {
              if (t.adminOnly && !isAdmin) return null;
              return (
                <div
                  key={t.id}
                  className="flex flex-col gap-3 rounded-lg border border-white/10 bg-black/20 p-4 sm:flex-row sm:items-center sm:justify-between"
                >
                  <div className="min-w-0">
                    <div className="font-medium">{t.title}</div>
                    <p className="mt-1 text-sm text-muted">{t.desc}</p>
                    {t.id === "item_gifs" && isAdmin && (
                      <div className="mt-2 flex flex-wrap items-center gap-2 text-xs">
                        <select
                          className="rounded border border-border bg-background px-2 py-1"
                          value={gifScope}
                          onChange={(e) =>
                            setGifScope(
                              e.target.value as "scan" | "pickable" | "range",
                            )
                          }
                        >
                          <option value="scan">visual_item_scan.json</option>
                          <option value="pickable">Todos pickable</option>
                          <option value="range">Range manual</option>
                        </select>
                        {gifScope === "range" && (
                          <input
                            className="min-w-[140px] rounded border border-border bg-background px-2 py-1"
                            placeholder="ex. 12600-12750"
                            value={gifRange}
                            onChange={(e) => setGifRange(e.target.value)}
                          />
                        )}
                      </div>
                    )}
                  </div>
                  <button
                    type="button"
                    disabled={busy !== null}
                    onClick={() => void runSync(t.id)}
                    className="shrink-0 rounded-md bg-emerald-600 px-4 py-2 text-sm font-medium text-white hover:bg-emerald-500 disabled:opacity-50"
                  >
                    {busy === t.id ? "Sincronizando..." : "Sincronizar"}
                  </button>
                </div>
              );
            })}
          </div>
        </section>
      ))}

      {last && (
        <div className="rounded-lg border border-white/10 bg-black/20 p-4 text-sm">
          <div className="font-medium">
            Ultimo: {last.target}{" "}
            <span className={last.ok ? "text-emerald-400" : "text-red-300"}>
              {last.ok ? "OK" : "ERRO"}
            </span>
          </div>
          {last.detail && (
            <pre className="mt-2 max-h-64 overflow-auto whitespace-pre-wrap break-all text-xs text-muted">
              {JSON.stringify(last.detail, null, 2)}
            </pre>
          )}
        </div>
      )}

      {status?.files && (
        <div className="rounded-lg border border-white/10 bg-black/20 p-4">
          <div className="font-medium text-sm">Arquivos no host</div>
          <ul className="mt-2 space-y-1 text-xs text-muted">
            {Object.entries(status.files).map(([k, f]) => (
              <li key={k} className="break-all">
                <span className="text-foreground">{k}</span>:{" "}
                {f.exists ? `mtime ${fmtMtime(f.mtime)}` : "ausente"} - {f.path}
              </li>
            ))}
          </ul>
        </div>
      )}
    </div>
  );
}
// END CHANGE
