/**
 * EncryptID Login Button Component
 *
 * A customizable login button that handles WebAuthn authentication
 * and key derivation. Can be embedded in any r-ecosystem app.
 */

import {
  registerPasskey,
  authenticatePasskey,
  base64urlToBuffer,
  bufferToBase64url,
  detectCapabilities,
  startConditionalUI,
  WebAuthnCapabilities,
} from '../webauthn';
import { getKeyManager, EncryptIDKeyManager } from '../key-derivation';
import { getSessionManager, AuthLevel } from '../session';
import { getDocBridge, resetDocBridge } from '../../../shared/local-first/encryptid-bridge';
import { getVaultManager, resetVaultManager } from '../vault';
import { syncWalletsOnLogin } from '../wallet-sync';

// ============================================================================
// KNOWN ACCOUNTS (localStorage)
// ============================================================================

const KNOWN_ACCOUNTS_KEY = 'encryptid-known-accounts';
const ENCRYPTID_AUTH = ''; // same-origin — avoids cross-origin issues on Safari

interface KnownAccount {
  username: string;
  displayName?: string;
}

function getKnownAccounts(): KnownAccount[] {
  try {
    return JSON.parse(localStorage.getItem(KNOWN_ACCOUNTS_KEY) || '[]');
  } catch { return []; }
}

function addKnownAccount(account: KnownAccount): void {
  const accounts = getKnownAccounts().filter(a => a.username !== account.username);
  accounts.unshift(account);  // most recent first
  localStorage.setItem(KNOWN_ACCOUNTS_KEY, JSON.stringify(accounts));
}

function removeKnownAccount(username: string): void {
  const accounts = getKnownAccounts().filter(a => a.username !== username);
  localStorage.setItem(KNOWN_ACCOUNTS_KEY, JSON.stringify(accounts));
}

function escapeHtml(s: string): string {
  return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}

// ============================================================================
// STYLES
// ============================================================================

