/**
 * CredentialSheet — credential bottom sheet for admin rotation and member self-service (D-07).
 *
 * Shared by two paths:
 *   - admin-rotate: admin sets a credential for a member who already has one ("Rotate Credential")
 *   - admin-add: admin sets a credential for a member who has none ("Add Credential")
 *   - self-service: member sets their own credential ("Add your calendar credential")
 *
 * UI-SPEC §Surface 3:
 *   - Bottom sheet: role="dialog", aria-modal, zIndex 301 (backdrop 300)
 *   - borderRadius 12px 12px 0 0 / padding var(--space-6) / maxWidth 480px centered desktop
 *   - Heading variant per mode, member-name subtitle, type="password" / autocomplete="new-password" (T-10-16)
 *   - Helper text + Fastmail app-password link (new tab, rel="noopener noreferrer") (T-10-15)
 *   - "Validating against CalDAV…" Loader2 spinner inline during mutation
 *   - CalDAV 400 failure copy, Save Credential / Cancel actions
 *   - Success: invalidates ['admin','members'] + ['me'] → needsProviderSetup refresh → SetupBanner unmounts
 *   - Escape closes; focus returns to trigger on close
 *   - 44px touch targets throughout
 *
 * Security:
 *   T-10-15: password never pre-filled, never logged, never stored beyond in-flight request
 *   T-10-16: autoComplete="new-password" prevents autofill of stored credential
 */

import { useState, useEffect, useRef } from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { Loader2 } from 'lucide-react';
import {
  saveCredential,
  saveMyCredential,
  type SaveCredentialPayload,
  type SaveMyCredentialPayload,
} from '../api/client.js';

export type CredentialSheetMode = 'admin-rotate' | 'admin-add' | 'self-service';

interface CredentialSheetProps {
  isOpen: boolean;
  onClose: () => void;
  /** Mode determines heading copy and which API endpoint is called */
  mode: CredentialSheetMode;
  /** The member being targeted (admin modes). For self-service, the current user's name. */
  memberName: string | null;
  /** The member's user id (admin modes only — ignored for self-service) */
  memberId?: number;
  /** Ref to the trigger element — focus returns here on close (a11y) */
  triggerRef?: React.RefObject<HTMLElement | null>;
}

// ── Copywriting contract (UI-SPEC §Copywriting Contract) ───────────────────

function headingFor(mode: CredentialSheetMode): string {
  if (mode === 'admin-rotate') return 'Rotate Credential';
  if (mode === 'admin-add') return 'Add Credential';
  return 'Add your calendar credential';
}

const HELPER_TEXT = 'Enter the Fastmail app password scoped to Calendars/CalDAV.';
const HELPER_LINK_HREF = 'https://app.fastmail.com/settings/security/devicetokens';
const HELPER_LINK_TEXT = 'Get an app password';
const HELPER_LINK_SUFFIX = " — choose the 'Calendars & Contacts (CalDAV)' scope.";
const VALIDATING_TEXT = 'Validating against CalDAV…';
const FAILURE_TEXT =
  "Invalid password — CalDAV validation failed. Check the scope is 'Calendars & Contacts (CalDAV)' and try again.";
const SAVE_LABEL = 'Save Credential';
const CANCEL_LABEL = 'Cancel';

// ── Component ──────────────────────────────────────────────────────────────

