// Admin — owner-only access control.
//
// Lets the owner (SITE_OWNER_EMAIL) grant any Supabase-auth email access to
// specific private pages, at a chosen level (full / read-only), and toggle them
// on/off — all persisted to the `site_access` table, which also drives the
// Row-Level-Security policies server-side (see the site_access migration). No
// SQL editor required.
//
// The route (#admin) and this component are gated to the owner in index.html.

const ADMIN_PAGES = [
  { id: 'objects', label: 'Objects' },
  { id: 'ideas',   label: 'Ideas' },
  { id: 'places',  label: 'Places' },
  { id: 'forme',   label: 'For Me' },
];

const LEVELS = [
  { id: 'full', label: 'Full access', hint: 'view + add / edit / delete' },
  { id: 'read', label: 'Read-only',   hint: 'view only — every write blocked' },
];

const blankGrant = () => ({ email: '', pages: ['objects', 'ideas', 'places'], access_level: 'read', active: true, note: '' });

const emailValid = (e) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test((e || '').trim());

const lbl = { fontFamily: fontMono, fontSize: '10px', letterSpacing: '0.12em', color: tokens.inkMute, display: 'block', marginBottom: '6px' };
const inp = { width: '100%', padding: '9px 11px', border: `1px solid ${tokens.inkLine}`, background: '#FDFBF7', fontFamily: 'inherit', fontSize: '13px', color: tokens.ink, boxSizing: 'border-box', outline: 'none' };

// Page-checkbox + level + active editor shared by the add form and each row.
const GrantEditor = ({ value, onChange }) => {
  const togglePage = (id) => {
    const has = value.pages.includes(id);
    onChange({ ...value, pages: has ? value.pages.filter((p) => p !== id) : [...value.pages, id] });
  };
  return (
    <div>
      <div style={{ marginBottom: '12px' }}>
        <label style={lbl}>PAGES VISIBLE</label>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px' }}>
          {ADMIN_PAGES.map((p) => {
            const on = value.pages.includes(p.id);
            return (
              <span key={p.id} onClick={() => togglePage(p.id)}
                style={{ fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.04em', padding: '6px 12px', cursor: 'pointer',
                  border: `1px solid ${on ? tokens.ink : tokens.inkLine}`, background: on ? tokens.ink : 'none', color: on ? tokens.paper : tokens.inkMute }}>
                {on ? '✓ ' : ''}{p.label}
              </span>
            );
          })}
        </div>
      </div>
      <div style={{ display: 'flex', gap: '20px', flexWrap: 'wrap', alignItems: 'flex-start' }}>
        <div>
          <label style={lbl}>ACCESS LEVEL</label>
          <div style={{ display: 'flex', gap: '8px' }}>
            {LEVELS.map((l) => {
              const on = value.access_level === l.id;
              return (
                <span key={l.id} onClick={() => onChange({ ...value, access_level: l.id })} title={l.hint}
                  style={{ fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.04em', padding: '6px 12px', cursor: 'pointer',
                    border: `1px solid ${on ? (l.id === 'full' ? tokens.green : tokens.ochre) : tokens.inkLine}`,
                    background: on ? (l.id === 'full' ? tokens.green : tokens.ochre) : 'none', color: on ? tokens.paper : tokens.inkMute }}>
                  {l.label}
                </span>
              );
            })}
          </div>
        </div>
        <div>
          <label style={lbl}>STATUS</label>
          <span onClick={() => onChange({ ...value, active: !value.active })}
            style={{ fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.04em', padding: '6px 14px', cursor: 'pointer', display: 'inline-block',
              border: `1px solid ${value.active ? tokens.green : tokens.inkLine}`, background: value.active ? tokens.green : 'none', color: value.active ? tokens.paper : tokens.inkMute }}>
            {value.active ? '● ACTIVE' : '○ DISABLED'}
          </span>
        </div>
      </div>
    </div>
  );
};