const styles = `
  :host {
    --eid-primary: #06b6d4;
    --eid-primary-hover: #0891b2;
    --eid-bg: #0f172a;
    --eid-bg-hover: #1e293b;
    --eid-text: #f1f5f9;
    --eid-text-secondary: #94a3b8;
    --eid-radius: 8px;
    --eid-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.3);

    display: inline-block;
    font-family: system-ui, -apple-system, sans-serif;
  }

  .login-container {
    position: relative;
  }

  .login-btn {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 24px;
    background: var(--eid-primary);
    color: white;
    border: none;
    border-radius: var(--eid-radius);
    font-size: 1rem;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.2s;
    box-shadow: var(--eid-shadow);
  }

  .login-btn:hover {
    background: var(--eid-primary-hover);
    transform: translateY(-1px);
  }

  .login-btn:disabled {
    opacity: 0.6;
    cursor: not-allowed;
    transform: none;
  }

  .login-btn.outline {
    background: transparent;
    border: 2px solid var(--eid-primary);
    color: var(--eid-primary);
  }

  .login-btn.outline:hover {
    background: var(--eid-primary);
    color: white;
  }

  .login-btn.small {
    padding: 8px 16px;
    font-size: 0.875rem;
  }

  .login-btn.large {
    padding: 16px 32px;
    font-size: 1.125rem;
  }

  .passkey-icon {
    width: 24px;
    height: 24px;
  }

  .login-btn.small .passkey-icon {
    width: 18px;
    height: 18px;
  }

  .login-btn.large .passkey-icon {
    width: 28px;
    height: 28px;
  }

  .user-info {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 16px;
    background: var(--eid-bg);
    border-radius: var(--eid-radius);
    color: var(--eid-text);
  }

  .user-avatar {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--eid-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: 0.875rem;
  }

  .user-details {
    flex: 1;
    min-width: 0;
  }

  .user-did {
    font-size: 0.75rem;
    color: var(--eid-text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 150px;
  }

  .auth-level {
    font-size: 0.625rem;
    padding: 2px 6px;
    border-radius: 4px;
    background: var(--eid-bg-hover);
  }

  .auth-level.elevated {
    background: rgba(34, 197, 94, 0.2);
    color: #22c55e;
  }

  .auth-level.standard {
    background: rgba(234, 179, 8, 0.2);
    color: #eab308;
  }

  .logout-btn {
    padding: 6px 12px;
    background: transparent;
    border: 1px solid var(--eid-text-secondary);
    border-radius: var(--eid-radius);
    color: var(--eid-text-secondary);
    font-size: 0.75rem;
    cursor: pointer;
    transition: all 0.2s;
  }

  .logout-btn:hover {
    border-color: #ef4444;
    color: #ef4444;
  }

  .dropdown {
    position: absolute;
    top: 100%;
    right: 0;
    margin-top: 8px;
    background: var(--eid-bg);
    border-radius: var(--eid-radius);
    box-shadow: var(--eid-shadow);
    min-width: 200px;
    z-index: 100;
    overflow: hidden;
  }

  .dropdown-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
    color: var(--eid-text);
    text-decoration: none;
    cursor: pointer;
    transition: background 0.2s;
  }

  .dropdown-item:hover {
    background: var(--eid-bg-hover);
  }

  .dropdown-item.danger {
    color: #ef4444;
  }

  .dropdown-divider {
    height: 1px;
    background: #334155;
    margin: 4px 0;
  }

  .loading-spinner {
    width: 20px;
    height: 20px;
    border: 2px solid transparent;
    border-top-color: currentColor;
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
  }

  @keyframes spin {
    to { transform: rotate(360deg); }
  }

  .hidden {
    display: none;
  }

  .account-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 200px;
  }

  .account-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 16px;
    background: var(--eid-bg);
    border: 1px solid transparent;
    border-radius: var(--eid-radius);
    color: var(--eid-text);
    cursor: pointer;
    transition: all 0.2s;
    font-size: 0.9rem;
    font-family: inherit;
    text-align: left;
    width: 100%;
    box-sizing: border-box;
  }

  .account-item:hover {
    background: var(--eid-bg-hover);
    border-color: var(--eid-primary);
  }

  .account-item .account-avatar {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--eid-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: 0.75rem;
    color: white;
    flex-shrink: 0;
  }

  .account-item .account-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .alt-action {
    display: block;
    margin-top: 8px;
    padding: 0;
    background: none;
    border: none;
    color: var(--eid-text-secondary);
    font-size: 0.8rem;
    cursor: pointer;
    text-align: center;
    width: 100%;
    font-family: inherit;
  }

  .alt-action:hover {
    color: var(--eid-primary);
    text-decoration: underline;
  }

  .username-form {
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-width: 200px;
  }

  .username-input {
    padding: 10px 14px;
    background: var(--eid-bg);
    border: 1px solid var(--eid-text-secondary);
    border-radius: var(--eid-radius);
    color: var(--eid-text);
    font-size: 0.95rem;
    font-family: inherit;
    outline: none;
    transition: border-color 0.2s;
  }

  .username-input:focus {
    border-color: var(--eid-primary);
  }

  .username-input::placeholder {
    color: var(--eid-text-secondary);
    opacity: 0.7;
  }

  .email-fallback-section {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid rgba(255,255,255,0.1);
  }

  .email-sent-msg {
    font-size: 0.85rem;
    color: #22c55e;
    text-align: center;
    padding: 8px;
  }

  .error-msg {
    font-size: 0.85rem;
    color: #f87171;
    background: rgba(248,113,113,0.1);
    border: 1px solid rgba(248,113,113,0.25);
    border-radius: var(--eid-radius);
    padding: 10px 14px;
    margin-bottom: 8px;
    line-height: 1.45;
  }

  .compat-note {
    font-size: 0.85rem;
    color: #fbbf24;
    background: rgba(251,191,36,0.1);
    border: 1px solid rgba(251,191,36,0.25);
    border-radius: var(--eid-radius);
    padding: 10px 14px;
    margin-bottom: 8px;
    line-height: 1.45;
    text-align: center;
  }

  .passphrase-overlay {
    position: fixed;
    inset: 0;
    z-index: 10000;
    background: rgba(0,0,0,0.6);
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .passphrase-modal {
    background: var(--eid-bg, #0f172a);
    border: 1px solid rgba(255,255,255,0.15);
    border-radius: 12px;
    padding: 24px;
    max-width: 380px;
    width: 90vw;
    box-shadow: 0 8px 32px rgba(0,0,0,0.5);
  }
`;

// ============================================================================
// PASSKEY SVG ICON
// ============================================================================

const PASSKEY_ICON = `
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="passkey-icon">
  <circle cx="12" cy="10" r="3"/>
  <path d="M12 13v8"/>
  <path d="M9 18h6"/>
  <circle cx="12" cy="10" r="7"/>
</svg>
`;

