/**
 * @file KeySetupStep.jsx
 * @description Key generation step for partner setup after admin approval.
 * Generates X25519 (scope DEK ECDH) and ES256 (DPoP) keypairs entirely in the
 * browser using the Web Crypto API. Private keys never leave client memory.
 */

window.KeySetupStep = function KeySetupStep({ clientId, registrationAccessToken, onComplete }) {
  const [generating, setGenerating] = React.useState(false);
  const [submitting, setSubmitting] = React.useState(false);
  const [error, setError] = React.useState(null);
  const [keys, setKeys] = React.useState(null);
  const [submitted, setSubmitted] = React.useState(false);
  const [copied, setCopied] = React.useState({});
  const [privateSaved, setPrivateSaved] = React.useState(false);
  const [certPackage, setCertPackage] = React.useState(null);
  const [certPackageError, setCertPackageError] = React.useState(null);
  const [certPackageSaved, setCertPackageSaved] = React.useState(false);

  const copyToClipboard = async (text, field) => {
    try {
      await navigator.clipboard.writeText(text);
      setCopied(prev => ({ ...prev, [field]: true }));
      setTimeout(() => setCopied(prev => ({ ...prev, [field]: false })), 2000);
    } catch (err) {
      setError('Failed to copy to clipboard');
    }
  };

  const arrayBufferToBase64 = (buffer) => {
    const bytes = new Uint8Array(buffer);
    let binary = '';
    for (let i = 0; i < bytes.length; i++) {
      binary += String.fromCharCode(bytes[i]);
    }
    return btoa(binary);
  };

  const generateKeys = async () => {
    setGenerating(true);
    setError(null);

    try {
      // Generate X25519 keypair for scope DEK ECDH
      const x25519 = await crypto.subtle.generateKey('X25519', true, ['deriveBits']);

      const x25519PublicRaw = await crypto.subtle.exportKey('raw', x25519.publicKey);
      const x25519PrivatePkcs8 = await crypto.subtle.exportKey('pkcs8', x25519.privateKey);
      // X25519 PKCS8 DER has the 32-byte raw key as the last 32 bytes
      const x25519PrivateRaw = x25519PrivatePkcs8.slice(-32);

      const x25519PublicBase64 = arrayBufferToBase64(x25519PublicRaw);
      const x25519PrivateBase64 = arrayBufferToBase64(x25519PrivateRaw);

      // Generate ES256 (P-256) keypair for DPoP (RFC 9449)
      const ecdsa = await crypto.subtle.generateKey(
        { name: 'ECDSA', namedCurve: 'P-256' },
        true,
        ['sign', 'verify']
      );

      const ecdsaPublicJwk = await crypto.subtle.exportKey('jwk', ecdsa.publicKey);
      const ecdsaPrivateJwk = await crypto.subtle.exportKey('jwk', ecdsa.privateKey);
      const ecdsaPrivatePkcs8Der = await crypto.subtle.exportKey('pkcs8', ecdsa.privateKey);
      const ecdsaPrivatePkcs8B64 = arrayBufferToBase64(ecdsaPrivatePkcs8Der);
      const ecdsaPrivatePem =
        '-----BEGIN PRIVATE KEY-----\n' +
        (ecdsaPrivatePkcs8B64.match(/.{1,64}/g) || []).join('\n') +
        '\n-----END PRIVATE KEY-----\n';

      // JWK thumbprint per RFC 7638
      const thumbprintInput = JSON.stringify({
        crv: ecdsaPublicJwk.crv,
        kty: ecdsaPublicJwk.kty,
        x: ecdsaPublicJwk.x,
        y: ecdsaPublicJwk.y
      });
      const thumbprintHash = await crypto.subtle.digest(
        'SHA-256',
        new TextEncoder().encode(thumbprintInput)
      );
      const thumbprintBytes = new Uint8Array(thumbprintHash);
      let thumbprintBase64url = btoa(String.fromCharCode(...thumbprintBytes))
        .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');

      const dpopKeyId = thumbprintBase64url;

      setKeys({
        x25519: {
          publicBase64: x25519PublicBase64,
          privateBase64: x25519PrivateBase64,
        },
        dpop: {
          publicJwk: {
            kty: ecdsaPublicJwk.kty,
            crv: ecdsaPublicJwk.crv,
            x: ecdsaPublicJwk.x,
            y: ecdsaPublicJwk.y,
            kid: dpopKeyId,
          },
          privateJwk: { ...ecdsaPrivateJwk, kid: dpopKeyId },
          privatePem: ecdsaPrivatePem,
          thumbprint: thumbprintBase64url,
          keyId: dpopKeyId,
        }
      });
    } catch (err) {
      if (err.name === 'NotSupportedError') {
        setError(
          'Your browser does not support X25519 key generation. ' +
          'Please use Chrome 113+, Firefox 130+, or Safari 17.4+, ' +
          'or use the CLI tool: npx ts-node src/cli/generatePartnerKeys.ts'
        );
      } else {
        setError('Key generation failed: ' + (err.message || 'Unknown error'));
      }
    } finally {
      setGenerating(false);
    }
  };

  const handleSubmitPublicKey = async () => {
    if (!keys || !clientId || !registrationAccessToken) return;

    setSubmitting(true);
    setError(null);

    try {
      await API.submitPublicKey(clientId, registrationAccessToken, keys.x25519.publicBase64);
      setSubmitted(true);
      try {
        const pkg = await API.fetchCertPackage(clientId, registrationAccessToken);
        if (pkg && pkg.zipBase64 && pkg.filename) {
          setCertPackage(pkg);
        }
      } catch (pkgErr) {
        setCertPackageError(pkgErr.message || 'Unable to fetch certificate package');
      }
      if (onComplete) onComplete();
    } catch (err) {
      setError(err.message || 'Failed to submit public key');
    } finally {
      setSubmitting(false);
    }
  };

  const downloadDpopPem = () => {
    if (!keys || !keys.dpop || !keys.dpop.privatePem) return;
    const blob = new Blob([keys.dpop.privatePem], { type: 'application/x-pem-file' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `v0uchme-partner-dpop-${clientId.substring(0, 8)}.pem`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  };

  const downloadCertPackage = () => {
    if (!certPackage) return;
    const binary = atob(certPackage.zipBase64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
    const blob = new Blob([bytes], { type: 'application/zip' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = certPackage.filename;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
    setCertPackageSaved(true);
  };

  const downloadPrivateKeys = () => {
    const data = {
      x25519_encryption: {
        description: 'X25519 private key for scope DEK ECDH decryption',
        privateKeyBase64: keys.x25519.privateBase64,
        publicKeyBase64: keys.x25519.publicBase64,
      },
      dpop_signing: {
        description: 'ES256 private key for DPoP proof signing (RFC 9449)',
        keyId: keys.dpop.keyId,
        privateKeyJwk: keys.dpop.privateJwk,
        privateKeyPem: keys.dpop.privatePem,
        publicKeyJwk: keys.dpop.publicJwk,
        thumbprint: keys.dpop.thumbprint,
      },
      generated_at: new Date().toISOString(),
      warning: 'Store these keys securely. They are NOT stored by v0uchme and cannot be recovered.'
    };

    const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `v0uchme-partner-keys-${clientId.substring(0, 8)}.json`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);

    setPrivateSaved(true);
  };

  const CopyButton = ({ text, field }) => (
    <button
      onClick={() => copyToClipboard(text, field)}
      className="ml-2 text-gray-400 hover:text-gray-600 flex-shrink-0"
      title="Copy to clipboard"
    >
      {copied[field] ? (
        <svg className="w-5 h-5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
        </svg>
      ) : (
        <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
        </svg>
      )}
    </button>
  );

  if (submitted) {
    return (
      <div className="fade-in text-center py-8">
        <div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
          <svg className="w-8 h-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
          </svg>
        </div>
        <h2 className="text-2xl font-bold text-gray-900">Setup Complete</h2>
        <p className="text-gray-600 mt-2">
          Your encryption public key has been registered. Your partner account is fully configured.
        </p>
        {certPackage && (
          <div className="mt-6 bg-amber-50 border border-amber-200 rounded-lg p-4 text-left max-w-lg mx-auto">
            <p className="text-sm text-amber-900 font-medium">Download your certificate package</p>
            <p className="text-sm text-amber-800 mt-1">
              This one-time download contains your mTLS client cert, CockroachDB client cert, CA bundle,
              env.template (with client_secret), JWKS, and README. v0uchme does not retain a copy.
            </p>
            <button
              onClick={downloadCertPackage}
              className="mt-3 inline-flex items-center px-4 py-2 bg-amber-600 text-white text-sm font-medium rounded hover:bg-amber-700"
            >
              {certPackageSaved ? 'Downloaded - re-download' : `Download ${certPackage.filename}`}
            </button>
          </div>
        )}
        {certPackageError && (
          <div className="mt-6 bg-red-50 border border-red-200 rounded-lg p-4 text-left max-w-lg mx-auto">
            <p className="text-sm text-red-800 font-medium">Certificate package unavailable</p>
            <p className="text-sm text-red-700 mt-1">{certPackageError}</p>
          </div>
        )}
        <div className="mt-6 bg-green-50 border border-green-200 rounded-lg p-4 text-left max-w-lg mx-auto">
          <p className="text-sm text-green-800 font-medium">Next steps:</p>
          <ul className="mt-2 text-sm text-green-700 space-y-1 list-disc list-inside">
            <li>Save both the keys JSON and the certificate package somewhere safe</li>
            <li>Use the X25519 private key for scope DEK ECDH decryption</li>
            <li>Use the ES256 private key for DPoP proof signing</li>
            <li>Host your JWKS at /.well-known/jwks.json</li>
          </ul>
        </div>
      </div>
    );
  }

  return (
    <div className="fade-in">
      <div className="text-center mb-8">
        <div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
          <svg className="w-8 h-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
          </svg>
        </div>
        <h2 className="text-2xl font-bold text-gray-900">Generate Encryption Keys</h2>
        <p className="text-gray-600 mt-2">
          Your account has been approved. Generate your cryptographic keys to complete setup.
        </p>
      </div>

      <div className="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-6">
        <div className="flex items-start">
          <svg className="w-5 h-5 text-blue-600 mt-0.5 mr-3 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
          </svg>
          <div>
            <p className="font-medium text-blue-800">Zero-knowledge key generation</p>
            <p className="text-blue-700 text-sm mt-1">
              Keys are generated entirely in your browser. Private keys never leave your device
              and are not sent to v0uch.me. Only the public key is submitted for registration.
            </p>
          </div>
        </div>
      </div>

      {error && (
        <div className="mb-6 p-4 bg-red-50 border border-red-200 rounded-lg">
          <p className="text-red-700 text-sm">{error}</p>
        </div>
      )}

      {!keys ? (
        <div className="text-center py-8">
          <button
            onClick={generateKeys}
            disabled={generating}
            className="px-8 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 text-lg font-medium transition-colors"
          >
            {generating ? (
              <span className="flex items-center">
                <svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" fill="none" viewBox="0 0 24 24">
                  <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
                  <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
                </svg>
                Generating...
              </span>
            ) : (
              'Generate Keys'
            )}
          </button>
          <p className="text-gray-500 text-sm mt-4">
            Requires Chrome 113+, Firefox 130+, or Safari 17.4+
          </p>
          <p className="text-gray-400 text-xs mt-1">
            Alternatively, use the CLI: <code className="bg-gray-100 px-1 rounded">npx ts-node src/cli/generatePartnerKeys.ts</code>
          </p>
        </div>
      ) : (
        <div className="space-y-6">
          {/* Step 1: Save private keys */}
          <div className="bg-red-50 border border-red-200 rounded-lg p-6">
            <div className="flex justify-between items-center mb-4">
              <div>
                <h3 className="text-lg font-medium text-gray-900">Step 1: Save Your Private Keys</h3>
                <p className="text-red-700 text-sm mt-1">
                  Download and store these securely. They cannot be recovered.
                </p>
              </div>
              <div className="flex flex-col gap-2 flex-shrink-0">
                <button
                  onClick={downloadPrivateKeys}
                  className="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 text-sm flex items-center"
                >
                  <svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
                  </svg>
                  Download Private Keys (JSON)
                </button>
                <button
                  onClick={downloadDpopPem}
                  className="px-4 py-2 bg-red-700 text-white rounded-lg hover:bg-red-800 text-sm flex items-center"
                  title="DPoP private key as a standalone .pem file (PKCS#8)"
                >
                  <svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
                  </svg>
                  Download DPoP PEM
                </button>
              </div>
            </div>

            <div className="space-y-4">
              <div>
                <label className="text-sm text-gray-500">X25519 Private Key (scope DEK decryption)</label>
                <div className="flex items-center mt-1">
                  <code className="flex-1 p-3 bg-white rounded border font-mono text-sm break-all text-red-600">
                    {keys.x25519.privateBase64}
                  </code>
                  <CopyButton text={keys.x25519.privateBase64} field="x25519Private" />
                </div>
              </div>

              <div>
                <label className="text-sm text-gray-500">ES256 DPoP Private Key JWK (token binding)</label>
                <div className="flex items-center mt-1">
                  <code className="flex-1 p-3 bg-white rounded border font-mono text-xs break-all text-red-600">
                    {JSON.stringify(keys.dpop.privateJwk)}
                  </code>
                  <CopyButton text={JSON.stringify(keys.dpop.privateJwk)} field="dpopPrivate" />
                </div>
              </div>
            </div>

            <label className="flex items-center mt-4 cursor-pointer">
              <input
                type="checkbox"
                checked={privateSaved}
                onChange={(e) => setPrivateSaved(e.target.checked)}
                className="rounded border-gray-300 text-blue-600 mr-2"
              />
              <span className="text-sm text-gray-700">
                I have securely saved my private keys
              </span>
            </label>
          </div>

          {/* Step 2: Review public keys */}
          <div className="bg-gray-50 rounded-lg p-6">
            <h3 className="text-lg font-medium text-gray-900 mb-4">Step 2: Review Public Keys</h3>
            <p className="text-gray-600 text-sm mb-4">
              These public keys will be registered with v0uch.me when you click "Submit" below.
            </p>

            <div className="space-y-4">
              <div>
                <label className="text-sm text-gray-500">X25519 Public Key (submitted to v0uchme)</label>
                <div className="flex items-center mt-1">
                  <code className="flex-1 p-3 bg-white rounded border font-mono text-sm break-all">
                    {keys.x25519.publicBase64}
                  </code>
                  <CopyButton text={keys.x25519.publicBase64} field="x25519Public" />
                </div>
              </div>

              <div>
                <label className="text-sm text-gray-500">ES256 DPoP Public Key JWK (auto-sent in DPoP proof headers)</label>
                <div className="flex items-center mt-1">
                  <code className="flex-1 p-3 bg-white rounded border font-mono text-xs break-all">
                    {JSON.stringify(keys.dpop.publicJwk)}
                  </code>
                  <CopyButton text={JSON.stringify(keys.dpop.publicJwk)} field="dpopPublic" />
                </div>
                <p className="text-xs text-gray-400 mt-1">
                  Thumbprint: {keys.dpop.thumbprint}
                </p>
              </div>
            </div>
          </div>

          {/* Step 3: Submit */}
          <div className="text-center">
            <button
              onClick={handleSubmitPublicKey}
              disabled={!privateSaved || submitting}
              className="px-8 py-3 bg-green-600 text-white rounded-lg hover:bg-green-700 disabled:opacity-50 text-lg font-medium transition-colors"
            >
              {submitting ? (
                <span className="flex items-center">
                  <svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" fill="none" viewBox="0 0 24 24">
                    <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
                    <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
                  </svg>
                  Submitting...
                </span>
              ) : (
                'Submit Public Key to v0uch.me'
              )}
            </button>
            {!privateSaved && (
              <p className="text-amber-600 text-sm mt-2">
                Please confirm you have saved your private keys before submitting.
              </p>
            )}
          </div>
        </div>
      )}
    </div>
  );
};
