// BEGIN CHANGE: inspector de contas/players com suggest, ACL UI e secrets
"use client";

import { FormEvent, ReactNode, useEffect, useRef, useState } from "react";
import Link from "next/link";
import { apiUrl } from "@/lib/api";
import { ItemIcon } from "@/components/ItemIcon";
import { CollapsiblePanel } from "@/components/CollapsiblePanel";
import { canCritical, canEconomy, needsAdminPassword } from "./acl";
import { AdminConfirmModal } from "./AdminConfirmModal";
import {
  AdminAccount,
  AdminPlayer,
  LookupData,
  fmtGold,
  fmtTs,
  rentStatusClass,
  rentStatusLabel,
} from "./types";

function Field({ label, value }: { label: string; value: ReactNode }) {
  return (
    <div className="grid grid-cols-[140px_1fr] gap-2 border-b border-border/40 py-1.5 text-sm last:border-0">
      <dt className="text-muted">{label}</dt>
      <dd className="break-all font-medium text-foreground">{value}</dd>
    </div>
  );
}

type Suggest = {
  players: { id: number; name: string; level: number; accountId: number; online: boolean; deleted: boolean }[];
  accounts: { id: number; name: string; pageAccess: number; premiumPoints: number; blocked: boolean }[];
};

export function AdminInspector({
  token,
  access,
  initialQuery,
  initialMode,
  labels,
}: {
  token: string;
  access: number;
  initialQuery?: string;
  initialMode?: "player" | "account" | "accountId";
  labels: Record<string, string>;
}) {
  const [query, setQuery] = useState(initialQuery || "");
  const [mode, setMode] = useState<"player" | "account">(
    initialMode === "account" || initialMode === "accountId" ? "account" : "player",
  );
  const [data, setData] = useState<LookupData | null>(null);
  const [status, setStatus] = useState<"idle" | "loading" | "ok" | "error" | "empty">("idle");
  const [msg, setMsg] = useState("");
  const [err, setErr] = useState("");
  const [busy, setBusy] = useState(false);
  const [suggest, setSuggest] = useState<Suggest | null>(null);
  const [reveal, setReveal] = useState(false);
  const [pending, setPending] = useState<Record<string, unknown> | null>(null);
  const suggestTimer = useRef<ReturnType<typeof setTimeout> | null>(null);

  const [points, setPoints] = useState("");
  const [pageAccess, setPageAccess] = useState("0");
  const [email, setEmail] = useState("");
  const [newPass, setNewPass] = useState("");
  const [premDays, setPremDays] = useState("");
  const [accName, setAccName] = useState("");
  const [warnings, setWarnings] = useState("0");
  const [selectedPlayerId, setSelectedPlayerId] = useState(0);
  const [salvar, setSalvar] = useState("0");
  const [groupId, setGroupId] = useState("1");
  const [resetVal, setResetVal] = useState("0");
  const [mlDelta, setMlDelta] = useState("1");
  const [newPlayerName, setNewPlayerName] = useState("");
  const [migrateDest, setMigrateDest] = useState("");
  const [itemIdDel, setItemIdDel] = useState("");
  const [guildPtsDelta, setGuildPtsDelta] = useState("10");

  // BEGIN CHANGE: item 13 - valores de page_access carregados do banco + insercao de novo
  const [pageAccessValues, setPageAccessValues] = useState<number[]>([]);
  const [pageAccessCustom, setPageAccessCustom] = useState(false);
  // END CHANGE

  // BEGIN CHANGE: item 12 - serials inline (sem redirecionar para /serial-items)
  type SerialItem = { itemId: number; itemName: string; serial: string; location: string };
  const [serialItems, setSerialItems] = useState<SerialItem[] | null>(null);
  const [serialBusy, setSerialBusy] = useState(false);
  const [serialErr, setSerialErr] = useState("");
  const [showSerial, setShowSerial] = useState(false);

  // BEGIN CHANGE: resultado completo de /character incorporado no Admin
  const [showChar, setShowChar] = useState(false);
  // END CHANGE

  async function loadSerials(playerId: number) {
    setSerialBusy(true);
    setSerialErr("");
    try {
      const r = await fetch(apiUrl(`admin-panel.php?tool=player_serials&playerId=${playerId}`), {
        headers: { Authorization: `Bearer ${token}` },
      });
      const json = await r.json().catch(() => ({}));
      if (!r.ok) {
        setSerialErr(json.error || labels.actionFailed);
        setSerialItems([]);
        return;
      }
      setSerialItems((json.data?.items || json.items || []) as SerialItem[]);
    } catch {
      setSerialErr(labels.actionFailed);
      setSerialItems([]);
    } finally {
      setSerialBusy(false);
    }
  }
  // END CHANGE

  function applyData(d: LookupData) {
    setData(d);
    setPoints(String(d.account.premiumPoints));
    setPageAccess(String(d.account.pageAccess));
    setEmail(d.account.email || "");
    setPremDays(String(d.account.premdays));
    setAccName(d.account.name);
    setWarnings(String(d.account.warnings));
    const focus =
      d.players.find((p) => p.focused) || d.players.find((p) => !p.deleted) || d.players[0];
    if (focus) {
      setSelectedPlayerId(focus.id);
      setSalvar(String(focus.salvar));
      setGroupId(String(focus.groupId));
      setResetVal(String(focus.reset));
    }
    setStatus("ok");
  }

  async function lookup(opts: {
    player?: string;
    account?: string;
    accountId?: number;
    revealSecrets?: boolean;
  }) {
    setStatus("loading");
    setErr("");
    setMsg("");
    setData(null);
    setSuggest(null);
    const revealSecrets = opts.revealSecrets ?? reveal;
    let url = "";
    if (opts.accountId && opts.accountId > 0) {
      url = apiUrl(`admin-panel.php?accountId=${opts.accountId}`);
    } else if (opts.account) {
      url = apiUrl(`admin-panel.php?account=${encodeURIComponent(opts.account)}`);
    } else if (opts.player) {
      url = apiUrl(`admin-panel.php?name=${encodeURIComponent(opts.player)}`);
    } else {
      return;
    }
    if (revealSecrets) url += (url.includes("?") ? "&" : "?") + "reveal=1";
    try {
      const r = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
      if (r.status === 404) {
        setStatus("empty");
        return;
      }
      if (!r.ok) throw new Error("http");
      const json = await r.json();
      setReveal(!!json.secretsRevealed);
      applyData(json.data as LookupData);
    } catch {
      setStatus("error");
    }
  }

  async function search(e?: FormEvent) {
    e?.preventDefault();
    if (!query.trim()) return;
    if (mode === "player") await lookup({ player: query.trim() });
    else await lookup({ account: query.trim() });
  }

  useEffect(() => {
    if (!initialQuery) return;
    if (initialMode === "accountId") {
      const id = Number(initialQuery);
      if (id > 0) lookup({ accountId: id });
    } else if (initialMode === "account") {
      setMode("account");
      setQuery(initialQuery);
      lookup({ account: initialQuery });
    } else {
      setMode("player");
      setQuery(initialQuery);
      lookup({ player: initialQuery });
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [initialQuery, initialMode]);

  // BEGIN CHANGE: item 13 - carrega valores de page_access existentes no banco
  useEffect(() => {
    fetch(apiUrl("admin-panel.php?tool=page_access_values"), {
      headers: { Authorization: `Bearer ${token}` },
    })
      .then((r) => (r.ok ? r.json() : null))
      .then((json) => {
        if (json && Array.isArray(json.data)) setPageAccessValues(json.data as number[]);
      })
      .catch(() => {});
  }, [token]);
  // END CHANGE

  // BEGIN CHANGE: item 12 - some com serials/character embed ao trocar de player
  useEffect(() => {
    setShowSerial(false);
    setSerialItems(null);
    setSerialErr("");
    setShowChar(false);
  }, [selectedPlayerId]);
  // END CHANGE

  useEffect(() => {
    if (suggestTimer.current) clearTimeout(suggestTimer.current);
    const q = query.trim();
    if (q.length < 2 || status === "ok") {
      setSuggest(null);
      return;
    }
    suggestTimer.current = setTimeout(async () => {
      try {
        const r = await fetch(apiUrl(`admin-panel.php?tool=suggest&q=${encodeURIComponent(q)}`), {
          headers: { Authorization: `Bearer ${token}` },
        });
        const json = await r.json().catch(() => ({}));
        if (r.ok) setSuggest(json.data as Suggest);
      } catch {
        /* ignore */
      }
    }, 280);
    return () => {
      if (suggestTimer.current) clearTimeout(suggestTimer.current);
    };
  }, [query, token, status]);

  async function postAction(body: Record<string, unknown>) {
    setBusy(true);
    setErr("");
    setMsg("");
    try {
      const r = await fetch(apiUrl("admin-panel.php"), {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${token}`,
        },
        body: JSON.stringify(body),
      });
      const json = await r.json().catch(() => ({}));
      if (!r.ok) {
        setErr(
          json.error === "admin_password_required"
            ? labels.adminPasswordRequired
            : json.error || labels.actionFailed,
        );
        return;
      }
      if (json.data) applyData(json.data as LookupData);
      else if (json.data === null) {
        setData(null);
        setStatus("idle");
      }
      if (json.recoveryKey) setMsg(`${labels.newRecoveryKey}: ${json.recoveryKey}`);
      else if (json.note) setMsg(json.note);
      else setMsg(labels.actionOk);
    } catch {
      setErr(labels.actionFailed);
    } finally {
      setBusy(false);
    }
  }

  function queueAction(body: Record<string, unknown>) {
    const action = String(body.action || "");
    if (needsAdminPassword(action) || action === "delete_player" || action === "delete_account") {
      setPending(body);
      return;
    }
    return postAction(body);
  }

  function onSelectPlayer(id: number) {
    setSelectedPlayerId(id);
    const p = data?.players.find((x) => x.id === id);
    if (p) {
      setSalvar(String(p.salvar));
      setGroupId(String(p.groupId));
      setResetVal(String(p.reset));
    }
  }

  const selected = data?.players.find((p) => p.id === selectedPlayerId) || null;
  const a: AdminAccount | undefined = data?.account;
  const offlineOk = selected ? !selected.online : true;

  return (
    <div className="space-y-6">
      <h2 className="text-xl font-semibold">{labels.accountSection}</h2>
      <form onSubmit={search} className="relative flex flex-wrap items-end gap-3 rounded-xl border border-border bg-panel p-4">
        <div className="flex gap-2 text-sm">
          <label className="inline-flex items-center gap-1.5">
            <input type="radio" checked={mode === "player"} onChange={() => setMode("player")} />
            {labels.byPlayer}
          </label>
          <label className="inline-flex items-center gap-1.5">
            <input type="radio" checked={mode === "account"} onChange={() => setMode("account")} />
            {labels.byAccount}
          </label>
        </div>
        <input
          className="min-w-[220px] flex-1 rounded-md border border-border bg-background px-3 py-2 text-sm"
          placeholder={mode === "player" ? labels.playerPlaceholder : labels.accountPlaceholder}
          value={query}
          onChange={(e) => {
            setQuery(e.target.value);
            if (status === "ok") setStatus("idle");
          }}
          autoComplete="off"
        />
        <button
          type="submit"
          disabled={busy || status === "loading"}
          className="rounded-md bg-brand px-4 py-2 text-sm font-medium text-background disabled:opacity-50"
        >
          {labels.search}
        </button>
        {suggest && (suggest.players.length > 0 || suggest.accounts.length > 0) && (
          <div className="absolute left-4 right-4 top-full z-20 mt-1 max-h-56 overflow-auto rounded-lg border border-border bg-panel shadow-lg">
            {suggest.players.map((p) => (
              <button
                key={"p" + p.id}
                type="button"
                className="block w-full px-3 py-2 text-left text-sm hover:bg-brand/10"
                onClick={() => {
                  setQuery(p.name);
                  setMode("player");
                  lookup({ player: p.name });
                }}
              >
                Player: {p.name} L{p.level}
                {p.online ? " ONLINE" : ""}
                {p.deleted ? " (del)" : ""}
              </button>
            ))}
            {suggest.accounts.map((acc) => (
              <button
                key={"a" + acc.id}
                type="button"
                className="block w-full px-3 py-2 text-left text-sm hover:bg-brand/10"
                onClick={() => {
                  setQuery(acc.name);
                  setMode("account");
                  lookup({ account: acc.name });
                }}
              >
                Account: {acc.name} (pts {acc.premiumPoints})
                {acc.blocked ? " BLOCKED" : ""}
              </button>
            ))}
          </div>
        )}
      </form>

      {msg && <p className="text-sm text-brand">{msg}</p>}
      {err && <p className="text-sm text-red-400">{err}</p>}
      {status === "loading" && <p className="text-muted">{labels.loading}</p>}
      {status === "error" && <p className="text-muted">{labels.failed}</p>}
      {status === "empty" && <p className="text-muted">{labels.notFound}</p>}

      {status === "ok" && a && (
        <div className="space-y-8">
          {/* BEGIN CHANGE: 2026-08-03 - CollapsiblePanel no inspector */}
          <CollapsiblePanel
            id="admin-insp-account"
            title={labels.accountSection}
            titleClassName="text-lg font-semibold"
            defaultOpen={true}
            headerRight={
              canCritical(access) ? (
                <button
                  type="button"
                  className="rounded border border-border px-3 py-1 text-xs"
                  onClick={(e) => {
                    e.stopPropagation();
                    lookup({ accountId: a.id, revealSecrets: !reveal });
                  }}
                >
                  {reveal ? labels.hideSecrets : labels.showSecrets}
                </button>
              ) : undefined
            }
          >
            <p className="text-xs text-muted">{a.passwordNote}</p>
            <dl className="mt-4">
              <Field label="ID" value={a.id} />
              <Field label={labels.accountName} value={a.name} />
              <Field
                label={labels.passwordHash}
                value={<code className="font-mono text-xs">{a.passwordHash || "????"}</code>}
              />
              <Field
                label={labels.recoveryKey}
                value={
                  reveal && a.hasRecoveryKey && a.recoveryKey ? (
                    <code className="font-mono text-xs">{a.recoveryKey}</code>
                  ) : a.hasRecoveryKey ? (
                    "???????? (masked)"
                  ) : (
                    labels.noRecoveryKey
                  )
                }
              />
              <Field label="E-mail" value={a.email || "-"} />
              <Field label={labels.pageAccess} value={a.pageAccess} />
              <Field label={labels.premiumPoints} value={a.premiumPoints} />
              <Field label={labels.backupPoints} value={a.backupPoints} />
              <Field label={labels.guildPoints} value={a.guildPoints} />
              <Field label="Warnings" value={a.warnings} />
              <Field
                label="Premium"
                value={
                  a.isPremium
                    ? `${a.premiumDaysLeft} days left`
                    : `Free (stored ${a.premdays})`
                }
              />
              <Field label={labels.blocked} value={a.blocked ? "yes" : "no"} />
              <Field label="Created" value={fmtTs(a.created)} />
            </dl>
          </CollapsiblePanel>

          <CollapsiblePanel
            id="admin-insp-actions"
            title={labels.actionsSection}
            titleClassName="text-lg font-semibold"
            defaultOpen={true}
          >
            <div className="grid gap-4 sm:grid-cols-2">
              {canEconomy(access) ? (
                <>
                  {/* BEGIN CHANGE: so premium_points editavel; backup_points imutavel (doacao) */}
                  <ActionBox title={labels.setPoints}>
                    <label className="block text-xs text-muted">
                      {labels.premiumPoints}
                      <input
                        className="field mt-1"
                        value={points}
                        onChange={(e) => setPoints(e.target.value)}
                        placeholder="premium_points"
                      />
                    </label>
                    <label className="block text-xs text-muted">
                      {labels.backupPoints}
                      <input
                        className="field mt-1 opacity-70"
                        value={String(a.backupPoints)}
                        readOnly
                        disabled
                        title={labels.backupPointsHint}
                      />
                    </label>
                    <p className="text-xs text-muted">{labels.backupPointsHint}</p>
                    <Btn
                      busy={busy}
                      onClick={() =>
                        queueAction({
                          action: "set_points",
                          accountId: a.id,
                          premiumPoints: Number(points),
                        })
                      }
                    >
                      {labels.save}
                    </Btn>
                  </ActionBox>
                  {/* END CHANGE */}
                  <ActionBox title={labels.setEmail}>
                    <input className="field" value={email} onChange={(e) => setEmail(e.target.value)} />
                    <Btn busy={busy} onClick={() => queueAction({ action: "set_email", accountId: a.id, email })}>{labels.save}</Btn>
                  </ActionBox>
                  <ActionBox title={labels.setPremDays}>
                    <input className="field" value={premDays} onChange={(e) => setPremDays(e.target.value)} />
                    <Btn busy={busy} onClick={() => queueAction({ action: "set_premium_days", accountId: a.id, premdays: Number(premDays), lastday: Math.floor(Date.now() / 1000) })}>{labels.save}</Btn>
                  </ActionBox>
                  <ActionBox title="Warnings">
                    <input className="field" value={warnings} onChange={(e) => setWarnings(e.target.value)} />
                    <Btn busy={busy} onClick={() => queueAction({ action: "set_warnings", accountId: a.id, warnings: Number(warnings) })}>{labels.save}</Btn>
                  </ActionBox>
                  <ActionBox title="Guild points (+/-)">
                    <input className="field" value={guildPtsDelta} onChange={(e) => setGuildPtsDelta(e.target.value)} />
                    <Btn busy={busy} onClick={() => queueAction({ action: "add_guild_points", accountId: a.id, delta: Number(guildPtsDelta) })}>{labels.apply}</Btn>
                  </ActionBox>
                </>
              ) : (
                <p className="text-sm text-muted col-span-2">{labels.noEconomy}</p>
              )}

              {canCritical(access) ? (
                <>
                  <ActionBox title={labels.setAccess}>
                    {/* BEGIN CHANGE: item 13 - dropdown dinamico (valores do banco) + inserir novo */}
                    {!pageAccessCustom ? (
                      <select
                        className="field"
                        value={pageAccess}
                        onChange={(e) => {
                          if (e.target.value === "__custom__") {
                            setPageAccessCustom(true);
                          } else {
                            setPageAccess(e.target.value);
                          }
                        }}
                      >
                        {mergePageAccessOptions(pageAccessValues, Number(pageAccess)).map((v) => (
                          <option key={v} value={String(v)}>
                            {pageAccessLabel(v)}
                          </option>
                        ))}
                        <option value="__custom__">+ Inserir novo valor...</option>
                      </select>
                    ) : (
                      <div className="flex gap-2">
                        <input
                          className="field"
                          type="number"
                          value={pageAccess}
                          onChange={(e) => setPageAccess(e.target.value)}
                          placeholder="Novo page_access"
                          autoFocus
                        />
                        <button
                          type="button"
                          className="rounded border border-border px-2 text-xs text-muted"
                          onClick={() => setPageAccessCustom(false)}
                        >
                          Lista
                        </button>
                      </div>
                    )}
                    <Btn busy={busy} onClick={() => queueAction({ action: "set_page_access", accountId: a.id, pageAccess: Number(pageAccess) })}>{labels.save}</Btn>
                    {/* END CHANGE */}
                  </ActionBox>
                  <ActionBox title={labels.setPassword}>
                    <p className="text-xs text-muted">{labels.setPasswordHint}</p>
                    <input className="field" type="text" value={newPass} onChange={(e) => setNewPass(e.target.value)} />
                    <Btn busy={busy} onClick={() => { queueAction({ action: "set_password", accountId: a.id, password: newPass }); setNewPass(""); }}>{labels.save}</Btn>
                  </ActionBox>
                  <ActionBox title={labels.renameAccount}>
                    <input className="field" value={accName} onChange={(e) => setAccName(e.target.value)} />
                    <Btn busy={busy} onClick={() => queueAction({ action: "set_account_name", accountId: a.id, accountName: accName })}>{labels.save}</Btn>
                  </ActionBox>
                  <ActionBox title={labels.blockAccount}>
                    <div className="flex gap-2">
                      <Btn busy={busy || a.blocked} danger onClick={() => queueAction({ action: "set_blocked", accountId: a.id, blocked: true })}>{labels.block}</Btn>
                      <Btn busy={busy || !a.blocked} onClick={() => queueAction({ action: "set_blocked", accountId: a.id, blocked: false })}>{labels.unblock}</Btn>
                    </div>
                  </ActionBox>
                  <ActionBox title={labels.regenKey}>
                    <Btn busy={busy} onClick={() => queueAction({ action: "regen_recovery_key", accountId: a.id })}>{labels.regenKey}</Btn>
                  </ActionBox>
                </>
              ) : (
                <p className="text-sm text-muted col-span-2">{labels.noCritical}</p>
              )}
            </div>
          </CollapsiblePanel>

          <CollapsiblePanel
            id="admin-insp-players"
            title={labels.playersSection}
            titleClassName="text-lg font-semibold"
            defaultOpen={true}
          >
            <div className="mt-3 flex flex-wrap gap-2">
              {data!.players.map((p: AdminPlayer) => (
                <button
                  key={p.id}
                  type="button"
                  onClick={() => onSelectPlayer(p.id)}
                  className={`rounded-md border px-3 py-1.5 text-sm ${
                    selectedPlayerId === p.id ? "border-brand bg-brand/10" : "border-border text-muted"
                  } ${p.deleted ? "opacity-50 line-through" : ""}`}
                >
                  {p.name} (L{p.level})
                  {p.online ? <span className="ml-1 text-emerald-400">ONLINE</span> : null}
                </button>
              ))}
            </div>
            {selected && (
              <>
                <div className="mt-4 flex flex-wrap gap-2 text-sm">
                  {/* BEGIN CHANGE: item 12 (2026) - abre personagem INLINE (sem redirecionar) */}
                  <button
                    type="button"
                    className="rounded-md border border-border px-3 py-1.5 hover:border-brand"
                    onClick={() => {
                      setShowChar((v) => !v);
                    }}
                  >
                    {labels.openCharacter}
                  </button>
                  <Link
                    href={`/character?name=${encodeURIComponent(selected.name)}`}
                    target="_blank"
                    rel="noopener noreferrer"
                    className="rounded-md border border-border/50 px-2 py-1.5 text-xs text-muted hover:border-brand"
                  >
                    nova aba
                  </Link>
                  {/* item 12 - Item Serial inline (sem redirecionar) */}
                  <button
                    type="button"
                    className="rounded-md border border-border px-3 py-1.5 hover:border-brand"
                    onClick={() => {
                      const next = !showSerial;
                      setShowSerial(next);
                      if (next && serialItems === null) loadSerials(selected.id);
                    }}
                  >
                    {labels.openSerial}
                  </button>
                  {/* END CHANGE */}
                  {selected.online && (
                    <span className="rounded-md border border-amber-500/50 bg-amber-500/10 px-3 py-1.5 text-amber-200">
                      {labels.playerOnline}
                    </span>
                  )}
                </div>

                {/* BEGIN CHANGE: replica integralmente a pesquisa publica de personagem */}
                {showChar && (
                  <div className="mt-3 overflow-hidden rounded-lg border border-border/70 bg-background">
                    <iframe
                      key={selected.name}
                      src={`/character/?name=${encodeURIComponent(selected.name)}&embed=1`}
                      title={`Personagem ${selected.name}`}
                      className="h-[75vh] min-h-[720px] w-full border-0"
                      loading="lazy"
                    />
                  </div>
                )}
                {/* END CHANGE */}

                {/* BEGIN CHANGE: item 12 - painel inline de serials do player selecionado */}
                {showSerial && (
                  <div className="mt-3 rounded-lg border border-border/70 bg-background/60 p-3">
                    {serialBusy && <p className="text-sm text-muted">{labels.loading}</p>}
                    {serialErr && <p className="text-sm text-red-400">{serialErr}</p>}
                    {!serialBusy && serialItems && serialItems.length === 0 && (
                      <p className="text-sm text-muted">Nenhum item com serial neste personagem.</p>
                    )}
                    {serialItems && serialItems.length > 0 && (
                      <div className="overflow-auto">
                        <table className="w-full text-left text-xs">
                          <thead className="text-muted">
                            <tr>
                              {/* BEGIN CHANGE: imagem do item serial */}
                              <th className="py-1 pr-2">Imagem</th>
                              {/* END CHANGE */}
                              <th className="py-1 pr-2">Item</th>
                              <th className="py-1 pr-2">ID</th>
                              <th className="py-1 pr-2">Serial</th>
                              <th className="py-1">Local</th>
                            </tr>
                          </thead>
                          <tbody>
                            {serialItems.map((s, i) => (
                              <tr key={i} className="border-t border-border/40">
                                {/* BEGIN CHANGE: imagem do item serial */}
                                <td className="py-1 pr-2">
                                  <ItemIcon id={s.itemId} alt={s.itemName} size={32} />
                                </td>
                                {/* END CHANGE */}
                                <td className="py-1 pr-2">{s.itemName}</td>
                                <td className="py-1 pr-2 tabular-nums">{s.itemId}</td>
                                <td className="py-1 pr-2 font-mono">{s.serial}</td>
                                <td className="py-1">{s.location}</td>
                              </tr>
                            ))}
                          </tbody>
                        </table>
                      </div>
                    )}
                  </div>
                )}
                {/* END CHANGE */}
                <dl className="mt-5">
                  <Field label="ID" value={selected.id} />
                  <Field label="Level / Exp" value={`${selected.level} / ${selected.experience}`} />
                  <Field label="Vocation" value={selected.vocation} />
                  <Field label="Reset" value={selected.reset} />
                  <Field label="salvar" value={selected.salvar} />
                  <Field label="group_id" value={selected.groupId} />
                  <Field label="Magic" value={selected.magic} />
                  <Field label="HP / MP" value={`${selected.healthMax} / ${selected.manaMax}`} />
                  <Field label="Balance" value={selected.balance} />
                  <Field label="Pos" value={`${selected.position.x}, ${selected.position.y}, ${selected.position.z}`} />
                  <Field label="Last login" value={fmtTs(selected.lastlogin)} />
                  <Field label="IP" value={selected.lastIp} />
                </dl>

                {/* BEGIN CHANGE: 2026-08-03 - houses do personagem */}
                <div className="mt-4 rounded-lg border border-border/60 p-3">
                  <h4 className="text-sm font-medium">
                    Houses ({(selected.houses || []).length})
                  </h4>
                  {(selected.houses || []).length === 0 ? (
                    <p className="mt-2 text-xs text-muted">Nenhuma house alugada.</p>
                  ) : (
                    <div className="mt-2 overflow-auto">
                      <table className="w-full text-left text-xs">
                        <thead className="text-muted">
                          <tr>
                            <th className="py-1 pr-2">House</th>
                            <th className="py-1 pr-2">Town</th>
                            <th className="py-1 pr-2">Rent</th>
                            <th className="py-1 pr-2">Pago ate</th>
                            <th className="py-1 pr-2">Warn</th>
                            <th className="py-1">Status</th>
                          </tr>
                        </thead>
                        <tbody>
                          {(selected.houses || []).map((h) => (
                            <tr key={h.houseId} className="border-t border-border/40">
                              <td className="py-1 pr-2">{h.houseName}</td>
                              <td className="py-1 pr-2 tabular-nums">{h.town}</td>
                              <td className="py-1 pr-2 tabular-nums">{fmtGold(h.rent)}</td>
                              <td className="py-1 pr-2 whitespace-nowrap">{fmtTs(h.paid)}</td>
                              <td className="py-1 pr-2 tabular-nums">{h.warnings}</td>
                              <td className={`py-1 ${rentStatusClass(h.rentStatus)}`}>
                                {rentStatusLabel(h.rentStatus)}
                              </td>
                            </tr>
                          ))}
                        </tbody>
                      </table>
                    </div>
                  )}
                </div>
                {/* END CHANGE */}

                {canEconomy(access) && (
                  <div className="mt-4 grid gap-4 sm:grid-cols-3">
                    <ActionBox title="salvar">
                      <input className="field" value={salvar} onChange={(e) => setSalvar(e.target.value)} />
                      <Btn busy={busy} onClick={() => queueAction({ action: "set_salvar", playerId: selected.id, salvar: Number(salvar) })}>{labels.save}</Btn>
                      {/* BEGIN CHANGE: guia rapida salvar */}
                      <p className="mt-2 text-[11px] leading-snug text-muted">
                        0 = normal. &gt;0 = so online + /character (fora de rankings). &gt;1 = oculto no portal.
                        In-game: &gt;=1 isento Anti-Bot e protegido do dbcleaner.
                      </p>
                      {/* END CHANGE */}
                    </ActionBox>
                    <ActionBox title="reset">
                      <input className="field" value={resetVal} onChange={(e) => setResetVal(e.target.value)} />
                      <Btn busy={busy} onClick={() => queueAction({ action: "set_reset", playerId: selected.id, reset: Number(resetVal) })}>{labels.save}</Btn>
                    </ActionBox>
                    <ActionBox title="Magic level (+/-)">
                      <input className="field" value={mlDelta} onChange={(e) => setMlDelta(e.target.value)} disabled={!offlineOk} />
                      <Btn busy={busy || !offlineOk} onClick={() => queueAction({ action: "add_maglevel", playerId: selected.id, delta: Number(mlDelta) })}>{labels.apply}</Btn>
                    </ActionBox>
                    <ActionBox title="Rename">
                      <input className="field" value={newPlayerName} onChange={(e) => setNewPlayerName(e.target.value)} disabled={!offlineOk} />
                      <Btn busy={busy || !offlineOk} onClick={() => queueAction({ action: "rename_player", playerId: selected.id, newName: newPlayerName })}>{labels.apply}</Btn>
                    </ActionBox>
                    <ActionBox title="Delete item id">
                      <input className="field" value={itemIdDel} onChange={(e) => setItemIdDel(e.target.value)} disabled={!offlineOk} />
                      <Btn busy={busy || !offlineOk} onClick={() => queueAction({ action: "delete_player_item", playerId: selected.id, itemId: Number(itemIdDel) })}>{labels.apply}</Btn>
                    </ActionBox>
                  </div>
                )}

                {canCritical(access) && (
                  <div className="mt-4 grid gap-4 sm:grid-cols-2">
                    <ActionBox title="group_id">
                      <input className="field" value={groupId} onChange={(e) => setGroupId(e.target.value)} />
                      <Btn busy={busy} onClick={() => queueAction({ action: "set_player_group", playerId: selected.id, groupId: Number(groupId) })}>{labels.save}</Btn>
                    </ActionBox>
                    <ActionBox title="Migrate account">
                      <input className="field" value={migrateDest} onChange={(e) => setMigrateDest(e.target.value)} disabled={!offlineOk} placeholder="Account ID or player" />
                      <Btn
                        busy={busy || !offlineOk}
                        onClick={() => {
                          const n = Number(migrateDest);
                          if (Number.isFinite(n) && n > 0 && String(n) === migrateDest.trim()) {
                            queueAction({ action: "migrate_player", playerId: selected.id, destAccountId: n });
                          } else {
                            queueAction({ action: "migrate_player", playerId: selected.id, destPlayerName: migrateDest.trim() });
                          }
                        }}
                      >
                        {labels.apply}
                      </Btn>
                    </ActionBox>
                    <ActionBox title={labels.dangerZone}>
                      <Btn busy={busy || !offlineOk} danger onClick={() => queueAction({ action: "delete_player", playerId: selected.id, confirm: "DELETE" })}>
                        {labels.deletePlayer}
                      </Btn>
                      <Btn busy={busy} danger onClick={() => queueAction({ action: "delete_account", accountId: a.id, confirm: "DELETE" })}>
                        {labels.deleteAccount}
                      </Btn>
                    </ActionBox>
                  </div>
                )}
              </>
            )}
          </CollapsiblePanel>

          {data?.donations && (
            <CollapsiblePanel
              id="admin-insp-donations"
              title={labels.donationsAccount}
              titleClassName="text-lg font-semibold"
              defaultOpen={false}
            >
              {(["mercadopago", "paypal", "pagseguro"] as const).map((prov) => {
                const rows = data.donations?.[prov] || [];
                if (!rows.length) return null;
                return (
                  <div key={prov} className="mt-3">
                    <h4 className="text-sm text-muted capitalize">{prov}</h4>
                    <ul className="mt-1 space-y-1 text-sm">
                      {rows.slice(0, 10).map((d) => (
                        <li key={prov + d.id}>
                          {d.amount} - {d.status}
                          {d.processed === false ? " / unprocessed" : ""} - {d.when}
                        </li>
                      ))}
                    </ul>
                  </div>
                );
              })}
            </CollapsiblePanel>
          )}

          <CollapsiblePanel
            id="admin-insp-shop"
            title={labels.shopSection}
            titleClassName="text-lg font-semibold"
            defaultOpen={false}
          >
            {(!data!.shopHistory || data!.shopHistory.length === 0) && (
              <p className="mt-2 text-sm text-muted">{labels.empty}</p>
            )}
            {data!.shopHistory?.length > 0 && (
              <div className="mt-3 overflow-x-auto">
                <table className="w-full text-left text-sm">
                  <thead className="text-muted">
                    <tr>
                      <th className="py-1 pr-3">Catalog</th>
                      <th className="py-1 pr-3">Source</th>
                      <th className="py-1 pr-3">To</th>
                      <th className="py-1 pr-3">Item</th>
                      <th className="py-1 pr-3">Pts</th>
                      <th className="py-1">Delivered</th>
                    </tr>
                  </thead>
                  <tbody>
                    {data!.shopHistory.map((h, i) => (
                      <tr key={i} className="border-t border-border/40">
                        <td className="py-1.5 pr-3">{h.catalog || "-"}</td>
                        <td className="py-1.5 pr-3">{h.source}</td>
                        <td className="py-1.5 pr-3">{h.toName}</td>
                        <td className="py-1.5 pr-3">{h.item || "-"}</td>
                        <td className="py-1.5 pr-3">
                          {h.price} {h.catalog === "guild" ? "GP" : "PP"}
                        </td>
                        <td className="py-1.5">
                          {h.delivered > 0 ? fmtTs(h.delivered) : (
                            <span className="text-red-400">undelivered</span>
                          )}
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            )}
          </CollapsiblePanel>
          {/* END CHANGE */}
        </div>
      )}

      <AdminConfirmModal
        open={!!pending}
        title={labels.confirmTitle}
        hint={labels.confirmHint}
        requirePassword={needsAdminPassword(String(pending?.action || ""))}
        requireDeleteWord={["delete_player", "delete_account"].includes(String(pending?.action || ""))}
        confirmLabel={labels.confirmBtn}
        cancelLabel={labels.cancel}
        passwordLabel={labels.adminPassword}
        onCancel={() => setPending(null)}
        onConfirm={async ({ adminPassword, confirm }) => {
          if (!pending) return;
          const body = { ...pending, adminPassword, confirm: confirm || pending.confirm };
          setPending(null);
          await postAction(body);
        }}
      />

    </div>
  );
}

// BEGIN CHANGE: item 13 - helpers do dropdown de page_access
function mergePageAccessOptions(values: number[], current: number): number[] {
  const base = [0, 3, 4, 5, 6];
  const set = new Set<number>([...base, ...values]);
  if (Number.isFinite(current)) set.add(current);
  return Array.from(set).sort((a, b) => a - b);
}

function pageAccessLabel(v: number): string {
  const known: Record<number, string> = {
    0: "0 - Player",
    3: "3 - Manager / News",
    4: "4 - Support",
    5: "5 - Subadmin",
    6: "6 - Admin",
  };
  return known[v] || String(v);
}
// END CHANGE

function ActionBox({ title, children }: { title: string; children: ReactNode }) {
  return (
    <div className="space-y-2 rounded-lg border border-border/60 p-3 [&_input]:w-full [&_input]:rounded [&_input]:border [&_input]:border-border [&_input]:bg-background [&_input]:px-2 [&_input]:py-1.5 [&_input]:text-sm [&_select]:w-full [&_select]:rounded [&_select]:border [&_select]:border-border [&_select]:bg-background [&_select]:px-2 [&_select]:py-1.5 [&_select]:text-sm">
      <p className="text-sm font-medium">{title}</p>
      {children}
    </div>
  );
}

function Btn({
  children,
  onClick,
  busy,
  danger,
}: {
  children: ReactNode;
  onClick: () => void;
  busy?: boolean;
  danger?: boolean;
}) {
  return (
    <button
      type="button"
      disabled={busy}
      onClick={onClick}
      className={`rounded px-3 py-1.5 text-sm text-white disabled:opacity-50 ${
        danger ? "bg-rose-600" : "bg-brand/90"
      }`}
    >
      {children}
    </button>
  );
}
// END CHANGE