// ============================================================================
// WEB COMPONENT
// ============================================================================

export class EncryptIDLoginButton extends HTMLElement {
  private shadow: ShadowRoot;
  private loading: boolean = false;
  private showDropdown: boolean = false;
  private showEmailFallback: boolean = false;
  private emailSent: boolean = false;
  private errorMessage: string = '';
  private showPassphrasePrompt: boolean = false;
  private passphraseResolver: ((passphrase: string) => void) | null = null;
  private capabilities: WebAuthnCapabilities | null = null;

  // Configurable attributes
  static get observedAttributes() {
    return ['size', 'variant', 'label', 'show-user'];
  }

  constructor() {
    super();
    this.shadow = this.attachShadow({ mode: 'open' });
  }

  private _onSessionRevoked = () => {
    // Another browser session logged out — clear local state and re-render
    const keyManager = getKeyManager();
    keyManager.clear();
    resetVaultManager();
    resetDocBridge();
    this.dispatchEvent(new CustomEvent('logout', { bubbles: true }));
    this.render();
  };

  async connectedCallback() {
    // Listen for cross-session logout revocation
    window.addEventListener('encryptid:session-revoked', this._onSessionRevoked);

    // Detect capabilities
    this.capabilities = await detectCapabilities();

    // Start conditional UI if available
    if (this.capabilities.conditionalUI) {
      this.startConditionalAuth();
    }

    this.render();

    // Close dropdown on outside click
    document.addEventListener('click', (e) => {
      if (!this.contains(e.target as Node)) {
        this.showDropdown = false;
        this.render();
      }
    });
  }

  attributeChangedCallback() {
    this.render();
  }

  private get size(): 'small' | 'medium' | 'large' {
    return (this.getAttribute('size') as any) || 'medium';
  }

  private get variant(): 'primary' | 'outline' {
    return (this.getAttribute('variant') as any) || 'primary';
  }

  private get label(): string {
    return this.getAttribute('label') || 'Sign in with Passkey';
  }

  private get showUser(): boolean {
    return this.hasAttribute('show-user');
  }

  disconnectedCallback() {
    window.removeEventListener('encryptid:session-revoked', this._onSessionRevoked);
  }

  private render() {
    const session = getSessionManager();
    const isLoggedIn = session.isValid();
    const did = session.getDID();
    const authLevel = session.getAuthLevel();

    this.shadow.innerHTML = `
      <style>${styles}</style>
      <div class="login-container">
        ${isLoggedIn && this.showUser ? this.renderUserInfo(did!, authLevel) : this.renderLoginButton()}
        ${this.showDropdown ? this.renderDropdown() : ''}
      </div>
      ${this.showPassphrasePrompt ? this.renderPassphraseModal() : ''}
    `;

    this.attachEventListeners();
  }

  private renderLoginButton(): string {
    const sizeClass = this.size === 'medium' ? '' : this.size;
    const variantClass = this.variant === 'primary' ? '' : this.variant;

    if (this.loading) {
      return `
        <button class="login-btn ${sizeClass} ${variantClass}" disabled>
          <div class="loading-spinner"></div>
          <span>Authenticating...</span>
        </button>
      `;
    }

    // WebAuthn unavailable — show email-only login
    if (this.capabilities?.webauthn === false) {
      return this.renderEmailOnlyFallback(sizeClass, variantClass);
    }

    const errorDiv = this.errorMessage
      ? `<div class="error-msg">${escapeHtml(this.errorMessage)}</div>`
      : '';

    const accounts = getKnownAccounts();

    // No known accounts → passkey-first button + email fallback
    if (accounts.length === 0) {
      const emailSection = this.showEmailFallback ? (this.emailSent
        ? `<div class="email-fallback-section"><div class="email-sent-msg">Login link sent! Check your inbox.</div></div>`
        : `<div class="email-fallback-section">
            <input class="username-input" type="email" placeholder="you@example.com" data-email-input />
            <button class="login-btn small ${variantClass}" data-action="send-magic-link">Send magic link</button>
          </div>`) : '';

      return `
        <div class="username-form">
          ${errorDiv}
          <button class="login-btn ${sizeClass} ${variantClass}" data-action="passkey-login">
            ${PASSKEY_ICON}
            <span>${this.label}</span>
          </button>
          ${this.showEmailFallback ? '' : `<button class="alt-action" data-action="show-email">No passkey? Sign in with email</button>`}
          ${emailSection}
        </div>
      `;
    }

    // 1 known account → "Sign in as [username]" + alt link
    if (accounts.length === 1) {
      const name = escapeHtml(accounts[0].displayName || accounts[0].username);
      return `
        ${errorDiv}
        <button class="login-btn ${sizeClass} ${variantClass}" data-username="${escapeHtml(accounts[0].username)}">
          ${PASSKEY_ICON}
          <span>Sign in as ${name}</span>
        </button>
        <button class="alt-action" data-action="different-account">Use a different account</button>
      `;
    }

    // Multiple accounts → picker list
    const items = accounts.map(a => {
      const name = escapeHtml(a.displayName || a.username);
      const initial = escapeHtml((a.displayName || a.username).slice(0, 2).toUpperCase());
      return `
        <button class="account-item" data-username="${escapeHtml(a.username)}">
          <span class="account-avatar">${initial}</span>
          <span class="account-name">${name}</span>
        </button>
      `;
    }).join('');

    return `
      ${errorDiv}
      <div class="account-list">
        ${items}
      </div>
      <button class="alt-action" data-action="different-account">Use a different account</button>
    `;
  }

