/**
 * EncryptID Auth Store for rmaps-online
 *
 * Optional authentication — anonymous access remains the default.
 * When authenticated, the user gets a persistent DID-based identity.
 */

import { create } from 'zustand';
import { persist } from 'zustand/middleware';

const ENCRYPTID_SERVER = process.env.NEXT_PUBLIC_ENCRYPTID_SERVER_URL || 'https://encryptid.jeffemmett.com';

interface AuthState {
  /** Whether the user is authenticated via EncryptID */
  isAuthenticated: boolean;
  /** EncryptID JWT token */
  token: string | null;
  /** User's DID (persistent identity) */
  did: string | null;
  /** Username from EncryptID */
  username: string | null;
  /** Whether auth is loading */
  loading: boolean;

  /** Authenticate with EncryptID passkey */
  login: () => Promise<void>;
  /** Register a new EncryptID passkey */
  register: (username: string) => Promise<void>;
  /** Clear auth state */
  logout: () => void;
}

function toBase64url(buffer: ArrayBuffer): string {
  return btoa(String.fromCharCode(...new Uint8Array(buffer)))
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=+$/, '');
}

function fromBase64url(str: string): Uint8Array {
  return Uint8Array.from(atob(str.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0));
}

export const useAuthStore = create<AuthState>()(
  persist(
    (set) => ({
      isAuthenticated: false,
      token: null,
      did: null,
      username: null,
      loading: false,

      login: async () => {
        set({ loading: true });
        try {
          // Step 1: Get auth options
          const startRes = await fetch(`${ENCRYPTID_SERVER}/api/auth/start`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({}),
          });
          const { options } = await startRes.json();

          // Step 2: WebAuthn ceremony
          const publicKeyOptions: PublicKeyCredentialRequestOptions = {
            challenge: fromBase64url(options.challenge).buffer as ArrayBuffer,
            rpId: options.rpId,
            userVerification: options.userVerification as UserVerificationRequirement,
            timeout: options.timeout,
            allowCredentials: options.allowCredentials?.map((c: any) => ({
              type: c.type as PublicKeyCredentialType,
              id: fromBase64url(c.id).buffer as ArrayBuffer,
              transports: c.transports as AuthenticatorTransport[],
            })),
          };

          const assertion = await navigator.credentials.get({ publicKey: publicKeyOptions }) as PublicKeyCredential;
          if (!assertion) throw new Error('Authentication cancelled');

          const response = assertion.response as AuthenticatorAssertionResponse;

          // Step 3: Complete auth
          const completeRes = await fetch(`${ENCRYPTID_SERVER}/api/auth/complete`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
              challenge: options.challenge,
              credential: {
                credentialId: assertion.id,
                authenticatorData: toBase64url(response.authenticatorData),
                clientDataJSON: toBase64url(response.clientDataJSON),
                signature: toBase64url(response.signature),
                userHandle: response.userHandle ? toBase64url(response.userHandle) : null,
              },
            }),
          });

          const result = await completeRes.json();
          if (!result.success) throw new Error(result.error || 'Authentication failed');

          set({
            isAuthenticated: true,
            token: result.token,
            did: result.did,
            username: result.username,
            loading: false,
          });
        } catch (error) {
          set({ loading: false });
          throw error;
        }
      },

      register: async (username: string) => {
        set({ loading: true });
        try {
          // Step 1: Get registration options
          const startRes = await fetch(`${ENCRYPTID_SERVER}/api/register/start`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ username, displayName: username }),
          });
          const { options, userId } = await startRes.json();

          // Step 2: WebAuthn ceremony
          const publicKeyOptions: PublicKeyCredentialCreationOptions = {
            challenge: fromBase64url(options.challenge).buffer as ArrayBuffer,
            rp: options.rp,
            user: {
              id: fromBase64url(options.user.id).buffer as ArrayBuffer,
              name: options.user.name,
              displayName: options.user.displayName,
            },
            pubKeyCredParams: options.pubKeyCredParams,
            authenticatorSelection: options.authenticatorSelection,
            timeout: options.timeout,
            attestation: options.attestation as AttestationConveyancePreference,
          };

          const credential = await navigator.credentials.create({ publicKey: publicKeyOptions }) as PublicKeyCredential;
          if (!credential) throw new Error('Registration cancelled');

          const response = credential.response as AuthenticatorAttestationResponse;

          // Step 3: Complete registration
          const completeRes = await fetch(`${ENCRYPTID_SERVER}/api/register/complete`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
              challenge: options.challenge,
              userId,
              username,
              credential: {
                credentialId: credential.id,
                publicKey: toBase64url(response.getPublicKey?.() || response.attestationObject),
                attestationObject: toBase64url(response.attestationObject),
                clientDataJSON: toBase64url(response.clientDataJSON),
                transports: (response as any).getTransports?.() || [],
              },
            }),
          });

          const result = await completeRes.json();
          if (!result.success) throw new Error(result.error || 'Registration failed');

          set({
            isAuthenticated: true,
            token: result.token,
            did: result.did,
            username,
            loading: false,
          });
        } catch (error) {
          set({ loading: false });
          throw error;
        }
      },

      logout: () => {
        set({
          isAuthenticated: false,
          token: null,
          did: null,
          username: null,
          loading: false,
        });
      },
    }),
    {
      name: 'rmaps-auth',
      partialize: (state) => ({
        isAuthenticated: state.isAuthenticated,
        token: state.token,
        did: state.did,
        username: state.username,
      }),
    }
  )
);
