// Admin Portal React App.
// Bundlerless, loaded via Babel standalone. Fully inherits user's original design,
// mapping state changes directly to the remote D1 SQL database API.

const ADMIN_REASONS = [
  'Duplicate of an existing catalog entry.',
  'External URL returned HTTP 404 during verification.',
  'Low-effort / off-topic content.',
  'Broken download — archive could not be mirrored.',
  'Violates community guidelines.',
];

function stableAvatarColor(name) {
  if (!name) return 'oklch(0.62 0.16 25)';
  const charSum = name.split('').reduce((sum, c) => sum + c.charCodeAt(0), 0);
  const colors = [
    'oklch(0.62 0.16 25)', 'oklch(0.62 0.15 50)', 'oklch(0.60 0.14 145)',
    'oklch(0.58 0.15 200)', 'oklch(0.56 0.16 265)', 'oklch(0.58 0.17 310)',
    'oklch(0.60 0.15 95)', 'oklch(0.58 0.15 170)'
  ];
  return colors[charSum % colors.length];
}

function ConfirmDialog({ tone, icon, title, body, confirmLabel, onConfirm, onCancel }) {
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onCancel(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onCancel]);
  const toneColor = tone === 'warn' ? 'oklch(0.70 0.14 70)' : 'oklch(0.58 0.18 25)';
  const toneBg = tone === 'warn' ? 'var(--st-pending-bg)' : 'var(--st-reject-bg)';
  return (
    <div className="confirm-scrim" onClick={onCancel}>
      <div className="confirm-card" onClick={(e) => e.stopPropagation()}>
        <div className="cc-ic" style={{ background: toneBg, color: toneColor }}>{icon}</div>
        <h3>{title}</h3>
        <p>{body}</p>
        <div className="confirm-actions">
          <button className="cancel" onClick={onCancel}>Cancel</button>
          <button className={tone === 'warn' ? 'warn' : 'danger'} onClick={onConfirm}>{confirmLabel}</button>
        </div>
      </div>
    </div>
  );
}

// ── Review Queue ────────────────────────────────────────────────────────────
function QueueRow({ item, selected, onToggleSel, onApprove, onReject }) {
  const [open, setOpen] = React.useState(false);
  const isSub = item.type === 'submission';
  return (
    <div className={'qrow' + (selected ? ' sel' : '')}>
      <button className={'qcheck' + (selected ? ' on' : '')} onClick={onToggleSel} title="Select">{window.ic.check}</button>
      <div className="qmain">
        <div className="q-author">
          <window.Avatar identity={{ color: item.color, initial: item.author[0] }} size={16} />
          <b>{item.author}</b>
          <span className="st-badge neutral"><span className="d" />{isSub ? 'Submission' : 'Comment'}</span>
          <span className="time">{item.time}</span>
        </div>

        {isSub ? (
          <div className="q-title">
            Submitted game <b>{item.title}</b> by <b>{item.creator}</b>
          </div>
        ) : (
          <div className="q-text">“{item.text}”</div>
        )}

        {open && (
          <div className="q-detail">
            {isSub ? (
              <div className="meta-grid">
                <div><span>External Link</span><a href={item.url} target="_blank" rel="noopener noreferrer">{item.url}</a></div>
                {item.tags && item.tags.length > 0 && <div><span>Tags</span>{item.tags.join(', ')}</div>}
                {item.desc && <div className="full"><span>Description</span>{item.desc}</div>}
              </div>
            ) : (
              <div className="meta-grid">
                <div><span>Game Identity</span>{item.game}</div>
                {item.rating !== undefined && item.rating !== null && <div><span>Rating</span>{item.rating}/10</div>}
                {item.difficulty !== undefined && item.difficulty !== null && <div><span>Difficulty</span>{item.difficulty}/100</div>}
              </div>
            )}
          </div>
        )}
      </div>

      <div className="qrow-actions">
        <button className="qrow-btn" onClick={() => setOpen(!open)}>
          {open ? 'Collapse' : 'Inspect'}
        </button>
        <button className="qrow-btn approve" onClick={onApprove}>{window.ic.check}</button>
        <button className="qrow-btn reject" onClick={onReject}>{window.ic.x}</button>
      </div>
    </div>
  );
}