  private renderUserInfo(did: string, authLevel: AuthLevel): string {
    const shortDID = did.slice(0, 20) + '...' + did.slice(-8);
    const initial = did.slice(8, 10).toUpperCase();
    const levelName = AuthLevel[authLevel].toLowerCase();

    return `
      <div class="user-info" style="cursor: pointer;">
        <div class="user-avatar">${initial}</div>
        <div class="user-details">
          <div class="user-did">${shortDID}</div>
          <span class="auth-level ${levelName}">${levelName}</span>
        </div>
      </div>
    `;
  }

  private renderDropdown(): string {
    return `
      <div class="dropdown">
        <div class="dropdown-item" data-action="profile">
          👤 Profile
        </div>
        <div class="dropdown-item" data-action="recovery">
          🛡️ Recovery Settings
        </div>
        <div class="dropdown-item" data-action="upgrade">
          🔐 Upgrade Auth Level
        </div>
        <div class="dropdown-divider"></div>
        <div class="dropdown-item danger" data-action="logout">
          🚪 Sign Out
        </div>
      </div>
    `;
  }

  /**
   * Show passphrase modal and return the entered passphrase.
   * Resolves when user submits; returns empty string if dismissed.
   */
  private promptPassphrase(): Promise<string> {
    return new Promise((resolve) => {
      this.passphraseResolver = resolve;
      this.showPassphrasePrompt = true;
      this.loading = false; // allow modal interaction
      this.render();
    });
  }

  private renderPassphraseModal(): string {
    return `
      <div class="passphrase-overlay">
        <div class="passphrase-modal">
          <div class="compat-note" style="margin-bottom:12px">
            Your browser doesn't support hardware encryption.<br>
            Enter your encryption passphrase to unlock your documents.
          </div>
          <input class="username-input" type="password" placeholder="Encryption passphrase" data-passphrase-input autocomplete="off" />
          <button class="login-btn small" data-action="submit-passphrase" style="width:100%;justify-content:center;margin-top:8px">Unlock</button>
          <div style="font-size:0.75rem;color:var(--eid-text-secondary);text-align:center;margin-top:8px">
            This passphrase encrypts your documents. It's never sent to any server.
          </div>
        </div>
      </div>`;
  }

  private renderEmailOnlyFallback(sizeClass: string, variantClass: string): string {
    if (this.emailSent) {
      return `
        <div class="username-form">
          <div class="compat-note">Passkeys are not available in this browser.</div>
          <div class="email-fallback-section"><div class="email-sent-msg">Login link sent! Check your inbox.</div></div>
        </div>`;
    }
    return `
      <div class="username-form">
        <div class="compat-note">Passkeys are not available in this browser.</div>
        <div class="email-fallback-section">
          <input class="username-input" type="email" placeholder="you@example.com" data-email-input />
          <button class="login-btn small ${variantClass}" data-action="send-magic-link">Sign in with email</button>
        </div>
      </div>`;
  }

