/* global React, Button, apiFetch, useApiResource, useAuth, formatDateTime, Icon, ConfirmDialog, Select, SettingsBillingPage, SettingsAlertsPage, useToast, requirePaidAction */
const INVITATION_EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

const SETTINGS_SECTIONS = [
  {
    key: 'organization',
    title: 'Organization',
    subtitle: 'Workspace identity, plan, and runtime defaults.',
    icon: 'settings',
  },
  {
    key: 'profile',
    title: 'Profile',
    subtitle: 'Your account identity in this workspace.',
    icon: 'user',
  },
  {
    key: 'api-keys',
    title: 'API keys',
    subtitle: 'Bearer tokens for Armature MCP access.',
    icon: 'key',
  },
  {
    key: 'alerts',
    title: 'Alerts',
    subtitle: 'Notification channels for tool monitors and workflows. Rules are configured per target.',
    icon: 'bell',
  },
  {
    key: 'invitations',
    title: 'Team',
    subtitle: 'Workspace invitations.',
    icon: 'mail',
  },
  {
    key: 'billing',
    title: 'Billing',
    subtitle: 'Plan, payment status, refunds, and cancellation.',
    icon: 'creditCard',
  },
];

function normalizeSettingsSection(section) {
  const value = String(section || '').trim();
  return SETTINGS_SECTIONS.some((item) => item.key === value) ? value : 'organization';
}