export function CredentialSheet({
  isOpen,
  onClose,
  mode,
  memberName,
  memberId,
  triggerRef,
}: CredentialSheetProps) {
  const queryClient = useQueryClient();
  const [password, setPassword] = useState('');
  const [email, setEmail] = useState('');
  const [validationError, setValidationError] = useState<string | null>(null);
  // Focus the heading/first focusable element on open (a11y)
  const headingRef = useRef<HTMLHeadingElement>(null);

  // Escape key closes the sheet (SettingsSheet pattern)
  useEffect(() => {
    if (!isOpen) return;
    const onKeyDown = (e: KeyboardEvent) => {
      if (e.key === 'Escape') {
        handleClose();
      }
    };
    document.addEventListener('keydown', onKeyDown);
    return () => document.removeEventListener('keydown', onKeyDown);
  }, [isOpen]); // eslint-disable-line react-hooks/exhaustive-deps

  // Focus heading on open (a11y)
  useEffect(() => {
    if (isOpen && headingRef.current) {
      headingRef.current.focus();
    }
  }, [isOpen]);

  function handleClose() {
    setPassword('');
    setEmail('');
    setValidationError(null);
    onClose();
    // Return focus to trigger element (a11y)
    if (triggerRef?.current) {
      triggerRef.current.focus();
    }
  }

  const credentialMutation = useMutation({
    mutationFn: async () => {
      if (mode === 'self-service') {
        const payload: SaveMyCredentialPayload = {
          providerType: 'caldav',
          fastmailEmail: email,
          appPassword: password,
        };
        await saveMyCredential(payload);
      } else {
        if (!memberId) throw new Error('memberId required for admin modes');
        const payload: SaveCredentialPayload = {
          userId: memberId,
          providerType: 'caldav',
          fastmailEmail: email,
          appPassword: password,
        };
        await saveCredential(payload);
      }
    },
    onSuccess: () => {
      // Invalidate both caches: admin member list + /api/me (needsProviderSetup refresh)
      void queryClient.invalidateQueries({ queryKey: ['admin', 'members'] });
      void queryClient.invalidateQueries({ queryKey: ['me'] });
      handleClose();
    },
    onError: () => {
      setValidationError(FAILURE_TEXT);
    },
  });

  const handleSave = () => {
    setValidationError(null);
    credentialMutation.mutate();
  };

  if (!isOpen) return null;

  const heading = headingFor(mode);
  const isPending = credentialMutation.isPending;
  const saveDisabled = isPending || password.trim().length === 0 || email.trim().length === 0;
  const phone = window.matchMedia('(max-width: 767px)').matches;

  return (
    <>
      {/* Backdrop */}
      <div
        onClick={handleClose}
        aria-hidden="true"
        style={{
          position: 'fixed',
          inset: 0,
          background: 'rgba(0,0,0,0.4)',
          zIndex: 300,
        }}
      />

      {/* Sheet */}
      <div
        role="dialog"
        aria-modal="true"
        aria-label={heading}
        style={
          phone
            ? {
                position: 'fixed',
                bottom: 0,
                left: 0,
                right: 0,
                background: 'var(--color-surface)',
                borderRadius: '12px 12px 0 0',
                boxShadow: '0 -4px 24px rgba(0,0,0,0.15)',
                padding: 'var(--space-6, 24px)',
                zIndex: 301,
                fontFamily: 'var(--font-family-base)',
              }
            : {
                position: 'fixed',
                top: '50%',
                left: '50%',
                transform: 'translate(-50%, -50%)',
                maxWidth: '480px',
                width: 'calc(100% - var(--space-8, 32px))',
                maxHeight: 'calc(100dvh - var(--space-8, 32px))',
                overflowY: 'auto',
                background: 'var(--color-surface)',
                borderRadius: '12px',
                boxShadow: '0 8px 32px rgba(0,0,0,0.18)',
                padding: 'var(--space-6, 24px)',
                zIndex: 301,
                fontFamily: 'var(--font-family-base)',
              }
        }
      >
        {/* Heading */}
        <h2
          ref={headingRef}
          tabIndex={-1}
          style={{
            margin: '0 0 var(--space-1, 4px) 0',
            fontSize: 'var(--text-heading-size, 18px)',
            fontWeight: 600,
            lineHeight: 'var(--text-heading-line-height, 1.25)',
            color: 'var(--color-text-primary)',
            outline: 'none',
          }}
        >
          {heading}
        </h2>

        {/* Member subtitle */}
        {memberName && (
          <div
            style={{
              fontSize: 'var(--text-body-size, 15px)',
              fontWeight: 400,
              color: 'var(--color-text-secondary)',
              marginBottom: 'var(--space-6, 24px)',
            }}
          >
            {memberName}
          </div>
        )}

        {/* Email field */}
        <div style={{ marginBottom: 'var(--space-4, 16px)' }}>
          <label
            htmlFor="credential-email"
            style={{
              display: 'block',
              fontSize: 'var(--text-label-size, 13px)',
              fontWeight: 600,
              color: 'var(--color-text-primary)',
              marginBottom: 'var(--space-1, 4px)',
            }}
          >
            Fastmail email
          </label>
          <input
            id="credential-email"
            type="email"
            autoComplete="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            placeholder="user@fastmail.com"
            style={{
              width: '100%',
              boxSizing: 'border-box',
              padding: 'var(--space-3, 12px) var(--space-4, 16px)',
              border: `1px solid ${validationError ? 'var(--color-destructive, #DC2626)' : 'var(--color-border)'}`,
              borderRadius: 'var(--space-1, 4px)',
              fontSize: 'var(--text-body-size, 15px)',
              color: 'var(--color-text-primary)',
              background: 'var(--color-surface)',
              fontFamily: 'var(--font-family-base)',
              outline: 'none',
            }}
          />
        </div>

        {/* Password field */}
        <div style={{ marginBottom: 'var(--space-2, 8px)' }}>
          <label
            htmlFor="credential-password"
            style={{
              display: 'block',
              fontSize: 'var(--text-label-size, 13px)',
              fontWeight: 600,
              color: 'var(--color-text-primary)',
              marginBottom: 'var(--space-1, 4px)',
            }}
          >
            App password
          </label>
          <input
            id="credential-password"
            type="password"
            autoComplete="new-password"
            // NEVER pre-filled — T-10-16: existing credential is never fetched to client
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            aria-describedby="credential-helper"
            style={{
              width: '100%',
              boxSizing: 'border-box',
              padding: 'var(--space-3, 12px) var(--space-4, 16px)',
              border: `1px solid ${validationError ? 'var(--color-destructive, #DC2626)' : 'var(--color-border)'}`,
              borderRadius: 'var(--space-1, 4px)',
              fontSize: 'var(--text-body-size, 15px)',
              color: 'var(--color-text-primary)',
              background: 'var(--color-surface)',
              fontFamily: 'var(--font-family-base)',
              outline: 'none',
            }}
          />
        </div>

        {/* Helper text / validation state */}
        <div
          id="credential-helper"
          style={{
            fontSize: 'var(--text-label-size, 13px)',
            color: validationError
              ? 'var(--color-destructive, #DC2626)'
              : 'var(--color-text-secondary)',
            lineHeight: 1.4,
            marginBottom: 'var(--space-6, 24px)',
            display: 'flex',
            alignItems: 'center',
            gap: 'var(--space-2, 8px)',
          }}
        >
          {isPending ? (
            <>
              <Loader2
                size={16}
                aria-hidden="true"
                style={{ animation: 'spin 1s linear infinite', flexShrink: 0 }}
              />
              {VALIDATING_TEXT}
            </>
          ) : validationError ? (
            validationError
          ) : (
            <>
              {HELPER_TEXT}{' '}
              <a
                href={HELPER_LINK_HREF}
                target="_blank"
                rel="noopener noreferrer"
                style={{
                  color: 'var(--color-member-0, #4A90D9)',
                  textDecoration: 'underline',
                }}
              >
                {HELPER_LINK_TEXT}
              </a>
              {HELPER_LINK_SUFFIX}
            </>
          )}
        </div>

        {/* Actions row — right-aligned, Cancel + Save */}
        <div
          style={{
            display: 'flex',
            justifyContent: 'flex-end',
            gap: 'var(--space-3, 12px)',
          }}
        >
          <button
            type="button"
            onClick={handleClose}
            disabled={isPending}
            style={{
              background: 'none',
              border: 'none',
              cursor: isPending ? 'default' : 'pointer',
              fontSize: 'var(--text-label-size, 13px)',
              fontWeight: 600,
              color: 'var(--color-text-secondary)',
              minHeight: '44px',
              minWidth: '44px',
              padding: '0 var(--space-4, 16px)',
              fontFamily: 'var(--font-family-base)',
              borderRadius: 'var(--space-1, 4px)',
              opacity: isPending ? 0.5 : 1,
            }}
          >
            {CANCEL_LABEL}
          </button>

          <button
            type="button"
            onClick={handleSave}
            disabled={saveDisabled}
            style={{
              background: saveDisabled
                ? 'var(--color-border, #E2E4E9)'
                : 'var(--color-member-0, #4A90D9)',
              color: '#ffffff',
              border: 'none',
              cursor: saveDisabled ? 'default' : 'pointer',
              fontSize: 'var(--text-label-size, 13px)',
              fontWeight: 600,
              minHeight: '44px',
              minWidth: '44px',
              padding: '0 var(--space-4, 16px)',
              borderRadius: 'var(--space-1, 4px)',
              fontFamily: 'var(--font-family-base)',
              transition: 'background 0.15s ease',
            }}
          >
            {SAVE_LABEL}
          </button>
        </div>
      </div>
    </>
  );
}