  private attachEventListeners() {
    const session = getSessionManager();
    const isLoggedIn = session.isValid();

    if (isLoggedIn && this.showUser) {
      // User info click - toggle dropdown
      this.shadow.querySelector('.user-info')?.addEventListener('click', () => {
        this.showDropdown = !this.showDropdown;
        this.render();
      });

      // Dropdown actions
      this.shadow.querySelectorAll('.dropdown-item').forEach(item => {
        item.addEventListener('click', (e) => {
          e.stopPropagation();
          const action = (item as HTMLElement).dataset.action;
          this.handleDropdownAction(action!);
        });
      });
    } else {
      // Passkey-first button (no known accounts → unscoped auth)
      this.shadow.querySelector('[data-action="passkey-login"]')?.addEventListener('click', () => {
        this.handleLogin();
      });

      // "No passkey? Sign in with email" toggle
      this.shadow.querySelector('[data-action="show-email"]')?.addEventListener('click', () => {
        this.showEmailFallback = true;
        this.render();
      });

      // Send magic link button + enter key
      this.shadow.querySelector('[data-action="send-magic-link"]')?.addEventListener('click', () => {
        this.sendMagicLink();
      });
      const emailInput = this.shadow.querySelector('[data-email-input]') as HTMLInputElement;
      emailInput?.addEventListener('keydown', (e) => {
        if ((e as KeyboardEvent).key === 'Enter') this.sendMagicLink();
      });

      // Login button with scoped username (1 known account)
      const loginBtn = this.shadow.querySelector('.login-btn:not([data-action])');
      if (loginBtn) {
        loginBtn.addEventListener('click', () => {
          const username = (loginBtn as HTMLElement).dataset.username;
          this.handleLogin(username);
        });
      }

      // Account list items (multiple accounts)
      this.shadow.querySelectorAll('.account-item').forEach(item => {
        item.addEventListener('click', () => {
          const username = (item as HTMLElement).dataset.username;
          if (username) this.handleLogin(username);
        });
      });

      // "Use a different account" → unscoped auth
      this.shadow.querySelector('[data-action="different-account"]')?.addEventListener('click', () => {
        this.handleLogin();
      });
    }

    // Passphrase modal submit
    this.shadow.querySelector('[data-action="submit-passphrase"]')?.addEventListener('click', () => {
      const input = this.shadow.querySelector('[data-passphrase-input]') as HTMLInputElement;
      const passphrase = input?.value || '';
      if (passphrase && this.passphraseResolver) {
        this.showPassphrasePrompt = false;
        this.passphraseResolver(passphrase);
        this.passphraseResolver = null;
      }
    });
    const ppInput = this.shadow.querySelector('[data-passphrase-input]') as HTMLInputElement;
    ppInput?.addEventListener('keydown', (e) => {
      if ((e as KeyboardEvent).key === 'Enter') {
        this.shadow.querySelector<HTMLButtonElement>('[data-action="submit-passphrase"]')?.click();
      }
    });
    // Auto-focus passphrase input when modal is shown
    ppInput?.focus();
  }

