// ScopeMembersModal.jsx — §22 S4 (C.2): the Owner-only scope-role invitation
// surface, sibling of the per-RFC InvitationsModal.
//
// An Owner grants {owner, contributor} at a scope their reach covers — the
// project, or a single collection within it — to an existing account by email.
// The grant writes the membership row immediately and §15-notifies the grantee
// (no accept round-trip). The modal opens from the project directory's
// owner-only "Members" control.
//
// Two stacked sections, mirroring InvitationsModal:
//
//   1. "Grant a role" — email + role picker (Owner | RFC Contributor) + scope
//      picker (the project, or one collection). The scope options are bounded
//      to what the inviter may grant: a project Owner may pick the project or
//      any collection; a collection-only Owner sees only their collection(s),
//      never the project (C.2.3). There is no "grant at the project but exclude
//      a child" option (C.2.5) — only role and a single scope.
//
//   2. "Current members" — the project subtree's grants, with revoke buttons.
//
// RFC Contributors never see the trigger that opens this modal (C.2.4); the
// directory gates it on the viewer's `can_invite` flag.

import { useEffect, useMemo, useState } from 'react'
import { grantScopeMember, listScopeMembers, revokeScopeMember } from '../api'

const ROLE_OPTIONS = [
  { value: 'contributor', label: 'RFC Contributor — may propose entries in the scope' },
  { value: 'owner', label: 'Owner — administers the scope and its membership' },
]

