// members-pane.jsx — Settings → Members tab content.
//
// R03 / T0007 (Phase 5). Lists current memberships with their roles +
// per-row controls for changing role (`setMemberRole`) and removing the
// member (`removeMember`). Surfaces pending invitations beneath the
// members list (via `listInvitations`). "Invite member" button opens
// the `InviteMemberModal` (from invite-member.jsx).
//
// Last-owner guard: the UI greys out "Remove" + role-change for the
// last owner. Server still enforces this — UI courtesy only.
//
// Exposes window.MembersPane.

(function (window) {
  'use strict';

  const { db, fns, useAuth } = window.exoteamAuth || {};
  if (!useAuth) {
    console.error('members-pane.jsx: window.exoteamAuth missing');
    return;
  }

  const ROLES = ['owner', 'admin', 'member', 'viewer'];

  function MembersPane({ workspaceId }) {
    const { user } = useAuth();
    const [memberships, setMemberships] = React.useState([]);
    const [users, setUsers] = React.useState({}); // uid → user doc
    const [invitations, setInvitations] = React.useState([]);
    const [busy, setBusy] = React.useState(null); // uid currently being mutated
    const [error, setError] = React.useState(null);
    const [showInvite, setShowInvite] = React.useState(false);

    // Memberships for this workspace. Rules: members of this workspace
    // can read other memberships on the same workspace (T0006 rules
    // grant member+ read on memberships of their own workspace).
    React.useEffect(() => {
      if (!workspaceId) return undefined;
      const unsub = db
        .collection('memberships')
        .where('workspace_id', '==', workspaceId)
        .onSnapshot(
          (snap) => {
            setMemberships(snap.docs.map((d) => ({ id: d.id, ...d.data() })));
          },
          (err) => {
            console.error('MembersPane: memberships snapshot', err);
            setError(err.message || String(err));
          },
        );
      return unsub;
    }, [workspaceId]);

    // Fetch user docs (display_name, email) for each member. One-shot per
    // uid we haven't seen before. Rules: users/{uid} doc is readable by
    // the user themselves; reading other users' docs requires the
    // memberships-share-a-workspace rule (T0004 added this for the
    // Members UI).
    React.useEffect(() => {
      const uidsToFetch = memberships
        .map((m) => m.user_id)
        .filter((uid) => uid && !users[uid]);
      if (uidsToFetch.length === 0) return;
      Promise.all(
        uidsToFetch.map((uid) =>
          db
            .collection('users')
            .doc(uid)
            .get()
            .then((snap) => [uid, snap.exists ? snap.data() : null])
            .catch(() => [uid, null]),
        ),
      ).then((pairs) => {
        setUsers((prev) => {
          const next = { ...prev };
          for (const [uid, doc] of pairs) {
            next[uid] = doc;
          }
          return next;
        });
      });
    }, [memberships]);

    // Pending invitations for this workspace.
    React.useEffect(() => {
      if (!workspaceId || !fns) return undefined;
      let cancelled = false;
      const callable = fns.httpsCallable('listInvitations');
      callable({ workspace_id: workspaceId })
        .then(({ data }) => {
          if (cancelled) return;
          setInvitations((data && data.invitations) || []);
        })
        .catch((err) => {
          if (cancelled) return;
          console.warn('MembersPane: listInvitations', err);
        });
      return () => {
        cancelled = true;
      };
    }, [workspaceId, showInvite]); // refresh after invite modal closes

    const myMembership = memberships.find(
      (m) => user && m.user_id === user.uid,
    );
    const myRole = myMembership ? myMembership.role : null;
    const ownerCount = memberships.filter((m) => m.role === 'owner').length;

    function canManage() {
      return myRole === 'owner' || myRole === 'admin';
    }

    async function changeRole(uid, newRole) {
      setBusy(uid);
      setError(null);
      try {
        const callable = fns.httpsCallable('setMemberRole');
        await callable({
          workspace_id: workspaceId,
          user_id: uid,
          role: newRole,
        });
      } catch (err) {
        console.error('setMemberRole failed', err);
        setError(err.message || String(err));
      } finally {
        setBusy(null);
      }
    }

    async function removeMember(uid) {
      if (
        !window.confirm(
          'Remove this member from the Workspace? They will lose access immediately.',
        )
      ) {
        return;
      }
      setBusy(uid);
      setError(null);
      try {
        const callable = fns.httpsCallable('removeMember');
        await callable({ workspace_id: workspaceId, user_id: uid });
      } catch (err) {
        console.error('removeMember failed', err);
        setError(err.message || String(err));
      } finally {
        setBusy(null);
      }
    }

    if (!workspaceId) {
      return (
        <div>
          <h2 className="exo-admin-title">Members</h2>
          <p className="exo-admin-subtitle">Pick a workspace.</p>
        </div>
      );
    }

    return (
      <div>
        <header className="exo-admin-pane-header">
          <div>
            <h2 className="exo-admin-title">Members</h2>
            <p className="exo-admin-subtitle" style={{ margin: 0 }}>
              People with access to this Workspace.
            </p>
          </div>
          {canManage() && (
            <button
              type="button"
              className="exo-admin-btn"
              onClick={() => setShowInvite(true)}
            >
              + Invite member
            </button>
          )}
        </header>

        {error && (
          <div className="exo-admin-banner is-error" style={{ marginBottom: 14 }}>
            <span>{error}</span>
          </div>
        )}

        <ul className="exo-admin-list">
          {memberships.map((m) => {
            const u = users[m.user_id];
            const isLastOwner = m.role === 'owner' && ownerCount <= 1;
            const isMe = user && m.user_id === user.uid;
            const canEditThisRow = canManage() && !isLastOwner;
            return (
              <li key={m.id} className="exo-admin-list-row">
                <div style={{ flex: 1, minWidth: 0 }}>
                  <strong>
                    {u && u.display_name ? u.display_name : m.user_id.slice(0, 8) + '…'}
                    {isMe && (
                      <span style={{ color: 'var(--exo-ink-tertiary)', fontSize: 12, marginLeft: 8 }}>
                        (you)
                      </span>
                    )}
                  </strong>
                  <div className="exo-admin-list-row-meta">
                    {u && u.email ? u.email : '—'}
                  </div>
                </div>
                <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                  {canEditThisRow && !isMe ? (
                    <select
                      className="exo-admin-select"
                      value={m.role}
                      onChange={(e) => changeRole(m.user_id, e.target.value)}
                      disabled={busy === m.user_id}
                      style={{ width: 'auto', minWidth: 110, padding: '6px 10px', fontSize: 13 }}
                    >
                      {ROLES.map((r) => (
                        <option key={r} value={r}>
                          {r.charAt(0).toUpperCase() + r.slice(1)}
                        </option>
                      ))}
                    </select>
                  ) : (
                    <span className={'exo-admin-pill ' + rolePillModifier(m.role)}>
                      {m.role}
                    </span>
                  )}
                  {canManage() && !isMe && !isLastOwner && (
                    <button
                      type="button"
                      className="exo-admin-btn--link is-destructive"
                      onClick={() => removeMember(m.user_id)}
                      disabled={busy === m.user_id}
                    >
                      Remove
                    </button>
                  )}
                  {isLastOwner && !isMe && (
                    <span
                      style={{ color: 'var(--exo-ink-tertiary)', fontSize: 12 }}
                      title="A Workspace must have at least one owner."
                    >
                      Last owner
                    </span>
                  )}
                </div>
              </li>
            );
          })}
        </ul>

        {invitations.length > 0 && (
          <>
            <hr className="exo-admin-divider" />
            <h3 className="exo-admin-section-title">Pending invitations</h3>
            <ul className="exo-admin-list">
              {invitations.map((inv) => (
                <li key={inv.id} className="exo-admin-list-row">
                  <div style={{ flex: 1 }}>
                    <strong>{inv.email}</strong>
                    <div className="exo-admin-list-row-meta">
                      Invited as {inv.role}
                      {inv.expires_at && ' · expires ' + fmtTs(inv.expires_at)}
                    </div>
                  </div>
                  <span className={'exo-admin-pill ' + rolePillModifier(inv.role)}>
                    {inv.role}
                  </span>
                </li>
              ))}
            </ul>
          </>
        )}

        {showInvite && window.InviteMemberModal && (
          <window.InviteMemberModal
            workspaceId={workspaceId}
            actorRole={myRole}
            onClose={() => setShowInvite(false)}
            onInvited={() => {
              // Modal handles its own success state; we'll just refresh
              // the pending invitations list on close via the effect.
            }}
          />
        )}
      </div>
    );
  }

  function fmtTs(ts) {
    if (!ts) return '—';
    const d = ts.toDate ? ts.toDate() : new Date(ts);
    return d.toLocaleDateString();
  }

  // Hairline-pill modifier — see `.exo-admin-pill` in site-shared.css.
  function rolePillModifier(role) {
    if (role === 'owner') return 'is-success';
    if (role === 'admin') return 'is-accent';
    return 'is-muted';
  }

  window.MembersPane = MembersPane;
})(window);