  /**
   * Fetch scoped credentials from the auth server for a given username.
   * Returns PublicKeyCredentialDescriptor[] with transports, or undefined to fall back to unscoped.
   */
  private async fetchScopedCredentials(username: string): Promise<PublicKeyCredentialDescriptor[] | undefined> {
    try {
      const res = await fetch(`${ENCRYPTID_AUTH}/api/auth/start`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username }),
      });
      if (!res.ok) return undefined;
      const body = await res.json().catch(() => null);
      if (!body) return undefined;
      const { options, userFound } = body;
      if (!userFound || !options.allowCredentials?.length) return undefined;
      return options.allowCredentials.map((c: any) => ({
        type: 'public-key' as const,
        id: base64urlToBuffer(c.id),
        ...(c.transports?.length ? { transports: c.transports } : {}),
      }));
    } catch {
      return undefined;
    }
  }

  private async handleLogin(username?: string) {
    if (this.loading) return;

    this.errorMessage = '';
    this.loading = true;
    this.render();

    try {
      // If a username was selected, scope the passkey prompt to that user's credentials
      let scopedCredentials: PublicKeyCredentialDescriptor[] | undefined;
      if (username) {
        scopedCredentials = await this.fetchScopedCredentials(username);
        // If user not found on server, fall back to unscoped (still let them try)
      }

      // Authenticate — scoped if we have credentials, unscoped otherwise
      const result = await authenticatePasskey(scopedCredentials);

      // Initialize key manager with PRF output
      const keyManager = getKeyManager();
      if (result.prfOutput) {
        await keyManager.initFromPRF(result.prfOutput);
        // Initialize doc encryption bridge for local-first encrypted storage
        await getDocBridge().initFromAuth(result.prfOutput);
        // Load encrypted account vault in background
        const docCrypto = getDocBridge().getDocCrypto();
        if (docCrypto) {
          getVaultManager(docCrypto).load()
            .then(() => syncWalletsOnLogin(docCrypto))
            .catch(err => console.warn('Vault load failed:', err));
        }
      }

      // Get derived keys
      const keys = await keyManager.getKeys();

      // Create session first (need JWT for salt endpoint)
      const sessionManager = getSessionManager();
      await sessionManager.createSession(result, keys.did, {
        encrypt: true,
        sign: true,
        wallet: !!keys.eoaAddress,
      });

      // Non-PRF path: passphrase-based encryption for Firefox and other browsers
      if (!result.prfOutput) {
        const session = sessionManager.getSession();
        if (session?.token) {
          try {
            const saltRes = await fetch(`${ENCRYPTID_AUTH}/api/account/passphrase-salt`, {
              headers: { 'Authorization': `Bearer ${session.token}` },
            });
            if (saltRes.ok) {
              const { salt } = await saltRes.json();
              // Prompt for passphrase
              const passphrase = await this.promptPassphrase();
              if (passphrase) {
                const saltBytes = base64urlToBuffer(salt);
                await keyManager.initFromPassphrase(passphrase, new Uint8Array(saltBytes));
                // Get raw key material for DocBridge
                const rawKey = await keyManager.getKeyMaterial();
                await getDocBridge().initFromKey(new Uint8Array(rawKey));
                // Load vault
                const docCrypto = getDocBridge().getDocCrypto();
                if (docCrypto) {
                  getVaultManager(docCrypto).load()
                    .then(() => syncWalletsOnLogin(docCrypto))
                    .catch(err => console.warn('Vault load failed:', err));
                }
              }
            }
            // 404 = user doesn't have a passphrase salt yet (new registration, will be set up)
          } catch (e) {
            console.warn('Passphrase salt fetch failed:', e);
          }
        }
      }

      // Remember this account for next time
      // Use the username from JWT claims if available, otherwise the one selected
      const session = sessionManager.getSession();
      const resolvedUsername = session?.claims.username || username || keys.did.slice(0, 16);
      addKnownAccount({ username: resolvedUsername });

      // Dispatch success event
      this.dispatchEvent(new CustomEvent('login-success', {
        detail: {
          did: keys.did,
          credentialId: result.credentialId,
          prfAvailable: !!result.prfOutput,
        },
        bubbles: true,
      }));

    } catch (error: any) {
      const name = error?.name || '';
      switch (name) {
        case 'AbortError':
          // Conditional UI abort — silent, expected
          break;
        case 'NotAllowedError':
          this.errorMessage = 'Passkey dismissed. Try again, or sign in with email.';
          this.showEmailFallback = true;
          this.dispatchEvent(new CustomEvent('login-register-needed', { bubbles: true }));
          break;
        case 'NotSupportedError':
          this.errorMessage = 'Passkeys are not supported on this browser. Sign in with email.';
          this.showEmailFallback = true;
          break;
        case 'InvalidStateError':
          this.errorMessage = 'This passkey is already registered. Try signing in instead.';
          break;
        case 'SecurityError':
          this.errorMessage = 'Security error: this page cannot use your passkey.';
          break;
        default:
          this.errorMessage = error?.message || 'Authentication failed. Please try again.';
          this.dispatchEvent(new CustomEvent('login-error', {
            detail: { error: error?.message },
            bubbles: true,
          }));
          break;
      }
    } finally {
      this.loading = false;
      this.render();
    }
  }

  private async sendMagicLink() {
    const emailInput = this.shadow.querySelector('[data-email-input]') as HTMLInputElement;
    const email = emailInput?.value.trim();
    if (!email || !email.includes('@')) {
      emailInput?.focus();
      return;
    }
    try {
      await fetch(`${ENCRYPTID_AUTH}/api/auth/magic-link`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email }),
      });
      this.emailSent = true;
      this.render();
    } catch (error: any) {
      console.error('Magic link failed:', error);
    }
  }

  private async handleDropdownAction(action: string) {
    this.showDropdown = false;

    switch (action) {
      case 'profile':
        this.dispatchEvent(new CustomEvent('navigate', {
          detail: { path: '/profile' },
          bubbles: true,
        }));
        break;

      case 'recovery':
        this.dispatchEvent(new CustomEvent('navigate', {
          detail: { path: '/recovery' },
          bubbles: true,
        }));
        break;

      case 'upgrade':
        await this.handleUpgradeAuth();
        break;

      case 'logout':
        this.handleLogout();
        break;
    }

    this.render();
  }

  private async handleUpgradeAuth() {
    try {
      const result = await authenticatePasskey();

      const sessionManager = getSessionManager();
      sessionManager.upgradeAuthLevel(AuthLevel.ELEVATED);

      this.dispatchEvent(new CustomEvent('auth-upgraded', {
        detail: { level: AuthLevel.ELEVATED },
        bubbles: true,
      }));
    } catch (error) {
      console.error('Failed to upgrade auth:', error);
    }
  }

  private handleLogout() {
    const sessionManager = getSessionManager();
    // Keep known account in localStorage so the picker shows on next login
    sessionManager.clearSession();

    const keyManager = getKeyManager();
    keyManager.clear();

    // Clear vault and doc encryption bridge key material
    resetVaultManager();
    resetDocBridge();

    this.dispatchEvent(new CustomEvent('logout', { bubbles: true }));
    this.render();
  }

  private async startConditionalAuth() {
    // Start listening for conditional UI (passkey autofill)
    try {
      const result = await startConditionalUI();
      if (result) {
        // User selected a passkey from autofill
        const keyManager = getKeyManager();
        if (result.prfOutput) {
          await keyManager.initFromPRF(result.prfOutput);
          await getDocBridge().initFromAuth(result.prfOutput);
          // Load encrypted account vault in background
          const docCrypto = getDocBridge().getDocCrypto();
          if (docCrypto) {
            getVaultManager(docCrypto).load()
              .then(() => syncWalletsOnLogin(docCrypto))
              .catch(err => console.warn('Vault load failed:', err));
          }
        }

        const keys = await keyManager.getKeys();

        const sessionManager = getSessionManager();
        await sessionManager.createSession(result, keys.did, {
          encrypt: true,
          sign: true,
          wallet: !!keys.eoaAddress,
        });

        // Remember this account
        const sess = sessionManager.getSession();
        if (sess?.claims.username) {
          addKnownAccount({ username: sess.claims.username });
        }

        this.dispatchEvent(new CustomEvent('login-success', {
          detail: {
            did: keys.did,
            credentialId: result.credentialId,
            prfAvailable: !!result.prfOutput,
            viaConditionalUI: true,
          },
          bubbles: true,
        }));

        this.render();
      }
    } catch (error) {
      // Conditional UI cancelled or not available
      console.log('Conditional UI not completed:', error);
    }
  }

  /**
   * Public method to trigger registration flow
   */
  async register(username: string, displayName: string): Promise<void> {
    this.loading = true;
    this.render();

    try {
      const credential = await registerPasskey(username, displayName);

      // Remember this account
      addKnownAccount({ username, displayName });

      this.dispatchEvent(new CustomEvent('register-success', {
        detail: {
          credentialId: credential.credentialId,
          prfSupported: credential.prfSupported,
        },
        bubbles: true,
      }));

      // Auto-login after registration
      await this.handleLogin(username);

      // If PRF not supported, set up passphrase-based encryption
      if (!credential.prfSupported) {
        const session = getSessionManager().getSession();
        if (session?.token) {
          const passphrase = await this.promptPassphrase();
          if (passphrase) {
            const salt = EncryptIDKeyManager.generateSalt();
            const saltB64 = bufferToBase64url(salt);
            // Store salt server-side
            await fetch(`${ENCRYPTID_AUTH}/api/account/passphrase-salt`, {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${session.token}`,
              },
              body: JSON.stringify({ salt: saltB64 }),
            });
            // Init encryption from passphrase
            const keyManager = getKeyManager();
            await keyManager.initFromPassphrase(passphrase, salt);
            const rawKey = await keyManager.getKeyMaterial();
            await getDocBridge().initFromKey(new Uint8Array(rawKey));
          }
        }
      }
    } catch (error: any) {
      this.dispatchEvent(new CustomEvent('register-error', {
        detail: { error: error.message },
        bubbles: true,
      }));
    } finally {
      this.loading = false;
      this.render();
    }
  }
}

// Register the custom element
customElements.define('encryptid-login', EncryptIDLoginButton);
