/**
 * This file is part of the SplendidBear Websites' projects.
 *
 * Copyright (c) 2026 @ www.splendidbear.org
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

import React, { useState, useCallback } from 'react';
import { shape, string } from 'prop-types';

const base64ToArrayBuffer = base64 => {
  const binary = atob(base64.replace(/([-_])/g, m => ('-' === m ? '+' : '/')));
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; i++) {
    bytes[i] = binary.charCodeAt(i);
  }
  return bytes.buffer;
};

const arrayBufferToBase64url = buffer =>
  btoa(String.fromCharCode(...new Uint8Array(buffer)))
    .replace(/([+\/=])/g, m => ('+' === m ? '-' : '/' === m ? '_' : ''));

const credentialToJSON = credential => ({
  id: credential.id,
  rawId: arrayBufferToBase64url(credential.rawId),
  type: credential.type,
  response: {
    authenticatorData: arrayBufferToBase64url(credential.response.authenticatorData),
    clientDataJSON: arrayBufferToBase64url(credential.response.clientDataJSON),
    signature: arrayBufferToBase64url(credential.response.signature),
    userHandle: credential.response.userHandle
      ? arrayBufferToBase64url(credential.response.userHandle)
      : null,
  },
});

const PasskeyLogin = ({ apiRoutes }) => {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');

  const handleLogin = useCallback(async () => {
    setLoading(true);
    setError('');

    try {
      const beginResponse = await fetch(apiRoutes.authenticationBegin, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
      });

      if (!beginResponse.ok) throw new Error('Failed to get authentication options');
      const options = await beginResponse.json();

      const publicKey = {
        ...options,
        challenge: base64ToArrayBuffer(options.challenge),
        allowCredentials: (options.allowCredentials ?? []).map(cred => ({
          ...cred,
          id: base64ToArrayBuffer(cred.id),
        })),
      };

      const credential = await navigator.credentials.get({ publicKey });
      if (!credential) throw new Error('Authentication was cancelled');

      const completeResponse = await fetch(apiRoutes.authenticationComplete, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ credential: credentialToJSON(credential) }),
      });

      if (!completeResponse.ok) {
        const body = await completeResponse.json();
        throw new Error(body.error || 'Authentication failed');
      }

      const result = await completeResponse.json();
      if (result.success) {
        window.location.href = result.redirect || '/';
      }
    } catch (err) {
      setError(err.message);
    }

    setLoading(false);
  }, [apiRoutes]);

  return (
    <>
      <button
        type="button"
        className="auth-passkey-btn"
        onClick={handleLogin}
        disabled={loading}
      >
        <i className={loading ? 'fa fa-spinner fa-spin' : 'fa fa-key'} />
        {loading ? 'Waiting for passkey…' : 'Sign In with Passkey'}
      </button>
      {error && (
        <p className="auth-error" style={{ marginTop: '10px' }}>
          <i className="fa fa-exclamation-triangle" /> {error}
        </p>
      )}
    </>
  );
};

export default PasskeyLogin;

PasskeyLogin.propTypes = {
  apiRoutes: shape({
    authenticationBegin:    string.isRequired,
    authenticationComplete: string.isRequired,
  }).isRequired,
};