function ReviewQueue({ data, setData, pushLog }) {
  const [tab, setTab] = React.useState('comments'); // comments | submissions
  const [page, setPage] = React.useState(1);
  const [sel, setSel] = React.useState(new Set());
  const [confirm, setConfirm] = React.useState(null);

  React.useEffect(() => { setPage(1); setSel(new Set()); }, [tab]);

  const items = tab === 'comments' ? data.comments : data.submissions;
  const itemsPerPage = 8;
  const pages = Math.max(1, Math.ceil(items.length / itemsPerPage));
  const pageItems = items.slice((page - 1) * itemsPerPage, page * itemsPerPage);

  const toggleSel = (id) => {
    const next = new Set(sel);
    if (next.has(id)) next.delete(id); else next.add(id);
    setSel(next);
  };

  const toggleAll = () => {
    if (sel.size === pageItems.length) {
      setSel(new Set());
    } else {
      setSel(new Set(pageItems.map((x) => x.id)));
    }
  };

  const resolve = async (ids, action, reasonText = null) => {
    let successCount = 0;
    for (const id of ids) {
      const item = items.find(x => x.id === id);
      if (!item) continue;
      
      const type = item.type; // 'comment' or 'submission'
      
      // If rejecting and reason is not yet passed from confirm modal, prompt
      let finalReason = reasonText;
      if (action === 'reject' && !finalReason) {
        finalReason = prompt(`Enter rejection reason for ${item.title || item.author}'s ${type}:`);
        if (finalReason === null) return; // cancelled
        if (!finalReason.trim()) {
          alert("A rejection reason is required.");
          return;
        }
      }

      try {
        const res = await fetch('/api/queue', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ type, action, id, reason: finalReason ? finalReason.trim() : null })
        });
        
        if (!res.ok) {
          const errData = await res.json().catch(() => ({}));
          throw new Error(errData.error || `HTTP ${res.status}`);
        }
        
        successCount++;
        // Remove locally
        setData(d => {
          const nextComments = d.comments.filter(c => c.id !== id);
          const nextSubmissions = d.submissions.filter(s => s.id !== id);
          return { ...d, comments: nextComments, submissions: nextSubmissions };
        });
        
        pushLog(action === 'approve' ? 'approve' : 'reject', `${type}:${item.title || item.author}`);
      } catch (err) {
        alert(`Failed to update ${type} #${id}: ${err.message}`);
      }
    }
    
    if (successCount > 0) {
      window.pushToast(
        `${tab === 'comments' ? 'Comments' : 'Submissions'} resolved`,
        `${successCount} item(s) marked as ${action}d.`,
        action === 'approve' ? 'success' : 'error'
      );
      setSel(new Set());
    }
  };

  return (
    <>
      <div className="admin-h">
        <h2>Moderation Queue</h2>
        <span className="sub">{items.length} pending moderation actions</span>
      </div>

      <div className="q-tabs">
        <button className={tab === 'comments' ? 'on' : ''} onClick={() => setTab('comments')}>
          Comments ({data.comments.length})
        </button>
        <button className={tab === 'submissions' ? 'on' : ''} onClick={() => setTab('submissions')}>
          Submissions ({data.submissions.length})
        </button>
      </div>

      <div className="q-bulk-row">
        <button className={'qcheck' + (sel.size === pageItems.length && pageItems.length > 0 ? ' on' : '')} onClick={toggleAll} title="Select All" />
        <span className="bulk-lbl">{sel.size} selected</span>
        <button className="bulk-btn approve" disabled={sel.size === 0} onClick={() => resolve(Array.from(sel), 'approve')}>Approve</button>
        <button className="bulk-btn reject" disabled={sel.size === 0} onClick={() => setConfirm({
          tone: 'danger', icon: window.ic.x, title: 'Reject ' + sel.size + ' items?',
          body: <>This will reject all selected {tab} in bulk. This action is logged.</>,
          confirmLabel: 'Reject all', onConfirm: () => { resolve(Array.from(sel), 'reject'); setConfirm(null); }
        })}>Reject</button>
      </div>

      {pageItems.length === 0 ? (
        <window.EmptyState icon={window.ic.checkCircle} title="Queue clear" sub={'No pending ' + tab + ' to review. Nicely done.'} />
      ) : (
        <>
          {pageItems.map((item) => (
            <QueueRow key={item.id} item={item} selected={sel.has(item.id)}
              onToggleSel={() => toggleSel(item.id)}
              onApprove={() => resolve([item.id], 'approve')}
              onReject={() => setConfirm({
                tone: 'danger', icon: window.ic.x, title: 'Reject this ' + item.type + '?',
                body: <>This will remove <b>{item.title || item.author + "'s comment"}</b> from the queue and notify the author. This action is logged.</>,
                confirmLabel: 'Reject', onConfirm: () => { resolve([item.id], 'reject'); setConfirm(null); },
              })} />
          ))}
          {pages > 1 && (
            <div className="pager">
              <button disabled={page === 1} onClick={() => setPage((p) => p - 1)}>{window.ic.arrow_l}</button>
              {Array.from({ length: pages }).map((_, i) => (
                <button key={i} className={page === i + 1 ? 'on' : ''} onClick={() => setPage(i + 1)}>{i + 1}</button>
              ))}
              <button disabled={page === pages} onClick={() => setPage((p) => p + 1)}>{window.ic.arrow_r}</button>
            </div>
          )}
        </>
      )}

      {confirm && <ConfirmDialog {...confirm} onCancel={() => setConfirm(null)} />}
    </>
  );
}