export default function ScopeMembersModal({ projectId, collections, viewer, onClose }) {
  const [members, setMembers] = useState(null)
  const [loadError, setLoadError] = useState(null)
  const [email, setEmail] = useState('')
  const [role, setRole] = useState('contributor')
  const [scope, setScope] = useState('project') // 'project' | a collection id
  const [submitting, setSubmitting] = useState(false)
  const [submitError, setSubmitError] = useState(null)
  const [submitSuccess, setSubmitSuccess] = useState(null)
  const [revokingKey, setRevokingKey] = useState(null)

  // The project-scope option is offered only to an inviter whose reach covers
  // the project (can_invite at project level); a collection-only Owner gets the
  // collection options alone (C.2.3). Each collection is offered only when the
  // viewer may invite there (its own `can_invite` flag).
  const canInviteAtProject = !!viewer?.can_invite
  const scopeOptions = useMemo(() => {
    const opts = []
    if (canInviteAtProject) {
      opts.push({ value: 'project', label: 'The whole project (every collection)' })
    }
    for (const c of collections || []) {
      if (c.viewer_can_invite || canInviteAtProject) {
        opts.push({ value: c.id, label: `Collection — ${c.name || c.id}` })
      }
    }
    return opts
  }, [collections, canInviteAtProject])

  // Default the scope picker to the first allowed option.
  useEffect(() => {
    if (scopeOptions.length && !scopeOptions.some(o => o.value === scope)) {
      setScope(scopeOptions[0].value)
    }
  }, [scopeOptions]) // eslint-disable-line react-hooks/exhaustive-deps

  async function refresh() {
    setLoadError(null)
    try {
      const r = await listScopeMembers(projectId)
      setMembers(r.items || [])
    } catch (e) {
      // A collection-only Owner is not authorized for the project-wide list;
      // that is expected — they manage their collection via grant/revoke.
      setMembers([])
      setLoadError(e.message)
    }
  }

  useEffect(() => { refresh() /* eslint-disable-line react-hooks/exhaustive-deps */ }, [projectId])

  async function handleGrant(e) {
    e.preventDefault()
    const addr = email.trim()
    if (!addr) return
    setSubmitting(true)
    setSubmitError(null)
    setSubmitSuccess(null)
    try {
      await grantScopeMember(projectId, {
        email: addr,
        role,
        collectionId: scope === 'project' ? null : scope,
      })
      const where = scope === 'project' ? 'the project' : `collection ${scope}`
      setSubmitSuccess(`Granted ${addr} on ${where}.`)
      setEmail('')
      await refresh()
    } catch (err) {
      setSubmitError(err.message || 'Failed to grant the role.')
    } finally {
      setSubmitting(false)
    }
  }

  async function handleRevoke(m) {
    const collectionId = m.scope_type === 'collection' ? m.scope_id : null
    const key = `${m.scope_type}:${m.scope_id}:${m.user_id}`
    setRevokingKey(key)
    try {
      await revokeScopeMember(projectId, m.user_id, collectionId)
      await refresh()
    } catch (err) {
      setSubmitError(err.message || 'Failed to revoke.')
    } finally {
      setRevokingKey(null)
    }
  }

  function scopeLabel(m) {
    if (m.scope_type === 'project') return 'Project'
    return `Collection · ${m.collection_name || m.scope_id}`
  }

  return (
    <div className="modal-overlay" onClick={e => { if (e.target === e.currentTarget) onClose() }}>
      <div className="modal" style={{ maxWidth: 640 }}>
        <div className="modal-header">
          <h2>Members</h2>
          <button className="modal-close" onClick={onClose}>×</button>
        </div>
        <div className="modal-body">
          <p style={{ marginTop: 0, color: '#666' }}>
            Grant collaborators a role at this project or a single collection
            within it. An <strong>RFC Contributor</strong> may propose entries
            in the scope; an <strong>Owner</strong> administers the scope and
            invites others. A grant at the project covers every collection.
          </p>

          <form onSubmit={handleGrant} className="invitations-form" style={{ marginTop: 16 }}>
            <label htmlFor="grant-email">Email</label>
            <input
              id="grant-email"
              type="email"
              value={email}
              onChange={e => setEmail(e.target.value)}
              placeholder="someone@example.com"
              autoFocus
              required
            />
            <label htmlFor="grant-role" style={{ marginTop: 10 }}>Role</label>
            <select id="grant-role" value={role} onChange={e => setRole(e.target.value)}>
              {ROLE_OPTIONS.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
            </select>
            <label htmlFor="grant-scope" style={{ marginTop: 10 }}>Scope</label>
            <select id="grant-scope" value={scope} onChange={e => setScope(e.target.value)}>
              {scopeOptions.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
            </select>
            <div style={{ marginTop: 12, display: 'flex', gap: 8, alignItems: 'center' }}>
              <button type="submit" className="btn-primary" disabled={submitting || !scopeOptions.length}>
                {submitting ? 'Granting…' : 'Grant role'}
              </button>
              {submitError && <span style={{ color: '#c33' }}>{submitError}</span>}
              {submitSuccess && <span style={{ color: '#383' }}>{submitSuccess}</span>}
            </div>
          </form>

          <hr style={{ margin: '20px 0' }} />

          <h3 style={{ margin: '0 0 8px' }}>Current members</h3>
          {members === null && <div>Loading…</div>}
          {members !== null && members.length === 0 && (
            <div style={{ color: '#666' }}>
              {loadError ? 'Membership list is available to project Owners.' : 'No scope roles granted yet.'}
            </div>
          )}
          {members !== null && members.length > 0 && (
            <table style={{ width: '100%', borderCollapse: 'collapse' }}>
              <thead>
                <tr>
                  <th style={{ textAlign: 'left', padding: 4 }}>Member</th>
                  <th style={{ textAlign: 'left', padding: 4 }}>Role</th>
                  <th style={{ textAlign: 'left', padding: 4 }}>Scope</th>
                  <th style={{ padding: 4 }}></th>
                </tr>
              </thead>
              <tbody>
                {members.map(m => {
                  const key = `${m.scope_type}:${m.scope_id}:${m.user_id}`
                  return (
                    <tr key={key} style={{ borderTop: '1px solid #eee' }}>
                      <td style={{ padding: 4 }}>
                        {m.display_name || m.email}
                        {m.permission_state !== 'granted' && (
                          <span style={{ color: '#a60', marginLeft: 6 }}>(pending)</span>
                        )}
                      </td>
                      <td style={{ padding: 4 }}>{m.role === 'owner' ? 'Owner' : 'RFC Contributor'}</td>
                      <td style={{ padding: 4, color: '#666' }}>{scopeLabel(m)}</td>
                      <td style={{ padding: 4, textAlign: 'right' }}>
                        <button
                          type="button"
                          className="btn-link"
                          onClick={() => handleRevoke(m)}
                          disabled={revokingKey === key}
                        >
                          {revokingKey === key ? 'Revoking…' : 'Revoke'}
                        </button>
                      </td>
                    </tr>
                  )
                })}
              </tbody>
            </table>
          )}
        </div>
        <div className="modal-footer">
          <button type="button" className="btn-link" onClick={onClose}>Close</button>
        </div>
      </div>
    </div>
  )
}
