/*
 * TeamManagement.jsx — Agency Team Management panel (Gap 5)
 * v1.0.0 — 2026-06-07
 *
 * The owner-facing front door for agency multi-seat. Wraps the live API:
 *   - GET    /agency/config        — resolves the caller's agency_role + plan (gating)
 *   - GET    /agency/team          — roster (owner + dev)
 *   - POST   /auth/invite          — invite a teammate by email (owner, Agency plan)
 *   - PATCH  /agency/team/:id       — edit a member's role (owner, Agency plan)
 *   - DELETE /agency/team/:id       — revoke a member (owner)
 *
 * Access model (_AGENCY-MEMBERSHIP-AND-ROLES-SPEC.md §3):
 *   owner  → full CRUD
 *   dev    → read-only roster
 *   member → no roster access (server 403s GET /agency/team); shown an info notice
 *   below-Agency owner → roster + locked-CTA upsell (writes 402/403 → upgrade prompt),
 *                        per the locked "Locked-CTA = upsell, not a wall" decision.
 *
 * Role changes apply on the member's NEXT sign-in (the agency_role claim is resolved
 * at login), surfaced in the UI after an edit. Last-owner guard is enforced server-side
 * (409 LAST_OWNER) and surfaced as a toast.
 *
 * Design Standards 7-point compliance: self-contained .jsx, window namespace, IIFE,
 * theme vars only (var(--*)), mobile responsive (480/768), WCAG 2.1 AA, generic
 * placeholders only (example.com).
 *
 * Auth: window.WPSB.getToken() — defined in index.html bootstrap.
 */