// ── User Management ─────────────────────────────────────────────────────────
function UserManagement({ data, setData, pushLog }) {
  const [query, setQuery] = React.useState('');
  const [user, setUser] = React.useState(null);
  const [confirm, setConfirm] = React.useState(null);

  const search = (q) => {
    const name = q.trim().toLowerCase();
    if (!name) { setUser(null); return; }
    
    // Find matching user in the sync list
    const matched = data.users.find(u => u.nick.toLowerCase().includes(name) || u.id.toLowerCase() === name);
    if (matched) {
      setUser(matched);
    } else {
      alert("No matching user found in database.");
    }
  };

  const act = (label, tone, icon, body, actionName, newStatus) => setConfirm({
    tone, icon, title: label + ' ' + user.nick + '?', body, confirmLabel: label,
    onConfirm: async () => {
      let reason = null;
      if (actionName === 'ban' || actionName === 'mute' || actionName === 'purge') {
        reason = prompt(`Enter moderation reason for this user ${actionName}:`);
        if (reason === null) {
          setConfirm(null);
          return;
        }
        if (!reason.trim()) {
          alert("A moderation reason is required.");
          setConfirm(null);
          return;
        }
      }

      try {
        const res = await fetch('/api/users', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ action: actionName, userId: user.id, reason })
        });
        
        if (!res.ok) {
          const errData = await res.json().catch(() => ({}));
          throw new Error(errData.error || `HTTP ${res.status}`);
        }
        
        if (actionName === 'purge') {
          pushLog('purge', `user:${user.nick} (bulk)`);
          window.pushToast('Bulk reject', 'All pending content from ' + user.nick + ' rejected', 'error');
        } else {
          setUser((u) => ({ ...u, status: newStatus }));
          setData((d) => ({
            ...d,
            users: d.users.map(u => u.id === user.id ? { ...u, status: newStatus } : u)
          }));
          pushLog(actionName, 'user:' + user.nick);
          window.pushToast(label + ' applied', user.nick + ' is now ' + newStatus, tone === 'warn' ? 'warn' : (newStatus === 'normal' ? 'success' : 'error'));
        }
      } catch (err) {
        alert(`User action failed: ${err.message}`);
      } finally {
        setConfirm(null);
      }
    },
  });

  const statusBadge = (s) => {
    const map = { normal: ['approved', 'Normal'], muted: ['pending', 'Muted'], banned: ['rejected', 'Banned'] };
    const [cls, lbl] = map[s] || map.normal;
    return <span className={'st-badge ' + cls}><span className="d" />{lbl}</span>;
  };

  return (
    <>
      <div className="admin-h">
        <h2>User Management</h2>
        <span className="sub">Search, moderate, and review per-user activity</span>
      </div>

      <div className="user-search">
        {window.ic.search}
        <input value={query} placeholder="Search users by nickname…"
               onChange={(e) => setQuery(e.target.value)}
               onKeyDown={(e) => e.key === 'Enter' && search(query)} />
      </div>

      {!user ? (
        <window.EmptyState icon={window.ic2.users} title="No user selected" sub="Search a nickname above to locate a user and view their moderation panel." />
      ) : (
        <div className="user-card">
          <div className="uc-head">
            <window.Avatar identity={{ color: user.color, initial: user.nick[0] }} size={40} />
            <div style={{ flex: 1 }}>
              <div className="nick">{user.nick}</div>
              <div className="meta" style={{ color: 'var(--text-soft)' }}>joined {user.joined} · {user.subs.length} submissions · {user.cmts.length} comments</div>
            </div>
            {statusBadge(user.status)}
          </div>

          <div className="uc-actions">
            <button className="uc-btn warn" onClick={() => act('Mute', 'warn', window.ic2.mute, <>Muted users cannot post comments. You can unmute at any time.</>, 'mute', 'muted')} disabled={user.status === 'muted'}>{window.ic2.mute} Mute</button>
            <button className="uc-btn danger" onClick={() => act('Ban', 'danger', window.ic2.ban, <>Banning <b>{user.nick}</b> revokes account access and hides all their content. This is severe — confirm carefully.</>, 'ban', 'banned')} disabled={user.status === 'banned'}>{window.ic2.ban} Ban</button>
            <button className="uc-btn good" onClick={() => act('Unban', 'warn', window.ic.checkCircle, <>Restore <b>{user.nick}</b> to normal standing. Their content becomes visible again.</>, 'unban', 'normal')} disabled={user.status === 'normal'}>{window.ic.checkCircle} Unban</button>
            <button className="uc-btn danger" onClick={() => setConfirm({
              tone: 'danger', icon: window.ic.x, title: 'Reject all pending by ' + user.nick + '?',
              body: <>This rejects <b>every pending submission and comment</b> from this user in one action. Cannot be undone.</>,
              confirmLabel: 'Reject all', onConfirm: () => act('Purge', 'danger', window.ic.warning, <></>, 'purge', '').onConfirm()
            })}>{window.ic.warning} Reject All Pending</button>
          </div>

          <div className="uc-sub">
            <div className="uc-sub-label">Submissions ({user.subs.length})</div>
            {user.subs.length === 0 ? (
              <div style={{ fontSize: 12, color: 'var(--muted)', padding: '4px 0' }}>No submissions found for this user.</div>
            ) : (
              user.subs.map((s, i) => (
                <div className="mini-rec" key={i}><span className="mr-type">Game</span><span className="mr-text">{s}</span><span className="st-badge neutral"><span className="d" />Live</span></div>
              ))
            )}
            
            <div className="uc-sub-label" style={{ marginTop: 14 }}>Comments ({user.cmts.length})</div>
            {user.cmts.length === 0 ? (
              <div style={{ fontSize: 12, color: 'var(--muted)', padding: '4px 0' }}>No comments found for this user.</div>
            ) : (
              user.cmts.map((c, i) => (
                <div className="mini-rec" key={i}><span className="mr-type">Comment</span><span className="mr-text">“{c}”</span></div>
              ))
            )}
          </div>
        </div>
      )}

      {confirm && <ConfirmDialog {...confirm} onCancel={() => setConfirm(null)} />}
    </>
  );
}