const AddGrantForm = ({ onAdd, onCancel }) => {
  const [g, setG] = React.useState(blankGrant());
  const [saving, setSaving] = React.useState(false);
  const [err, setErr] = React.useState('');
  const submit = async (e) => {
    e.preventDefault();
    if (!emailValid(g.email)) { setErr('Enter a valid email address.'); return; }
    if (!g.pages.length) { setErr('Grant at least one page.'); return; }
    setSaving(true); setErr('');
    const error = await onAdd({ ...g, email: g.email.trim().toLowerCase() });
    if (error) { setErr(error.message || 'Failed to save.'); setSaving(false); }
  };
  return (
    <form onSubmit={submit} style={{ border: `1px solid ${tokens.inkLine}`, background: tokens.paper, padding: '20px', marginBottom: '24px' }}>
      <div style={{ fontFamily: fontMono, fontSize: '10px', letterSpacing: '0.16em', color: tokens.inkMute, marginBottom: '16px' }}>GRANT ACCESS</div>
      <div style={{ marginBottom: '14px' }}>
        <label style={lbl}>EMAIL <span style={{ opacity: 0.6 }}>(must be a Supabase Auth user)</span></label>
        <input style={inp} value={g.email} onChange={(e) => setG({ ...g, email: e.target.value })} placeholder="person@firm.com" autoFocus />
      </div>
      <div style={{ marginBottom: '14px' }}>
        <label style={lbl}>NOTE <span style={{ opacity: 0.6 }}>(optional — who they are)</span></label>
        <input style={inp} value={g.note} onChange={(e) => setG({ ...g, note: e.target.value })} placeholder="e.g. LP at Acme · diligence access to Ideas" />
      </div>
      <GrantEditor value={g} onChange={setG} />
      {err && <div style={{ color: tokens.red, fontSize: '12px', margin: '12px 0' }}>{err}</div>}
      <div style={{ display: 'flex', gap: '8px', marginTop: '18px' }}>
        <button type="submit" disabled={saving} style={{ fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.1em', padding: '10px 22px', background: tokens.ink, color: tokens.paper, border: 'none', cursor: saving ? 'default' : 'pointer', opacity: saving ? 0.6 : 1 }}>
          {saving ? 'SAVING…' : 'GRANT ACCESS'}
        </button>
        <button type="button" onClick={onCancel} style={{ fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.1em', padding: '10px 16px', background: 'none', border: `1px solid ${tokens.inkLine}`, cursor: 'pointer', color: tokens.inkMute }}>CANCEL</button>
      </div>
    </form>
  );
};

const GrantRow = ({ grant, onSave, onDelete }) => {
  const [editing, setEditing] = React.useState(false);
  const [draft, setDraft] = React.useState(grant);
  const [busy, setBusy] = React.useState(false);
  React.useEffect(() => { setDraft(grant); }, [grant]);
  const builtIn = grant.invited_by === 'system';

  const save = async () => { setBusy(true); const e = await onSave(grant.email, { pages: draft.pages, access_level: draft.access_level, active: draft.active, note: draft.note }); setBusy(false); if (!e) setEditing(false); };

  const levelColor = grant.access_level === 'full' ? tokens.green : tokens.ochre;
  return (
    <div style={{ border: `1px solid ${tokens.inkLine}`, background: tokens.paper, padding: '16px 18px', marginBottom: '10px', opacity: grant.active ? 1 : 0.6 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: '12px', flexWrap: 'wrap' }}>
        <div style={{ minWidth: 0 }}>
          <span style={{ fontSize: '15px', fontWeight: 600 }}>{grant.email}</span>
          {builtIn && <span style={{ fontFamily: fontMono, fontSize: '8.5px', letterSpacing: '0.1em', color: tokens.inkMute, marginLeft: '8px', padding: '2px 6px', background: tokens.bgAlt }}>BUILT-IN</span>}
          {grant.note && <div style={{ fontSize: '12.5px', color: tokens.inkSoft, marginTop: '3px' }}>{grant.note}</div>}
        </div>
        <div style={{ display: 'flex', gap: '10px', alignItems: 'center', flexShrink: 0 }}>
          <span style={{ fontFamily: fontMono, fontSize: '9px', letterSpacing: '0.08em', padding: '3px 8px', border: `1px solid ${levelColor}`, color: levelColor }}>{grant.access_level === 'full' ? 'FULL' : 'READ-ONLY'}</span>
          <span style={{ fontFamily: fontMono, fontSize: '9px', color: grant.active ? tokens.green : tokens.inkMute }}>{grant.active ? '● ACTIVE' : '○ OFF'}</span>
          <span onClick={() => setEditing((x) => !x)} style={{ fontFamily: fontMono, fontSize: '10px', letterSpacing: '0.08em', color: tokens.inkMute, cursor: 'pointer', borderBottom: `1px solid ${tokens.inkLine}` }}>{editing ? 'CLOSE' : 'EDIT'}</span>
        </div>
      </div>

      {!editing && (
        <div style={{ display: 'flex', gap: '6px', flexWrap: 'wrap', marginTop: '10px' }}>
          {ADMIN_PAGES.filter((p) => (grant.pages || []).includes(p.id)).map((p) => (
            <span key={p.id} style={{ fontFamily: fontMono, fontSize: '10px', padding: '3px 9px', border: `1px solid ${tokens.inkLine}`, color: tokens.inkSoft, background: tokens.bg }}>{p.label}</span>
          ))}
          {(grant.pages || []).length === 0 && <span style={{ fontFamily: fontMono, fontSize: '10px', color: tokens.inkMute }}>no pages granted</span>}
        </div>
      )}

      {editing && (
        <div style={{ marginTop: '16px', borderTop: `1px solid ${tokens.inkLineSoft}`, paddingTop: '16px' }}>
          <div style={{ marginBottom: '12px' }}>
            <label style={lbl}>NOTE</label>
            <input style={inp} value={draft.note || ''} onChange={(e) => setDraft({ ...draft, note: e.target.value })} />
          </div>
          <GrantEditor value={draft} onChange={setDraft} />
          <div style={{ display: 'flex', gap: '8px', marginTop: '16px', alignItems: 'center' }}>
            <button onClick={save} disabled={busy} style={{ fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.1em', padding: '9px 20px', background: tokens.ink, color: tokens.paper, border: 'none', cursor: busy ? 'default' : 'pointer', opacity: busy ? 0.6 : 1 }}>{busy ? 'SAVING…' : 'SAVE'}</button>
            <button onClick={() => { setDraft(grant); setEditing(false); }} style={{ fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.1em', padding: '9px 14px', background: 'none', border: `1px solid ${tokens.inkLine}`, cursor: 'pointer', color: tokens.inkMute }}>CANCEL</button>
            <span style={{ flexGrow: 1 }} />
            {!builtIn && <button onClick={() => { if (window.confirm(`Revoke all access for ${grant.email}?`)) onDelete(grant.email); }}
              style={{ fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.1em', padding: '9px 14px', background: 'none', border: `1px solid ${tokens.red}`, cursor: 'pointer', color: tokens.red }}>REVOKE</button>}
          </div>
        </div>
      )}
    </div>
  );
};

const Admin = () => {
  const isMobile = useIsMobile();
  const [grants, setGrants] = React.useState(null);
  const [showAdd, setShowAdd] = React.useState(false);
  const [err, setErr] = React.useState('');

  const load = React.useCallback(async () => {
    const client = window._supabaseClient;
    if (!client) { setGrants([]); setErr('No backend connected.'); return; }
    const { data, error } = await client.from('site_access').select('email, pages, access_level, active, note, invited_by, created_at').order('created_at', { ascending: true });
    if (error) { setGrants([]); setErr(error.message); return; }
    setGrants(data || []);
  }, []);
  React.useEffect(() => { load(); }, [load]);

  const add = async (g) => {
    const client = window._supabaseClient;
    if (!client) return { message: 'No backend connected.' };
    const { data: { user } } = await client.auth.getUser();
    const { error } = await client.from('site_access').upsert({ ...g, invited_by: user ? user.email : null }, { onConflict: 'email' });
    if (error) return error;
    setShowAdd(false);
    await load();
    return null;
  };

  const save = async (email, patch) => {
    const client = window._supabaseClient;
    if (!client) return { message: 'No backend connected.' };
    const { error } = await client.from('site_access').update(patch).eq('email', email);
    if (error) { setErr(error.message); return error; }
    await load();
    return null;
  };

  const del = async (email) => {
    const client = window._supabaseClient;
    if (!client) return;
    await client.from('site_access').delete().eq('email', email);
    await load();
  };

  return (
    <Section label="OWNER · ADMIN" title="Access control."
      sub="Grant any Supabase-auth email access to specific private pages, at full or read-only level. Changes drive both the navigation and the server-side Row-Level-Security policies.">

      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '20px', flexWrap: 'wrap', gap: '12px' }}>
        <div style={{ fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.14em', color: tokens.inkMute }}>
          AUTHORIZED EMAILS {grants ? `· ${grants.length}` : ''}
        </div>
        {!showAdd && (
          <button onClick={() => setShowAdd(true)} style={{ fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.1em', padding: '9px 18px', background: tokens.ink, color: tokens.paper, border: 'none', cursor: 'pointer' }}>+ GRANT ACCESS</button>
        )}
      </div>

      {err && <div style={{ color: tokens.red, fontSize: '12px', marginBottom: '16px', fontFamily: fontMono }}>{err}</div>}

      {showAdd && <AddGrantForm onAdd={add} onCancel={() => setShowAdd(false)} />}

      {grants === null ? (
        <div style={{ fontFamily: fontMono, fontSize: '11px', color: tokens.inkMute, letterSpacing: '0.1em' }}>LOADING…</div>
      ) : grants.length === 0 ? (
        <div style={{ fontFamily: fontMono, fontSize: '12px', color: tokens.inkMute, lineHeight: 1.7 }}>
          No grants yet. The owner (you) always has full access. Click <span style={{ color: tokens.ink }}>+ GRANT ACCESS</span> to invite someone — they must already exist as a user in Supabase Auth (Dashboard → Authentication → Add user).
        </div>
      ) : (
        <div>{grants.map((g) => <GrantRow key={g.email} grant={g} onSave={save} onDelete={del} />)}</div>
      )}

      <div style={{ marginTop: '32px', fontFamily: fontMono, fontSize: '10px', letterSpacing: '0.06em', color: tokens.inkMute, lineHeight: 1.8 }}>
        OWNER · {(window.SITE_OWNER_EMAIL || '').toUpperCase()} — ALWAYS FULL ACCESS ·
        SERVER-SIDE ENFORCEMENT VIA <span style={{ color: tokens.ink }}>site_access</span> RLS ·
        SEE <span style={{ color: tokens.ink }}>ADMIN-SETUP.md</span>
      </div>
    </Section>
  );
};

window.Admin = Admin;