(function () {
  'use strict';

  const { useState, useEffect, useCallback } = React;
  const tx = (k, v, f) => (window.WPSB && window.WPSB.t) ? window.WPSB.t(k, v, f) : (f != null ? f : k);
  const RAILWAY = (window.WPSB && window.WPSB.RAILWAY) || 'https://wpsitebeam-railway-api-production.up.railway.app';

  const ROLES = ['owner', 'dev', 'member'];
  const ROLE_LABEL = { owner: tx('page.team.role_owner', null, 'Owner'), dev: tx('page.team.role_dev', null, 'Developer'), member: tx('page.team.role_member', null, 'Member') };
  const ROLE_DESC = {
    owner:  tx('page.team.role_owner_desc', null, 'Full access — runs every tool, manages the team, billing, and branding.'),
    dev:    tx('page.team.role_dev_desc', null, 'Full operator — runs every tool and sees the roster, but touches no billing, branding, or membership.'),
    member: tx('page.team.role_member_desc', null, 'Operator — runs tools and their own dashboards. No roster access.'),
  };
  // agency-tier plans (stable across the two PLAN_ORDER variants — see spec Finding F1).
  const AGENCY_PLUS = ['agency', 'enterprise', 'internal'];

  /* ── network ─────────────────────────────────────────────────────── */
  function authHeaders() {
    const token = (window.WPSB && window.WPSB.getToken && window.WPSB.getToken()) || '';
    return { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' };
  }
  async function api(method, path, body) {
    const r = await fetch(RAILWAY + path, {
      method,
      headers: authHeaders(),
      body: body ? JSON.stringify(body) : undefined,
    });
    const data = await r.json().catch(() => ({}));
    if (!r.ok) {
      const e = new Error(data.error || ('HTTP ' + r.status));
      e.code = data.code; e.status = r.status;
      throw e;
    }
    return data;
  }

  /* ── helpers ─────────────────────────────────────────────────────── */
  function fmtDate(iso) {
    if (!iso) return '\u2014';
    try { return new Date(iso).toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' }); }
    catch { return '\u2014'; }
  }
  function initials(member) {
    const src = (member.name && member.name.trim()) || member.email || '?';
    const parts = src.replace(/@.*$/, '').split(/[\s._-]+/).filter(Boolean);
    const two = (parts[0] || '?')[0] + (parts.length > 1 ? parts[parts.length - 1][0] : '');
    return two.toUpperCase();
  }
  function roleColor(role) {
    if (role === 'owner') return 'var(--orange)';
    if (role === 'dev')   return 'var(--beam)';
    return 'var(--muted)';
  }

  /* ── modal shell — thin adapter over THE ONE shared dialog (L12) ────
     This fork donated the {title, children, onClose} API shape to the
     shared primitive (design/ActivationPrimitives.jsx §4e), so the
     adapter is a pass-through and the four call sites below are
     untouched (R-SHAREDCORE: thin per-caller adapter, never a second
     implementation). The fork's card-level `gap` moves onto the body,
     because the shared primitive now owns the header row.
     ActivationPrimitives.jsx loads at index.html:645, this file at :762. */
  const SharedModal = window.WPSB.Activation.Modal;

  function Modal({ title, children, onClose }) {
    return React.createElement(SharedModal, { title, onClose, width: 440 },
      React.createElement('div', {
        style: { display: 'flex', flexDirection: 'column', gap: 'var(--sp-4)' },
      }, children));
  }

  const inputStyle = {
    padding: '10px 12px', borderRadius: 'var(--r-sm)', border: '1px solid var(--border)',
    background: 'var(--surface-2)', color: 'var(--text)', fontSize: '.9rem', width: '100%',
    boxSizing: 'border-box', fontFamily: 'var(--font-body)',
  };
  const labelStyle = { fontSize: '.78rem', color: 'var(--dim)', fontWeight: 'var(--fw-label)', marginBottom: 4, display: 'block' };

  /* ── invite modal ────────────────────────────────────────────────── */
  function InviteModal({ onClose, onDone }) {
    const [email, setEmail] = useState('');
    const [name, setName]   = useState('');
    const [role, setRole]   = useState('member');
    const [busy, setBusy]   = useState(false);
    const [err, setErr]     = useState('');

    async function submit() {
      setErr('');
      const e = email.trim();
      if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(e)) return setErr(tx('page.team.invalid_email', null, 'Enter a valid email address.'));
      setBusy(true);
      try {
        await api('POST', '/auth/invite', { email: e, name: name.trim() || null, agency_role: role });
        onDone(tx('page.team.invite_sent', { email: e }, 'Invitation sent to {email}.'));
      } catch (ex) {
        setBusy(false);
        if (ex.code === 'PLAN_UPGRADE_REQUIRED' || ex.status === 402) {
          return setErr(tx('page.team.invite_requires_agency_upgrade', null, 'Inviting teammates requires the Agency plan. Upgrade to add seats.'));
        }
        setErr(ex.message || tx('page.team.invite_failed', null, 'Could not send the invitation.'));
      }
    }

    return React.createElement(Modal, { title: tx('page.team.invite_title', null, 'Invite a teammate'), onClose },
      React.createElement('div', null,
        React.createElement('label', { style: labelStyle, htmlFor: 'tm-inv-email' }, tx('page.team.field_email', null, 'Email')),
        React.createElement('input', {
          id: 'tm-inv-email', type: 'email', value: email, autoFocus: true,
          placeholder: tx('page.team.email_placeholder', null, 'teammate@example.com'), autoComplete: 'off',
          onChange: (e) => setEmail(e.target.value),
          onKeyDown: (e) => { if (e.key === 'Enter') submit(); },
          style: inputStyle,
        })
      ),
      React.createElement('div', null,
        React.createElement('label', { style: labelStyle, htmlFor: 'tm-inv-name' }, tx('page.team.field_name', null, 'Name '), React.createElement('span', { style: { color: 'var(--muted)' } }, tx('page.team.optional', null, '(optional)'))),
        React.createElement('input', {
          id: 'tm-inv-name', type: 'text', value: name, placeholder: tx('page.team.name_placeholder', null, 'Jane Doe'),
          onChange: (e) => setName(e.target.value),
          onKeyDown: (e) => { if (e.key === 'Enter') submit(); },
          style: inputStyle,
        })
      ),
      React.createElement('div', null,
        React.createElement('label', { style: labelStyle, htmlFor: 'tm-inv-role' }, tx('page.team.field_role', null, 'Role')),
        React.createElement('select', {
          id: 'tm-inv-role', value: role, onChange: (e) => setRole(e.target.value), style: inputStyle,
        }, ROLES.filter(r => r !== 'owner').map(r => React.createElement('option', { key: r, value: r }, ROLE_LABEL[r]))),
        React.createElement('div', { style: { fontSize: '.76rem', color: 'var(--muted)', marginTop: 6, lineHeight: 1.45 } }, ROLE_DESC[role])
      ),
      err && React.createElement('div', { role: 'alert', style: { fontSize: '.8rem', color: 'var(--red)' } }, err),
      React.createElement('div', { style: { display: 'flex', gap: 10, justifyContent: 'flex-end', marginTop: 4 } },
        React.createElement('button', { type: 'button', className: 'btn btn-ghost', onClick: onClose, disabled: busy }, tx('page.team.cancel', null, 'Cancel')),
        React.createElement('button', { type: 'button', className: 'btn btn-primary', onClick: submit, disabled: busy }, busy ? tx('page.team.sending', null, 'Sending\u2026') : tx('page.team.send_invite', null, 'Send invite'))
      )
    );
  }

  /* ── role-edit modal ─────────────────────────────────────────────── */
  function RoleModal({ member, onClose, onDone }) {
    const [role, setRole] = useState(member.agency_role);
    const [busy, setBusy] = useState(false);
    const [err, setErr]   = useState('');

    async function submit() {
      setErr('');
      if (role === member.agency_role) return onClose();
      setBusy(true);
      try {
        const res = await api('PATCH', '/agency/team/' + member.id, { agency_role: role });
        onDone(res.note || tx('page.team.role_updated', null, 'Role updated.'));
      } catch (ex) {
        setBusy(false);
        if (ex.code === 'LAST_OWNER') return setErr(tx('page.team.last_owner_demote', null, 'You can\u2019t demote the only owner. Promote another member to owner first.'));
        if (ex.code === 'PLAN_UPGRADE_REQUIRED' || ex.status === 402) return setErr(tx('page.team.edit_requires_agency', null, 'Editing roles requires the Agency plan.'));
        setErr(ex.message || tx('page.team.role_update_failed', null, 'Could not update the role.'));
      }
    }

    return React.createElement(Modal, { title: tx('page.team.edit_role', null, 'Edit role'), onClose },
      React.createElement('div', { style: { fontSize: '.86rem', color: 'var(--text-2)' } },
        (member.name || member.email)
      ),
      React.createElement('div', null,
        React.createElement('label', { style: labelStyle, htmlFor: 'tm-role' }, tx('page.team.field_role', null, 'Role')),
        React.createElement('select', {
          id: 'tm-role', value: role, autoFocus: true, onChange: (e) => setRole(e.target.value), style: inputStyle,
        }, ROLES.map(r => React.createElement('option', { key: r, value: r }, ROLE_LABEL[r]))),
        React.createElement('div', { style: { fontSize: '.76rem', color: 'var(--muted)', marginTop: 6, lineHeight: 1.45 } }, ROLE_DESC[role])
      ),
      React.createElement('div', { style: { fontSize: '.74rem', color: 'var(--muted)', lineHeight: 1.45 } },
        tx('page.team.role_change_next_signin', null, 'Role changes apply on the member\u2019s next sign-in.')
      ),
      err && React.createElement('div', { role: 'alert', style: { fontSize: '.8rem', color: 'var(--red)' } }, err),
      React.createElement('div', { style: { display: 'flex', gap: 10, justifyContent: 'flex-end', marginTop: 4 } },
        React.createElement('button', { type: 'button', className: 'btn btn-ghost', onClick: onClose, disabled: busy }, tx('page.team.cancel', null, 'Cancel')),
        React.createElement('button', { type: 'button', className: 'btn btn-primary', onClick: submit, disabled: busy }, busy ? tx('common.saving', null, 'Saving\u2026') : tx('common.save', null, 'Save'))
      )
    );
  }

  /* ── revoke confirm ──────────────────────────────────────────────── */
  function RevokeModal({ member, onClose, onDone }) {
    const [busy, setBusy] = useState(false);
    const [err, setErr]   = useState('');
    async function go() {
      setBusy(true); setErr('');
      try {
        await api('DELETE', '/agency/team/' + member.id);
        onDone(tx('page.team.member_removed', { name: (member.name || member.email) }, 'Removed {name} from the team.'));
      } catch (ex) {
        setBusy(false);
        if (ex.code === 'LAST_OWNER') return setErr(tx('page.team.last_owner_remove', null, 'You can\u2019t remove the only owner.'));
        setErr(ex.message || tx('page.team.remove_failed', null, 'Could not remove the member.'));
      }
    }
    return React.createElement(Modal, { title: tx('page.team.revoke_title', null, 'Remove team member'), onClose },
      React.createElement('div', { style: { fontSize: '.88rem', color: 'var(--text-2)', lineHeight: 1.5 } },
        tx('page.team.revoke_prefix', null, 'Remove '), React.createElement('strong', { style: { color: 'var(--text)' } }, member.name || member.email),
        tx('page.team.revoke_suffix', null, ' from your team? They lose access on their next sign-in. Their work and any client data stay with the account.')
      ),
      err && React.createElement('div', { role: 'alert', style: { fontSize: '.8rem', color: 'var(--red)' } }, err),
      React.createElement('div', { style: { display: 'flex', gap: 10, justifyContent: 'flex-end', marginTop: 4 } },
        React.createElement('button', { type: 'button', className: 'btn btn-ghost', onClick: onClose, disabled: busy }, tx('page.team.cancel', null, 'Cancel')),
        React.createElement('button', { type: 'button', className: 'btn btn-danger', onClick: go, disabled: busy }, busy ? tx('page.team.removing', null, 'Removing\u2026') : tx('page.team.remove', null, 'Remove'))
      )
    );
  }

  /* ── member card ─────────────────────────────────────────────────── */
  function MemberCard({ member, isMe, canManage, onEdit, onRevoke }) {
    const pending = !member.user_id;
    return React.createElement('div', {
      style: {
        display: 'flex', alignItems: 'center', gap: 'var(--sp-3)',
        padding: 'var(--sp-3) var(--sp-4)', border: '1px solid var(--border)',
        borderRadius: 'var(--r)', background: 'var(--surface)', flexWrap: 'wrap',
      },
    },
      React.createElement('div', {
        'aria-hidden': 'true',
        style: {
          width: 38, height: 38, borderRadius: '50%', flexShrink: 0,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          background: 'var(--surface-3)', color: roleColor(member.agency_role),
          fontWeight: 'var(--fw-semibold)', fontSize: '.82rem',
          border: '1px solid var(--border)',
        },
      }, initials(member)),
      React.createElement('div', { style: { flex: '1 1 160px', minWidth: 0 } },
        React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } },
          React.createElement('span', { style: { color: 'var(--text)', fontWeight: 'var(--fw-medium)', fontSize: '.92rem' } }, member.name || member.email),
          isMe && React.createElement('span', { className: 'tag', style: { fontSize: '.66rem' } }, tx('page.team.you', null, 'You'))
        ),
        React.createElement('div', { style: { color: 'var(--dim)', fontSize: '.78rem', overflow: 'hidden', textOverflow: 'ellipsis' } }, member.email),
        React.createElement('div', { style: { color: 'var(--muted)', fontSize: '.72rem', marginTop: 2 } },
          pending ? tx('page.team.invitation_pending', null, 'Invitation pending') : tx('page.team.joined', { date: fmtDate(member.created_at) }, 'Joined {date}'))
      ),
      React.createElement('span', {
        style: {
          fontSize: '.72rem', fontWeight: 'var(--fw-tag)', color: roleColor(member.agency_role),
          border: '1px solid ' + roleColor(member.agency_role), borderRadius: 'var(--r-sm)',
          padding: '2px 8px', whiteSpace: 'nowrap',
        },
      }, ROLE_LABEL[member.agency_role] || member.agency_role),
      pending && React.createElement('span', {
        style: { fontSize: '.68rem', color: 'var(--warn)', border: '1px solid var(--warn)', borderRadius: 'var(--r-sm)', padding: '2px 8px', whiteSpace: 'nowrap' },
      }, tx('page.team.pending', null, 'Pending')),
      canManage && React.createElement('div', { style: { display: 'flex', gap: 6, marginLeft: 'auto' } },
        React.createElement('button', { type: 'button', className: 'btn btn-ghost btn-sm', onClick: () => onEdit(member) }, tx('page.team.edit_role', null, 'Edit role')),
        React.createElement('button', { type: 'button', className: 'btn btn-ghost btn-sm', onClick: () => onRevoke(member), style: { color: 'var(--red)' } }, tx('page.team.remove', null, 'Remove'))
      )
    );
  }

  /* ── main panel ──────────────────────────────────────────────────── */
  function TeamManagement() {
    const [loading, setLoading] = useState(true);
    const [err, setErr]         = useState(null);
    const [team, setTeam]       = useState([]);
    const [myRole, setMyRole]   = useState(null);
    const [plan, setPlan]       = useState(null);
    const [modal, setModal]     = useState(null);   // { type:'invite'|'role'|'revoke', member? }
    const [toast, setToast]     = useState(null);

    const meId = (window.currentUser && window.currentUser.id) || null;
    const isOwner = myRole === 'owner';
    const isDev = myRole === 'dev';
    const agencyPlus = plan ? AGENCY_PLUS.includes(plan) : false;

    const refresh = useCallback(async () => {
      setLoading(true); setErr(null);
      try {
        const cfg = await api('GET', '/agency/config');
        const role = cfg.agency_role || 'owner';
        setMyRole(role);
        setPlan(cfg.plan || 'starter');
        if (role === 'member') { setTeam([]); }
        else {
          const t = await api('GET', '/agency/team');
          setTeam(t.team || []);
        }
      } catch (e) {
        setErr(e.message || tx('page.team.load_failed', null, 'Could not load your team.'));
      } finally {
        setLoading(false);
      }
    }, []);
    useEffect(() => { refresh(); }, [refresh]);

    function flash(msg) { setToast(msg); setTimeout(() => setToast(null), 4000); }
    function done(msg) { setModal(null); flash(msg); refresh(); }

    function openInvite() {
      if (!agencyPlus) { flash(tx('page.team.invite_requires_agency', null, 'Inviting teammates requires the Agency plan.')); return; }
      setModal({ type: 'invite' });
    }

    const upgradeUrl = '/?billing=1';

    /* — loading — */
    if (loading) {
      return React.createElement('div', { style: { padding: 'var(--sp-6)', display: 'flex', justifyContent: 'center' } },
        React.createElement('div', { className: 'spinner', 'aria-label': tx('page.team.loading_team', null, 'Loading team') })
      );
    }

    /* — hard error — */
    if (err) {
      return React.createElement('div', { style: { padding: 'var(--sp-5)' } },
        React.createElement('div', { className: 'card', style: { padding: 'var(--sp-5)', textAlign: 'center' } },
          React.createElement('div', { style: { color: 'var(--text-2)', fontSize: '.92rem', marginBottom: 'var(--sp-3)' } }, err),
          React.createElement('button', { type: 'button', className: 'btn btn-ghost', onClick: refresh }, tx('page.team.try_again', null, 'Try again'))
        )
      );
    }

    /* — plain member (no roster access) — */
    if (myRole === 'member') {
      return React.createElement('div', { style: { padding: 'var(--sp-5)' } },
        React.createElement('h2', { style: { margin: '0 0 var(--sp-4)', fontSize: '1.2rem', color: 'var(--text)' } }, tx('page.team.heading', null, 'Team')),
        React.createElement('div', { className: 'card', style: { padding: 'var(--sp-5)', maxWidth: 560 } },
          React.createElement('div', { style: { fontSize: '.94rem', color: 'var(--text-2)', lineHeight: 1.55 } },
            tx('page.team.member_notice', null, 'Your account owner manages who\u2019s on the team. If you need access changed, reach out to them.'))
        )
      );
    }

    /* — owner / dev roster — */
    return React.createElement('div', { style: { padding: 'var(--sp-5)' } },
      React.createElement('style', null,
        '@media (max-width:480px){.tm-head{flex-direction:column;align-items:flex-start!important;gap:var(--sp-3)!important;}}'),

      /* header */
      React.createElement('div', { className: 'tm-head', style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 'var(--sp-4)', gap: 'var(--sp-3)' } },
        React.createElement('div', null,
          React.createElement('h2', { style: { margin: 0, fontSize: '1.2rem', color: 'var(--text)' } }, tx('page.team.heading', null, 'Team')),
          React.createElement('div', { style: { color: 'var(--dim)', fontSize: '.82rem', marginTop: 2 } },
            (team.length === 1
              ? tx('page.team.member_count_one', { n: team.length }, '{n} member')
              : tx('page.team.member_count_many', { n: team.length }, '{n} members'))
            + (isDev ? tx('page.team.read_only_suffix', null, ' \u00b7 read-only') : ''))
        ),
        isOwner && React.createElement('button', { type: 'button', className: 'btn btn-primary', onClick: openInvite }, tx('page.team.invite_teammate', null, '+ Invite teammate'))
      ),

      /* upsell banner — owner on a below-Agency plan */
      isOwner && !agencyPlus && React.createElement('div', {
        style: {
          display: 'flex', alignItems: 'center', gap: 'var(--sp-3)', flexWrap: 'wrap',
          padding: 'var(--sp-4)', marginBottom: 'var(--sp-4)', borderRadius: 'var(--r)',
          border: '1px solid var(--orange)', background: 'var(--orange-soft)',
        },
      },
        React.createElement('div', { style: { flex: '1 1 220px' } },
          React.createElement('div', { style: { color: 'var(--text)', fontWeight: 'var(--fw-semibold)', fontSize: '.92rem' } }, tx('page.team.upsell_title', null, 'Add your team on the Agency plan')),
          React.createElement('div', { style: { color: 'var(--text-2)', fontSize: '.82rem', marginTop: 2, lineHeight: 1.45 } },
            tx('page.team.upsell_body', null, 'Invite developers and staff, assign roles, and manage seats. Available on Agency and above.'))
        ),
        React.createElement('a', { href: upgradeUrl, className: 'btn btn-primary', style: { textDecoration: 'none' } }, tx('page.team.upgrade', null, 'Upgrade'))
      ),

      /* roster */
      React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 'var(--sp-2)' } },
        team.length === 0
          ? React.createElement('div', { className: 'card', style: { padding: 'var(--sp-5)', textAlign: 'center', color: 'var(--dim)' } }, tx('page.team.no_members', null, 'No team members yet.'))
          : team.map(m => React.createElement(MemberCard, {
              key: m.id, member: m, isMe: meId && m.user_id === meId,
              canManage: isOwner && agencyPlus,
              onEdit: (mem) => setModal({ type: 'role', member: mem }),
              onRevoke: (mem) => setModal({ type: 'revoke', member: mem }),
            }))
      ),

      /* dev read-only hint */
      isDev && React.createElement('div', { style: { color: 'var(--muted)', fontSize: '.78rem', marginTop: 'var(--sp-3)' } },
        tx('page.team.dev_hint', null, 'Only the account owner can invite, edit, or remove members.')),

      /* modals */
      modal && modal.type === 'invite' && React.createElement(InviteModal, { onClose: () => setModal(null), onDone: done }),
      modal && modal.type === 'role'   && React.createElement(RoleModal,   { member: modal.member, onClose: () => setModal(null), onDone: done }),
      modal && modal.type === 'revoke' && React.createElement(RevokeModal, { member: modal.member, onClose: () => setModal(null), onDone: done }),

      /* toast */
      toast && React.createElement('div', {
        role: 'status',
        style: {
          position: 'fixed', bottom: 'var(--sp-5)', left: '50%', transform: 'translateX(-50%)',
          background: 'var(--surface-3)', color: 'var(--text)', border: '1px solid var(--border)',
          borderRadius: 'var(--r)', padding: '10px 16px', fontSize: '.85rem',
          boxShadow: 'var(--shadow-md)', zIndex: 9100, maxWidth: '90vw',
        },
      }, toast)
    );
  }

  window.TeamManagement = TeamManagement;
})();