// ── Audit Logs ──────────────────────────────────────────────────────────────
function AuditLogs({ data }) {
  const [target, setTarget] = React.useState('');
  const rows = data.logs.filter((l) =>
    !target || l.target.toLowerCase().includes(target.toLowerCase()));

  return (
    <>
      <div className="admin-h">
        <h2>Audit Logs</h2>
        <span className="sub">{data.logs.length} entries · read-only</span>
      </div>
      <div className="log-filter-row">
        <input className="log-filter" value={target} placeholder="Filter by target…" onChange={(e) => setTarget(e.target.value)} />
      </div>
      {rows.length === 0 ? (
        <window.EmptyState title="No matching entries" sub="Adjust the target filter." />
      ) : (
        <table className="log-table">
          <thead><tr><th>Time</th><th>Action</th><th>Target</th></tr></thead>
          <tbody>
            {rows.map((l) => (
              <tr key={l.id}>
                <td className="lt-time">{l.time}</td>
                <td><span className={'log-action ' + l.action}>{l.action}</span></td>
                <td className="mono" style={{ fontSize: 12, color: 'var(--text-soft)' }}>{l.target}</td>
              </tr>
            ))}
          </tbody>
        </table>
      )}
    </>
  );
}

// ── Overview ────────────────────────────────────────────────────────────────
function Overview({ data, onJump }) {
  const cards = [
    { k: 'comments', n: data.comments.length, label: 'Pending comments', color: 'oklch(0.58 0.15 200)', icon: window.ic.mail, jump: 'queue' },
    { k: 'submissions', n: data.submissions.length, label: 'Pending submissions', color: 'oklch(0.56 0.16 265)', icon: window.ic2.upload, jump: 'queue' },
    { k: 'today', n: data.logs.length, label: 'Recorded admin actions', color: 'oklch(0.60 0.14 145)', icon: window.ic2.gauge, jump: 'logs' },
    { k: 'flagged', n: data.users.filter(u => u.status !== 'normal').length, label: 'Muted/Banned users', color: 'oklch(0.62 0.16 25)', icon: window.ic2.users, jump: 'users' },
  ];
  return (
    <>
      <div className="admin-h"><h2>Overview</h2><span className="sub">Moderation at a glance</span></div>
      <div className="ov-grid">
        {cards.map((c) => (
          <button className="ov-card" key={c.k} onClick={() => onJump(c.jump)}>
            <div className="ov-ic" style={{ background: 'color-mix(in oklab, ' + c.color + ' 15%, transparent)', color: c.color }}>
              {c.icon}
            </div>
            <div className="ov-num" style={{ color: c.color }}>{c.n}</div>
            <div className="ov-lbl">{c.label} {window.ic.arrow_r}</div>
          </button>
        ))}
      </div>
    </>
  );
}