function SettingsPage({ section = 'organization', navigate, queryString = '' }) {
  const activeSection = normalizeSettingsSection(section);
  const activeSectionMeta = SETTINGS_SECTIONS.find((item) => item.key === activeSection) || SETTINGS_SECTIONS[0];
  const auth = useAuth();
  const toast = useToast();
  function navigateSection(key) {
    const path = `/settings/${key}`;
    if (navigate) navigate(path);
    else pushBrowserRoute(path);
  }
  function navigateFallback(path) {
    if (navigate) {
      navigate(path);
      return;
    }
    pushBrowserRoute(path);
  }
  function requireSettingsPaidAction() {
    if (navigate) return requirePaidAction(auth, navigate);
    return requirePaidAction(auth, navigateFallback);
  }
  const canManageInvitations = ['owner', 'admin'].includes(auth?.me?.role);
  const org = useApiResource(activeSection === 'organization' ? '/api/settings/organization' : null);
  const apiKeys = useApiResource(activeSection === 'api-keys' ? '/api/settings/api-keys' : null);
  const invitations = useApiResource(activeSection === 'invitations' && canManageInvitations ? '/api/settings/invitations' : null);
  // The daily digest email's footer links here with `?digest=unsubscribe`.
  // The toggle state below answers that link, and the effect further down
  // turns the flag off automatically when the query string asks us to.
  const digestPrefs = useApiResource(activeSection === 'profile' ? '/api/insights/email-preferences' : null);
  const [digestSaving, setDigestSaving] = React.useState(false);
  const organization = org.data?.organization;
  const isLoading = org.loading && !org.data;
  const [keyName, setKeyName] = React.useState('');
  const [createdToken, setCreatedToken] = React.useState('');
  const [creatingKey, setCreatingKey] = React.useState(false);
  const [revokingKeyId, setRevokingKeyId] = React.useState('');
  const [revokeTarget, setRevokeTarget] = React.useState(null);
  const apiKeyMutating = creatingKey || Boolean(revokingKeyId);
  const [inviteMode, setInviteMode] = React.useState('email');
  const [inviteEmail, setInviteEmail] = React.useState('');
  const [inviteRole, setInviteRole] = React.useState('viewer');
  const [createdInviteUrl, setCreatedInviteUrl] = React.useState('');
  const [inviteMessage, setInviteMessage] = React.useState('');
  const [inviteMessageIsError, setInviteMessageIsError] = React.useState(false);
  const [creatingInvite, setCreatingInvite] = React.useState(false);
  const [revokingInviteId, setRevokingInviteId] = React.useState('');
  const [inviteRevokeTarget, setInviteRevokeTarget] = React.useState(null);
  const invitationMutating = creatingInvite || Boolean(revokingInviteId);
  const trimmedInviteEmail = inviteEmail.trim();
  const inviteEmailIsValid = INVITATION_EMAIL_RE.test(trimmedInviteEmail);
  const showInviteEmailError = inviteMode === 'email' && trimmedInviteEmail && !inviteEmailIsValid;
  const canCreateInvite = !invitationMutating && (inviteMode === 'link' || inviteEmailIsValid);

  async function createKey() {
    if (!requireSettingsPaidAction()) return;
    setCreatingKey(true);
    setCreatedToken('');
    try {
      const result = await apiFetch('/api/settings/api-keys', {
        method: 'POST',
        body: JSON.stringify({ name: keyName }),
      });
      setKeyName('');
      setCreatedToken(result.token);
      await apiKeys.reload();
      toast.show({ tone: 'ok', title: 'API key created' });
    } catch (error) {
      toast.show({ tone: 'bad', title: 'Could not create key', description: error.message });
    } finally {
      setCreatingKey(false);
    }
  }

  async function copyCreatedToken() {
    if (!createdToken) return;
    try {
      await navigator.clipboard.writeText(createdToken);
      toast.show({ tone: 'ok', title: 'Token copied' });
    } catch (_e) {
      toast.show({ tone: 'bad', title: 'Copy failed', description: 'Select the token text manually.' });
    }
  }

  async function revokeKey() {
    if (!revokeTarget) return;
    if (!requireSettingsPaidAction()) {
      setRevokeTarget(null);
      return;
    }
    setRevokingKeyId(revokeTarget.id);
    try {
      await apiFetch(`/api/settings/api-keys/${revokeTarget.id}`, { method: 'DELETE' });
      setCreatedToken('');
      await apiKeys.reload();
      toast.show({ tone: 'ok', title: 'API key revoked' });
    } catch (error) {
      toast.show({ tone: 'bad', title: 'Revoke failed', description: error.message });
    } finally {
      setRevokingKeyId('');
      setRevokeTarget(null);
    }
  }

  async function createInvitation() {
    if (!canCreateInvite) return;
    if (!requireSettingsPaidAction()) return;
    setCreatingInvite(true);
    setInviteMessage('');
    setInviteMessageIsError(false);
    setCreatedInviteUrl('');
    try {
      const result = await apiFetch('/api/settings/invitations', {
        method: 'POST',
        body: JSON.stringify({
          role: inviteRole,
          email: inviteMode === 'email' ? trimmedInviteEmail : null,
        }),
      });
      setInviteEmail('');
      setCreatedInviteUrl(result.inviteUrl || '');
      await invitations.reload();
      if (result.emailDelivery?.ok === true) {
        setInviteMessage('Invite sent.');
        setInviteMessageIsError(false);
      } else if (result.emailDelivery?.ok === false) {
        setInviteMessage('Invite created, but email delivery failed. Copy the link below.');
        setInviteMessageIsError(true);
      } else {
        setInviteMessage('Invite link created.');
        setInviteMessageIsError(false);
      }
    } catch (error) {
      setInviteMessage(error.message);
      setInviteMessageIsError(true);
    } finally {
      setCreatingInvite(false);
    }
  }

  async function copyCreatedInvite() {
    if (!createdInviteUrl) return;
    try {
      await navigator.clipboard.writeText(createdInviteUrl);
      setInviteMessage('Invite link copied.');
      setInviteMessageIsError(false);
    } catch (error) {
      setInviteMessage('Copy failed. Select the link manually.');
      setInviteMessageIsError(true);
    }
  }

  async function setDigestEmail(next) {
    if (digestSaving) return;
    setDigestSaving(true);
    try {
      await apiFetch('/api/insights/email-preferences', {
        method: 'PATCH',
        body: JSON.stringify({ enabled: Boolean(next) }),
      });
      await digestPrefs.reload();
      toast.show({
        tone: 'ok',
        title: next ? 'Daily digest enabled' : 'Daily digest disabled',
      });
    } catch (error) {
      toast.show({
        tone: 'bad',
        title: 'Could not update preference',
        description: error.message,
      });
    } finally {
      setDigestSaving(false);
    }
  }

  // The digest email's "Unsubscribe" footer points to
  // /settings/profile?digest=unsubscribe. When that lands, flip the
  // toggle off automatically so the click acts as a real unsubscribe
  // instead of just opening a settings page with no obvious action.
  // A ref latches the attempt so a failed PATCH cannot retry-loop:
  // `replaceState` doesn't fire popstate, so the router's queryString
  // prop wouldn't update on its own — without the latch, every cycle
  // of `digestSaving` back to false would re-enter this effect.
  const digestEnabled = digestPrefs.data?.enabled ?? null;
  const digestUnsubscribeRequested = React.useMemo(() => {
    try {
      const params = new URLSearchParams(queryString || '');
      return params.get('digest') === 'unsubscribe';
    } catch (_error) {
      return false;
    }
  }, [queryString]);
  const digestUnsubscribeAttemptedRef = React.useRef(false);
  React.useEffect(() => {
    if (activeSection !== 'profile') return;
    if (!digestUnsubscribeRequested) return;
    if (digestEnabled === null) return; // wait for the fetch to settle
    if (digestUnsubscribeAttemptedRef.current) return;
    digestUnsubscribeAttemptedRef.current = true;
    const cleanUrl = () => {
      try {
        const url = new URL(window.location.href);
        url.searchParams.delete('digest');
        window.history.replaceState(null, '', url.pathname + (url.search || ''));
      } catch (_error) {
        /* best-effort URL cleanup */
      }
    };
    if (digestEnabled !== true) {
      // Already opted out. Strip the query so a later opt-in toggle
      // doesn't get silently undone the next time this effect runs.
      cleanUrl();
      return;
    }
    setDigestEmail(false).finally(cleanUrl);
    // setDigestEmail is recreated on every render but the ref guard
    // above guarantees we run at most once per page load.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [activeSection, digestUnsubscribeRequested, digestEnabled]);

  async function revokeInvitation() {
    if (!inviteRevokeTarget) return;
    if (!requireSettingsPaidAction()) {
      setInviteRevokeTarget(null);
      return;
    }
    setRevokingInviteId(inviteRevokeTarget.id);
    setInviteMessage('');
    setInviteMessageIsError(false);
    try {
      await apiFetch(`/api/settings/invitations/${inviteRevokeTarget.id}`, { method: 'DELETE' });
      await invitations.reload();
      setInviteMessage('Invite revoked.');
      setInviteMessageIsError(false);
    } catch (error) {
      setInviteMessage(error.message);
      setInviteMessageIsError(true);
    } finally {
      setRevokingInviteId('');
      setInviteRevokeTarget(null);
    }
  }

  return (
    <div className="page-inner settings-page">
      <div className="ui-page-head">
        <h1 className="ui-page-title">Settings</h1>
      </div>

      <nav className="settings-nav" aria-label="Settings sections">
        {SETTINGS_SECTIONS.map((s) => (
          <button
            key={s.key}
            type="button"
            className={`settings-nav-link ${activeSection === s.key ? 'active' : ''}`}
            onClick={() => navigateSection(s.key)}>
            <Icon name={s.icon} size={13} />
            <span>{s.title}</span>
          </button>
        ))}
      </nav>

      <div className="settings-section-head">
        <div className="ui-section-title">{activeSectionMeta.title}</div>
        <div className="text-xs muted">{activeSectionMeta.subtitle}</div>
      </div>

      {activeSection === 'organization' && (
        <div className="card settings-card">
        {isLoading ? (
          <SettingsLoadingRows />
        ) : org.error ? (
          <div className="field-row">
            <div><div className="field-label">Organization</div></div>
            <div className="field-value error-text">{org.error.message}</div>
          </div>
        ) : (
          <>
            <SettingRow label="Organization name" value={organization?.name || 'Organization'} />
            <SettingRow label="Slug" value={organization?.slug || '-'} mono />
            <SettingRow label="Plan" value={organization?.plan || '-'} />
            <SettingRow label="Default region" value={organization?.default_region || '-'} mono />
            <SettingRow label="Trace retention" value={organization?.trace_retention_days ? `${organization.trace_retention_days} days` : '-'} />
          </>
        )}
        </div>
      )}

      {activeSection === 'profile' && (
        <div className="card settings-card">
          <SettingRow label="Name" value={auth?.me?.profile?.displayName || 'Not set'} />
          <SettingRow label="Email" value={auth?.me?.profile?.email || '-'} />
          <SettingRow label="Workspace role" value={auth?.me?.role || '-'} />
          <SettingRow label="Profile ID" value={auth?.me?.profile?.id || '-'} mono />
          <div className="field-row">
            <div>
              <div className="field-label">Daily Insights email</div>
              <div className="field-help">Receive the daily digest of MCP findings.</div>
            </div>
            <div className="field-value">
              <label className="i-toggle">
                <input
                  type="checkbox"
                  checked={digestEnabled === true}
                  disabled={digestEnabled === null || digestSaving}
                  onChange={(event) => setDigestEmail(event.target.checked)} />
                <span>
                  {digestEnabled === null ? '' : digestEnabled ? 'Subscribed' : 'Unsubscribed'}
                </span>
              </label>
            </div>
          </div>
        </div>
      )}

      {activeSection === 'billing' && <SettingsBillingPage navigate={navigate} />}

      {activeSection === 'alerts' && <SettingsAlertsPage navigate={navigate} queryString={queryString} />}

      {activeSection === 'api-keys' && (
        <div className="card settings-card">
        <div className="field-row api-key-create-row">
          <div>
            <div className="field-label">API keys</div>
            <div className="field-help">Bearer tokens for the Armature MCP.</div>
          </div>
          <div className="api-key-create">
            <input
              className="input"
              placeholder="Key name"
              value={keyName}
              disabled={apiKeyMutating}
              onChange={(event) => setKeyName(event.target.value)}
            />
            <Button
              variant="primary"
              disabled={apiKeyMutating || !keyName.trim()}
              loading={creatingKey}
              loadingLabel="Creating key..."
              onClick={createKey}>
              <Icon name="key" size={13} />Create key
            </Button>
          </div>
        </div>

        {createdToken && (
          <div className="field-row api-key-token-row">
            <div>
              <div className="field-label">New token</div>
              <div className="field-help">Shown once. Store it in your MCP client.</div>
            </div>
            <div className="settings-token-row">
              <div className="code api-key-token">{createdToken}</div>
              <Button size="sm" variant="ghost" onClick={copyCreatedToken}>
                <Icon name="copy" size={13} />Copy
              </Button>
            </div>
          </div>
        )}

        <div className="api-key-list">
          {apiKeys.loading && !apiKeys.data ? (
            <div className="field-row loading-row">
              <div><div className="field-label">Existing keys</div></div>
              <div className="field-value"><span className="skel skel-long"></span></div>
            </div>
          ) : apiKeys.error ? (
            <div className="field-row">
              <div><div className="field-label">Existing keys</div></div>
              <div className="field-value error-text">{apiKeys.error.message}</div>
            </div>
          ) : (apiKeys.data?.rows || []).length === 0 ? (
            <div className="field-row">
              <div><div className="field-label">Existing keys</div></div>
              <div className="field-value muted">No API keys created.</div>
            </div>
          ) : (
            (apiKeys.data?.rows || []).map((key) => (
              <div className="field-row api-key-row" key={key.id}>
                <div>
                  <div className="field-label">{key.name}</div>
                  <div className="field-help mono">{key.tokenPrefix}...</div>
                </div>
                <div className="api-key-meta">
                  <div>
                    <span className="badge badge-neutral">{key.role}</span>
                    <span className="muted">Created {formatDateTime(key.createdAt)}</span>
                    <span className="muted">Calls {key.callCount || 0}</span>
                    <span className="muted">Last used {key.lastUsedAt ? formatDateTime(key.lastUsedAt) : 'never'}</span>
                  </div>
                  <Button
                    size="sm"
                    disabled={apiKeyMutating}
                    loading={revokingKeyId === key.id}
                    loadingLabel="Revoking..."
                    onClick={() => setRevokeTarget(key)}>
                    <Icon name="trash" size={13} />Revoke
                  </Button>
                </div>
              </div>
            ))
          )}
        </div>
        </div>
      )}
      {activeSection === 'invitations' && (
        <div className="card settings-card">
          {canManageInvitations ? (
            <div className="field-row invitation-create-row">
            <div>
              <div className="field-label">Invitations</div>
              <div className="field-help">Send a workspace invite or create a manual link.</div>
            </div>
            <div className="invitation-create">
              <div className="seg invitation-mode" role="tablist" aria-label="Invitation type">
                <button type="button" className={inviteMode === 'email' ? 'active' : ''} onClick={() => setInviteMode('email')}>Email</button>
                <button type="button" className={inviteMode === 'link' ? 'active' : ''} onClick={() => setInviteMode('link')}>Link only</button>
              </div>
              <div className="invitation-form-row">
                {inviteMode === 'email' ? (
                  <input
                    className="input"
                    type="email"
                    inputMode="email"
                    placeholder="user@example.com"
                    value={inviteEmail}
                    aria-invalid={showInviteEmailError ? 'true' : undefined}
                    disabled={invitationMutating}
                    onChange={(event) => setInviteEmail(event.target.value)}
                  />
                ) : (
                  <div className="field-value muted">Anyone with the link can join before it expires.</div>
                )}
                <Select
                  value={inviteRole}
                  disabled={invitationMutating}
                  onChange={setInviteRole}
                  options={[
                    { value: 'viewer', label: 'Viewer' },
                    { value: 'editor', label: 'Editor' },
                    { value: 'admin', label: 'Admin' },
                  ]} />
                <Button
                  variant="primary"
                  disabled={!canCreateInvite}
                  loading={creatingInvite}
                  loadingLabel={inviteMode === 'email' ? 'Sending invite...' : 'Creating link...'}
                  onClick={createInvitation}>
                  <Icon name="mail" size={13} />{inviteMode === 'email' ? 'Send invite' : 'Create link'}
                </Button>
              </div>
              {showInviteEmailError && <div className="field-help error-text">Enter a valid email address.</div>}
            </div>
            </div>
          ) : (
            <div className="field-row">
              <div>
                <div className="field-label">Invite team members</div>
                <div className="field-help">Requires workspace owner or admin access.</div>
              </div>
              <div className="field-value muted">Your current role is {auth?.me?.role || 'unknown'}.</div>
            </div>
          )}

          {createdInviteUrl && (
            <div className="field-row invitation-link-row">
              <div>
                <div className="field-label">New invite link</div>
                <div className="field-help">Shown once. Copy it before leaving this page.</div>
              </div>
              <div className="invitation-created-link">
                <div className="code api-key-token">{createdInviteUrl}</div>
                <Button size="sm" onClick={copyCreatedInvite}>
                  <Icon name="copy" size={13} />Copy
                </Button>
              </div>
            </div>
          )}

          {inviteMessage && (
            <div className="field-row">
              <div><div className="field-label">Invite status</div></div>
              <div className={`field-value ${inviteMessageIsError ? 'error-text' : ''}`}>{inviteMessage}</div>
            </div>
          )}

          {canManageInvitations && (
          <div className="invitation-list">
            {invitations.loading && !invitations.data ? (
              <div className="field-row loading-row">
                <div><div className="field-label">Existing invites</div></div>
                <div className="field-value"><span className="skel skel-long"></span></div>
              </div>
            ) : invitations.error ? (
              <div className="field-row">
                <div><div className="field-label">Existing invites</div></div>
                <div className="field-value error-text">{invitations.error.message}</div>
              </div>
            ) : (invitations.data?.rows || []).length === 0 ? (
              <div className="field-row">
                <div><div className="field-label">Existing invites</div></div>
                <div className="field-value muted">No invites created.</div>
              </div>
            ) : (
              (invitations.data?.rows || []).map((invite) => (
                <div className="field-row invitation-row" key={invite.id}>
                  <div>
                    <div className="field-label">{invite.email || 'Link-only invite'}</div>
                    <div className="field-help">Expires {formatDateTime(invite.expiresAt)}</div>
                  </div>
                  <div className="invitation-meta">
                    <div>
                      <InvitationStatusBadge status={invite.status} />
                      <span className="badge badge-neutral">{invite.role}</span>
                      <span className="muted">Created {formatDateTime(invite.createdAt)}</span>
                    </div>
                    {canManageInvitations && (
                      <Button
                        size="sm"
                        disabled={invitationMutating || invite.status !== 'pending'}
                        loading={revokingInviteId === invite.id}
                        loadingLabel="Revoking..."
                        onClick={() => setInviteRevokeTarget(invite)}>
                        <Icon name="trash" size={13} />Revoke
                      </Button>
                    )}
                  </div>
                </div>
              ))
            )}
          </div>
          )}
        </div>
      )}
      <ConfirmDialog
        open={Boolean(revokeTarget)}
        tone="danger"
        title="Revoke this API key?"
        description="Existing MCP clients using it will fail immediately."
        confirmLabel="Revoke key"
        confirmBusyLabel="Revoking..."
        busy={Boolean(revokingKeyId)}
        onCancel={() => {
          if (!revokingKeyId) setRevokeTarget(null);
        }}
        onConfirm={revokeKey}
      />
      <ConfirmDialog
        open={Boolean(inviteRevokeTarget)}
        tone="danger"
        title="Revoke this invite?"
        description="The invite link will stop working. Existing members are not affected."
        confirmLabel="Revoke invite"
        confirmBusyLabel="Revoking..."
        busy={Boolean(revokingInviteId)}
        onCancel={() => {
          if (!revokingInviteId) setInviteRevokeTarget(null);
        }}
        onConfirm={revokeInvitation}
      />
    </div>
  );
}

function InvitationStatusBadge({ status }) {
  const map = {
    pending: 'badge-pending',
    accepted: 'badge-success',
    revoked: 'badge-fail',
    expired: 'badge-warning',
  };
  const label = status ? status[0].toUpperCase() + status.slice(1) : 'Unknown';
  return <span className={`badge ${map[status] || 'badge-neutral'}`}><span className="dot"></span>{label}</span>;
}

function SettingRow({ label, value, mono = false }) {
  return (
    <div className="field-row">
      <div><div className="field-label">{label}</div></div>
      <div className={`field-value ${mono ? 'mono' : ''}`}>{value}</div>
    </div>
  );
}

function SettingsLoadingRows() {
  return (
    <>
      {['Organization name', 'Slug', 'Plan', 'Default region', 'Trace retention'].map((label, index) => (
        <div className="field-row loading-row" key={label}>
          <div>
            <div className="field-label">{label}</div>
            {index === 0 && <span className="skel skel-field-help"></span>}
          </div>
          <div className="field-value">
            <span className={`skel ${index === 0 ? 'skel-long' : 'skel-mid'}`}></span>
          </div>
        </div>
      ))}
    </>
  );
}


window.SettingsPage = SettingsPage;