// ── Admin shell ─────────────────────────────────────────────────────────────
function AdminView({ identity, onExit }) {
  const [section, setSection] = React.useState('overview');
  const [loading, setLoading] = React.useState(true);
  const [data, setData] = React.useState({ comments: [], submissions: [], logs: [], users: [] });
  const [menuOpen, setMenuOpen] = React.useState(false);

  const pushLog = (action, target) => {
    setData((d) => ({
      ...d,
      logs: [{
        id: 'lg_' + Date.now(),
        time: new Date().toLocaleString(),
        op: identity.nick,
        opColor: identity.color,
        action,
        target
      }, ...d.logs],
    }));
  };

  const fetchData = React.useCallback(async () => {
    setLoading(true);
    try {
      const qRes = await fetch('/api/queue');
      const qData = await qRes.json();

      const uRes = await fetch('/api/users');
      const uData = await uRes.json();

      const lRes = await fetch('/api/logs');
      const lData = await lRes.json();

      const commentsMapped = (qData.comments || []).map(c => ({
        id: c.id,
        type: 'comment',
        author: c.user,
        color: stableAvatarColor(c.user),
        game: `Game ID: ${c.game_id}`,
        text: c.content,
        time: c.date,
        user_id: c.user_id,
        rating: c.rating,
        difficulty: c.difficulty
      }));

      const submissionsMapped = (qData.submissions || []).map(s => ({
        id: s.id,
        type: 'submission',
        author: s.author_name,
        color: stableAvatarColor(s.author_name),
        title: s.title,
        creator: s.author_name,
        url: s.external_url,
        tags: s.tags,
        desc: s.description || "",
        time: new Date(s.created_at).toLocaleString(),
        submitter_id: s.submitter_id
      }));

      const logsMapped = (lData.logs || []).map(l => ({
        id: 'lg_' + l.id,
        time: new Date(l.created_at).toLocaleString(),
        op: l.actor_id.split('@')[0],
        opColor: stableAvatarColor(l.actor_id),
        action: l.action.replace('_comment', '').replace('_submission', '').replace('_user', '').replace('purge_user_pending', 'purge'),
        target: `${l.target_type}:${l.target_id}`,
        meta: l.meta
      }));

      // Gather comments and submissions by user dynamically to populate uc-sub lists
      const usersMapped = (uData.users || []).map(u => {
        const userComments = commentsMapped.filter(c => c.user_id === u.id).map(c => c.text);
        const userSubmissions = submissionsMapped.filter(s => s.submitter_id === u.id).map(s => s.title);
        return {
          id: u.id,
          nick: u.display_name || u.email.split('@')[0] || "User",
          color: stableAvatarColor(u.display_name || u.id),
          status: u.status === 'muted' ? 'muted' : (u.status === 'banned' ? 'banned' : 'normal'),
          joined: new Date(u.created_at).toLocaleDateString(),
          email: u.email,
          subs: userSubmissions,
          cmts: userComments
        };
      });

      setData({
        comments: commentsMapped,
        submissions: submissionsMapped,
        logs: logsMapped,
        users: usersMapped
      });
    } catch (e) {
      console.error("Failed to load admin data", e);
    } finally {
      setLoading(false);
    }
  }, []);

  React.useEffect(() => {
    fetchData();
  }, [fetchData]);

  const NAV = [
    { k: 'queue', label: 'Queue', icon: window.ic2.inbox, count: data.comments.length + data.submissions.length },
    { k: 'users', label: 'Users', icon: window.ic2.users },
    { k: 'logs', label: 'Logs', icon: window.ic2.list2 },
    { k: 'overview', label: 'Overview', icon: window.ic2.gauge },
  ];

  return (
    <div className="admin">
      <div className="admin-top">
        <div className="a-brand">
          <div className="a-logo">{window.ic2.shield}</div>
          <span className="a-title">Archive Admin</span>
          <span className="a-tag">Moderation</span>
        </div>
        <span className="a-access" title="This deployment is protected by Cloudflare Zero Trust">{window.ic2.lock} Zero Trust</span>
        <button className="a-back" onClick={onExit}>{window.ic.arrow_l} Back to site</button>

      </div>

      <nav className="admin-nav">
        <div className="an-label">Moderation</div>
        {NAV.map((n) => (
          <button key={n.k} className={'an-item' + (section === n.k ? ' on' : '')} onClick={() => setSection(n.k)}>
            {n.icon}<span>{n.label}</span>
            {n.count != null && n.count > 0 && <span className="an-ct">{n.count}</span>}
          </button>
        ))}
      </nav>

      <div className="admin-body">
        {loading ? (
          <div style={{ padding: 40, textAlign: 'center', color: 'var(--text-soft)' }}>
            Loading database states from Cloudflare D1...
          </div>
        ) : (
          <>
            {section === 'queue' && <ReviewQueue data={data} setData={setData} pushLog={pushLog} />}
            {section === 'users' && <UserManagement data={data} setData={setData} pushLog={pushLog} />}
            {section === 'logs' && <AuditLogs data={data} />}
            {section === 'overview' && <Overview data={data} onJump={setSection} />}
          </>
        )}
      </div>
    </div>
  );
}

Object.assign(window, { stableAvatarColor, ConfirmDialog, ReviewQueue, UserManagement, AuditLogs, Overview, AdminView });
