import { Environment, User, DeviceKey, VerificationToken } from './types';

// Generate a cryptographically secure random token
function generateToken(): string {
  const array = new Uint8Array(32);
  crypto.getRandomValues(array);
  return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
}

// Generate a UUID v4
function generateUUID(): string {
  return crypto.randomUUID();
}

// Send email via self-hosted email relay (Mailcow SMTP)
async function sendEmail(
  env: Environment,
  to: string,
  subject: string,
  htmlContent: string
): Promise<boolean> {
  try {
    const relayUrl = env.EMAIL_RELAY_URL;
    const relayKey = env.EMAIL_RELAY_API_KEY;
    if (!relayUrl || !relayKey) {
      console.error('EMAIL_RELAY_URL or EMAIL_RELAY_API_KEY not configured');
      return false;
    }

    const response = await fetch(`${relayUrl}/send`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${relayKey}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        from: env.CRYPTID_EMAIL_FROM || 'enCryptID <noreply@jeffemmett.com>',
        to: [to],
        subject,
        html: htmlContent,
      }),
    });

    if (!response.ok) {
      const errorText = await response.text();
      console.error('Email relay error:', errorText);
      return false;
    }

    const result = await response.json() as { id?: string };
    console.log('Email sent successfully, id:', result.id);
    return true;
  } catch (error) {
    console.error('Email send error:', error);
    return false;
  }
}

// Clean up expired tokens
async function cleanupExpiredTokens(db: D1Database): Promise<void> {
  try {
    await db.prepare(
      "DELETE FROM verification_tokens WHERE expires_at < datetime('now') OR used = 1"
    ).run();
  } catch (error) {
    console.error('Token cleanup error:', error);
  }
}

/**
 * Link an email to an existing CryptID account (Device A)
 * POST /auth/link-email
 * Body: { email, cryptidUsername, publicKey, signature, challenge }
 */
export async function handleLinkEmail(
  request: Request,
  env: Environment
): Promise<Response> {
  try {
    const body = await request.json() as {
      email: string;
      cryptidUsername: string;
      publicKey: string;
      deviceName?: string;
    };

    const { email, cryptidUsername, publicKey, deviceName } = body;

    if (!email || !cryptidUsername || !publicKey) {
      return new Response(JSON.stringify({ error: 'Missing required fields' }), {
        status: 400,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Validate email format
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(email)) {
      return new Response(JSON.stringify({ error: 'Invalid email format' }), {
        status: 400,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    const db = env.CRYPTID_DB;
    if (!db) {
      return new Response(JSON.stringify({ error: 'Database not configured' }), {
        status: 503,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Check if email is already linked to a different account
    const existingUser = await db.prepare(
      'SELECT * FROM users WHERE email = ?'
    ).bind(email).first<User>();

    if (existingUser && existingUser.cryptid_username !== cryptidUsername) {
      return new Response(JSON.stringify({
        error: 'Email already linked to a different enCryptID account'
      }), {
        status: 409,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Check if this public key is already registered
    const existingKey = await db.prepare(
      'SELECT * FROM device_keys WHERE public_key = ?'
    ).bind(publicKey).first<DeviceKey>();

    if (existingKey) {
      // Key already registered, just need to verify email if not done
      if (existingUser && existingUser.email_verified) {
        return new Response(JSON.stringify({
          success: true,
          message: 'Email already verified',
          emailVerified: true
        }), {
          headers: { 'Content-Type': 'application/json' },
        });
      }
    }

    const userId = existingUser?.id || generateUUID();
    const userAgent = request.headers.get('User-Agent') || null;

    // Create or update user
    if (!existingUser) {
      await db.prepare(
        'INSERT INTO users (id, email, cryptid_username, email_verified) VALUES (?, ?, ?, 0)'
      ).bind(userId, email, cryptidUsername).run();
    }

    // Add device key if not exists
    if (!existingKey) {
      await db.prepare(
        'INSERT INTO device_keys (id, user_id, public_key, device_name, user_agent) VALUES (?, ?, ?, ?, ?)'
      ).bind(generateUUID(), userId, publicKey, deviceName || 'Primary Device', userAgent).run();
    }

    // If email not verified, send verification email
    if (!existingUser?.email_verified) {
      // Clean up old tokens
      await cleanupExpiredTokens(db);

      // Create verification token (24 hour expiry)
      const token = generateToken();
      const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString();

      await db.prepare(
        'INSERT INTO verification_tokens (id, email, token, token_type, expires_at) VALUES (?, ?, ?, ?, ?)'
      ).bind(generateUUID(), email, token, 'email_verify', expiresAt).run();

      // Send verification email
      const verifyUrl = `${env.APP_URL || 'https://jeffemmett.com'}/verify-email?token=${token}`;
      const emailSent = await sendEmail(
        env,
        email,
        'Verify your enCryptID email',
        `
        <h2>Verify your enCryptID email</h2>
        <p>Click the link below to verify your email address for enCryptID: <strong>${cryptidUsername}</strong></p>
        <p><a href="${verifyUrl}" style="display: inline-block; padding: 12px 24px; background: #4f46e5; color: white; text-decoration: none; border-radius: 6px;">Verify Email</a></p>
        <p>Or copy this link: ${verifyUrl}</p>
        <p>This link expires in 24 hours.</p>
        <p style="color: #666; font-size: 12px;">If you didn't request this, you can safely ignore this email.</p>
        `
      );

      return new Response(JSON.stringify({
        success: true,
        message: emailSent ? 'Verification email sent' : 'Account created but email failed to send',
        emailVerified: false,
        emailSent
      }), {
        headers: { 'Content-Type': 'application/json' },
      });
    }

    return new Response(JSON.stringify({
      success: true,
      message: 'Email already verified',
      emailVerified: true
    }), {
      headers: { 'Content-Type': 'application/json' },
    });

  } catch (error) {
    console.error('Link email error:', error);
    return new Response(JSON.stringify({ error: 'Internal server error' }), {
      status: 500,
      headers: { 'Content-Type': 'application/json' },
    });
  }
}

/**
 * Verify email via token (clicked from email)
 * GET /auth/verify-email/:token
 */
export async function handleVerifyEmail(
  token: string,
  env: Environment
): Promise<Response> {
  try {
    const db = env.CRYPTID_DB;
    if (!db) {
      return new Response(JSON.stringify({ error: 'Database not configured' }), {
        status: 503,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Find token
    const tokenRecord = await db.prepare(
      "SELECT * FROM verification_tokens WHERE token = ? AND token_type = 'email_verify' AND used = 0 AND expires_at > datetime('now')"
    ).bind(token).first<VerificationToken>();

    if (!tokenRecord) {
      return new Response(JSON.stringify({ error: 'Invalid or expired token' }), {
        status: 400,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Mark email as verified
    await db.prepare(
      "UPDATE users SET email_verified = 1, updated_at = datetime('now') WHERE email = ?"
    ).bind(tokenRecord.email).run();

    // Mark token as used
    await db.prepare(
      'UPDATE verification_tokens SET used = 1 WHERE id = ?'
    ).bind(tokenRecord.id).run();

    // Return success - frontend will redirect
    return new Response(JSON.stringify({
      success: true,
      message: 'Email verified successfully',
      email: tokenRecord.email
    }), {
      headers: { 'Content-Type': 'application/json' },
    });

  } catch (error) {
    console.error('Verify email error:', error);
    return new Response(JSON.stringify({ error: 'Internal server error' }), {
      status: 500,
      headers: { 'Content-Type': 'application/json' },
    });
  }
}

/**
 * Request to link a new device (Device B enters email)
 * POST /auth/request-device-link
 * Body: { email, publicKey, deviceName }
 */
export async function handleRequestDeviceLink(
  request: Request,
  env: Environment
): Promise<Response> {
  try {
    const body = await request.json() as {
      email: string;
      publicKey: string;
      deviceName?: string;
    };

    const { email, publicKey, deviceName } = body;

    if (!email || !publicKey) {
      return new Response(JSON.stringify({ error: 'Missing required fields' }), {
        status: 400,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    const db = env.CRYPTID_DB;
    if (!db) {
      return new Response(JSON.stringify({ error: 'Database not configured' }), {
        status: 503,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Check if email exists and is verified
    const user = await db.prepare(
      'SELECT * FROM users WHERE email = ? AND email_verified = 1'
    ).bind(email).first<User>();

    if (!user) {
      return new Response(JSON.stringify({
        error: 'No verified enCryptID account found for this email'
      }), {
        status: 404,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Check if this public key is already registered
    const existingKey = await db.prepare(
      'SELECT * FROM device_keys WHERE public_key = ?'
    ).bind(publicKey).first<DeviceKey>();

    if (existingKey) {
      return new Response(JSON.stringify({
        success: true,
        message: 'Device already linked',
        cryptidUsername: user.cryptid_username,
        alreadyLinked: true
      }), {
        headers: { 'Content-Type': 'application/json' },
      });
    }

    const userAgent = request.headers.get('User-Agent') || null;

    // Clean up old tokens
    await cleanupExpiredTokens(db);

    // Create device link token (1 hour expiry for security)
    const token = generateToken();
    const expiresAt = new Date(Date.now() + 60 * 60 * 1000).toISOString();

    await db.prepare(
      'INSERT INTO verification_tokens (id, email, token, token_type, public_key, device_name, user_agent, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)'
    ).bind(generateUUID(), email, token, 'device_link', publicKey, deviceName || 'New Device', userAgent, expiresAt).run();

    // Send device link email
    const linkUrl = `${env.APP_URL || 'https://jeffemmett.com'}/link-device?token=${token}`;
    const emailSent = await sendEmail(
      env,
      email,
      'Link new device to your enCryptID',
      `
      <h2>New Device Link Request</h2>
      <p>Someone is trying to link a new device to your enCryptID: <strong>${user.cryptid_username}</strong></p>
      <p><strong>Device:</strong> ${deviceName || 'New Device'}</p>
      <p>If this was you, click the button below to approve:</p>
      <p><a href="${linkUrl}" style="display: inline-block; padding: 12px 24px; background: #4f46e5; color: white; text-decoration: none; border-radius: 6px;">Approve Device</a></p>
      <p>Or copy this link: ${linkUrl}</p>
      <p>This link expires in 1 hour.</p>
      <p style="color: #c00; font-size: 12px;"><strong>If you didn't request this, do not click the link.</strong> Someone may be trying to access your account.</p>
      `
    );

    return new Response(JSON.stringify({
      success: true,
      message: emailSent ? 'Verification email sent to your address' : 'Failed to send email',
      emailSent,
      cryptidUsername: user.cryptid_username
    }), {
      headers: { 'Content-Type': 'application/json' },
    });

  } catch (error) {
    console.error('Request device link error:', error);
    return new Response(JSON.stringify({ error: 'Internal server error' }), {
      status: 500,
      headers: { 'Content-Type': 'application/json' },
    });
  }
}

/**
 * Complete device link (clicked from email on Device B)
 * GET /auth/link-device/:token
 */
export async function handleLinkDevice(
  token: string,
  env: Environment
): Promise<Response> {
  try {
    const db = env.CRYPTID_DB;
    if (!db) {
      return new Response(JSON.stringify({ error: 'Database not configured' }), {
        status: 503,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Find token
    const tokenRecord = await db.prepare(
      "SELECT * FROM verification_tokens WHERE token = ? AND token_type = 'device_link' AND used = 0 AND expires_at > datetime('now')"
    ).bind(token).first<VerificationToken>();

    if (!tokenRecord) {
      return new Response(JSON.stringify({ error: 'Invalid or expired token' }), {
        status: 400,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Get user
    const user = await db.prepare(
      'SELECT * FROM users WHERE email = ?'
    ).bind(tokenRecord.email).first<User>();

    if (!user) {
      return new Response(JSON.stringify({ error: 'User not found' }), {
        status: 404,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Add the new device key
    await db.prepare(
      'INSERT INTO device_keys (id, user_id, public_key, device_name, user_agent) VALUES (?, ?, ?, ?, ?)'
    ).bind(
      generateUUID(),
      user.id,
      tokenRecord.public_key,
      tokenRecord.device_name,
      tokenRecord.user_agent
    ).run();

    // Mark token as used
    await db.prepare(
      'UPDATE verification_tokens SET used = 1 WHERE id = ?'
    ).bind(tokenRecord.id).run();

    return new Response(JSON.stringify({
      success: true,
      message: 'Device linked successfully',
      cryptidUsername: user.cryptid_username,
      email: user.email
    }), {
      headers: { 'Content-Type': 'application/json' },
    });

  } catch (error) {
    console.error('Link device error:', error);
    return new Response(JSON.stringify({ error: 'Internal server error' }), {
      status: 500,
      headers: { 'Content-Type': 'application/json' },
    });
  }
}

/**
 * Send a backup email to set up account on another device
 * POST /api/auth/send-backup-email
 * Body: { email, username }
 *
 * This is called during registration when the user provides an email.
 * It sends an email with a link to set up their account on another device.
 */
export async function handleSendBackupEmail(
  request: Request,
  env: Environment
): Promise<Response> {
  try {
    const body = await request.json() as {
      email: string;
      username: string;
    };

    const { email, username } = body;

    if (!email || !username) {
      return new Response(JSON.stringify({ error: 'Missing required fields' }), {
        status: 400,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Validate email format
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(email)) {
      return new Response(JSON.stringify({ error: 'Invalid email format' }), {
        status: 400,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    const db = env.CRYPTID_DB;
    if (!db) {
      // If no DB, still try to send email (graceful degradation)
      console.log('No database configured, attempting to send email directly');
    }

    // If DB exists, create/update user record
    if (db) {
      // Check if user already exists
      const existingUser = await db.prepare(
        'SELECT * FROM users WHERE cryptid_username = ?'
      ).bind(username).first<User>();

      if (existingUser) {
        // Update email if user exists
        await db.prepare(
          "UPDATE users SET email = ?, updated_at = datetime('now') WHERE cryptid_username = ?"
        ).bind(email, username).run();
      } else {
        // Create new user record
        const userId = crypto.randomUUID();
        await db.prepare(
          'INSERT INTO users (id, email, cryptid_username, email_verified) VALUES (?, ?, ?, 0)'
        ).bind(userId, email, username).run();
      }
    }

    // Send the backup email
    const appUrl = env.APP_URL || 'https://jeffemmett.com';
    const setupUrl = `${appUrl}/setup-device?username=${encodeURIComponent(username)}`;

    const emailSent = await sendEmail(
      env,
      email,
      `Set up enCryptID "${username}" on another device`,
      `
      <!DOCTYPE html>
      <html>
      <head>
        <meta charset="utf-8">
        <style>
          body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
          .header { text-align: center; margin-bottom: 30px; }
          .icon { font-size: 48px; margin-bottom: 10px; }
          h1 { color: #8b5cf6; margin: 0 0 10px 0; }
          .card { background: #f9fafb; border-radius: 12px; padding: 24px; margin: 20px 0; }
          .step { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 16px; }
          .step-number { background: #8b5cf6; color: white; width: 24px; height: 24px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 600; flex-shrink: 0; }
          .button { display: inline-block; padding: 14px 28px; background: linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%); color: white; text-decoration: none; border-radius: 10px; font-weight: 600; margin: 20px 0; }
          .footer { color: #666; font-size: 12px; text-align: center; margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee; }
          .warning { background: #fef3c7; border-radius: 8px; padding: 12px 16px; margin: 20px 0; color: #92400e; font-size: 13px; }
        </style>
      </head>
      <body>
        <div class="header">
          <div class="icon">🔐</div>
          <h1>Welcome to enCryptID</h1>
          <p>Your passwordless account is ready!</p>
        </div>

        <p>Hi <strong>${username}</strong>,</p>

        <p>Your enCryptID account has been created on your current device. To access your account from another device (like your phone), follow these steps:</p>

        <div class="card">
          <div class="step">
            <span class="step-number">1</span>
            <div>
              <strong>Open the link below on your other device</strong>
              <p style="margin: 4px 0 0 0; color: #666; font-size: 14px;">Use your phone, tablet, or another computer</p>
            </div>
          </div>
          <div class="step">
            <span class="step-number">2</span>
            <div>
              <strong>A new cryptographic key will be generated</strong>
              <p style="margin: 4px 0 0 0; color: #666; font-size: 14px;">This key is unique to that device and stored securely in the browser</p>
            </div>
          </div>
          <div class="step">
            <span class="step-number">3</span>
            <div>
              <strong>You're set! Both devices can now access your account</strong>
              <p style="margin: 4px 0 0 0; color: #666; font-size: 14px;">No passwords to remember - your browser handles authentication</p>
            </div>
          </div>
        </div>

        <div style="text-align: center;">
          <a href="${setupUrl}" class="button">Set Up on Another Device</a>
        </div>

        <div class="warning">
          <strong>⚠️ Security Note:</strong> Only open this link on devices you own. Each device that accesses your account stores a unique cryptographic key.
        </div>

        <div class="footer">
          <p>This email was sent because you created an enCryptID account and opted for multi-device backup.</p>
          <p>If you didn't request this, you can safely ignore this email.</p>
          <p style="margin-top: 16px;">enCryptID by <a href="${appUrl}" style="color: #8b5cf6;">jeffemmett.com</a></p>
        </div>
      </body>
      </html>
      `
    );

    if (emailSent) {
      return new Response(JSON.stringify({
        success: true,
        message: 'Backup email sent successfully',
      }), {
        headers: { 'Content-Type': 'application/json' },
      });
    } else {
      return new Response(JSON.stringify({
        error: 'Failed to send email',
        message: 'Please check your email address and try again',
      }), {
        status: 500,
        headers: { 'Content-Type': 'application/json' },
      });
    }

  } catch (error) {
    console.error('Send backup email error:', error);
    return new Response(JSON.stringify({ error: 'Internal server error' }), {
      status: 500,
      headers: { 'Content-Type': 'application/json' },
    });
  }
}

/**
 * Check if a public key is linked to an account
 * POST /auth/lookup
 * Body: { publicKey }
 */
export async function handleLookup(
  request: Request,
  env: Environment
): Promise<Response> {
  try {
    const body = await request.json() as { publicKey: string };
    const { publicKey } = body;

    if (!publicKey) {
      return new Response(JSON.stringify({ error: 'Missing publicKey' }), {
        status: 400,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    const db = env.CRYPTID_DB;
    if (!db) {
      return new Response(JSON.stringify({ error: 'Database not configured' }), {
        status: 503,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Find device key and associated user
    const result = await db.prepare(`
      SELECT u.cryptid_username, u.email, u.email_verified, dk.device_name
      FROM device_keys dk
      JOIN users u ON dk.user_id = u.id
      WHERE dk.public_key = ?
    `).bind(publicKey).first<{
      cryptid_username: string;
      email: string;
      email_verified: number;
      device_name: string;
    }>();

    if (!result) {
      return new Response(JSON.stringify({
        found: false
      }), {
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Update last_used timestamp
    await db.prepare(
      "UPDATE device_keys SET last_used = datetime('now') WHERE public_key = ?"
    ).bind(publicKey).run();

    return new Response(JSON.stringify({
      found: true,
      cryptidUsername: result.cryptid_username,
      email: result.email,
      emailVerified: result.email_verified === 1,
      deviceName: result.device_name
    }), {
      headers: { 'Content-Type': 'application/json' },
    });

  } catch (error) {
    console.error('Lookup error:', error);
    return new Response(JSON.stringify({ error: 'Internal server error' }), {
      status: 500,
      headers: { 'Content-Type': 'application/json' },
    });
  }
}

/**
 * Get linked devices for an account
 * POST /auth/devices
 * Body: { publicKey } - authenticates via device's public key
 */
export async function handleGetDevices(
  request: Request,
  env: Environment
): Promise<Response> {
  try {
    const body = await request.json() as { publicKey: string };
    const { publicKey } = body;

    if (!publicKey) {
      return new Response(JSON.stringify({ error: 'Missing publicKey' }), {
        status: 400,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    const db = env.CRYPTID_DB;
    if (!db) {
      return new Response(JSON.stringify({ error: 'Database not configured' }), {
        status: 503,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Find user by public key
    const deviceKey = await db.prepare(`
      SELECT user_id FROM device_keys WHERE public_key = ?
    `).bind(publicKey).first<{ user_id: string }>();

    if (!deviceKey) {
      return new Response(JSON.stringify({ error: 'Device not found' }), {
        status: 404,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Get all devices for this user
    const devices = await db.prepare(`
      SELECT id, device_name, user_agent, created_at, last_used, public_key
      FROM device_keys
      WHERE user_id = ?
      ORDER BY created_at DESC
    `).bind(deviceKey.user_id).all<DeviceKey>();

    return new Response(JSON.stringify({
      devices: devices.results?.map((d: DeviceKey) => ({
        id: d.id,
        deviceName: d.device_name,
        userAgent: d.user_agent,
        createdAt: d.created_at,
        lastUsed: d.last_used,
        isCurrentDevice: d.public_key === publicKey
      })) || []
    }), {
      headers: { 'Content-Type': 'application/json' },
    });

  } catch (error) {
    console.error('Get devices error:', error);
    return new Response(JSON.stringify({ error: 'Internal server error' }), {
      status: 500,
      headers: { 'Content-Type': 'application/json' },
    });
  }
}

/**
 * Revoke a device
 * DELETE /auth/devices/:deviceId
 * Body: { publicKey } - authenticates via device's public key
 */
export async function handleRevokeDevice(
  deviceId: string,
  request: Request,
  env: Environment
): Promise<Response> {
  try {
    const body = await request.json() as { publicKey: string };
    const { publicKey } = body;

    if (!publicKey) {
      return new Response(JSON.stringify({ error: 'Missing publicKey' }), {
        status: 400,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    const db = env.CRYPTID_DB;
    if (!db) {
      return new Response(JSON.stringify({ error: 'Database not configured' }), {
        status: 503,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Find user by public key
    const currentDevice = await db.prepare(`
      SELECT user_id FROM device_keys WHERE public_key = ?
    `).bind(publicKey).first<{ user_id: string }>();

    if (!currentDevice) {
      return new Response(JSON.stringify({ error: 'Unauthorized' }), {
        status: 401,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Verify the device to revoke belongs to the same user
    const targetDevice = await db.prepare(`
      SELECT user_id, public_key FROM device_keys WHERE id = ?
    `).bind(deviceId).first<{ user_id: string; public_key: string }>();

    if (!targetDevice || targetDevice.user_id !== currentDevice.user_id) {
      return new Response(JSON.stringify({ error: 'Device not found' }), {
        status: 404,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Don't allow revoking the current device
    if (targetDevice.public_key === publicKey) {
      return new Response(JSON.stringify({ error: 'Cannot revoke current device' }), {
        status: 400,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Delete the device
    await db.prepare('DELETE FROM device_keys WHERE id = ?').bind(deviceId).run();

    return new Response(JSON.stringify({
      success: true,
      message: 'Device revoked'
    }), {
      headers: { 'Content-Type': 'application/json' },
    });

  } catch (error) {
    console.error('Revoke device error:', error);
    return new Response(JSON.stringify({ error: 'Internal server error' }), {
      status: 500,
      headers: { 'Content-Type': 'application/json' },
    });
  }
}

/**
 * Check if a username is available
 * GET /auth/check-username?username=<username>
 *
 * Returns whether a username is available for registration.
 * Used during the CryptID registration flow.
 */
export async function handleCheckUsername(
  request: Request,
  env: Environment
): Promise<Response> {
  try {
    const url = new URL(request.url);
    const username = url.searchParams.get('username');

    if (!username) {
      return new Response(JSON.stringify({ error: 'Username is required' }), {
        status: 400,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Normalize username (lowercase, no special chars)
    const normalizedUsername = username.toLowerCase().replace(/[^a-z0-9_-]/g, '');

    if (normalizedUsername.length < 3) {
      return new Response(JSON.stringify({
        available: false,
        error: 'Username must be at least 3 characters'
      }), {
        headers: { 'Content-Type': 'application/json' },
      });
    }

    if (normalizedUsername.length > 20) {
      return new Response(JSON.stringify({
        available: false,
        error: 'Username must be 20 characters or less'
      }), {
        headers: { 'Content-Type': 'application/json' },
      });
    }

    const db = env.CRYPTID_DB;
    if (!db) {
      // If no database, assume username is available (graceful degradation)
      return new Response(JSON.stringify({ available: true }), {
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Check if username exists in database
    const existingUser = await db.prepare(
      'SELECT id FROM users WHERE cryptid_username = ?'
    ).bind(normalizedUsername).first<{ id: string }>();

    return new Response(JSON.stringify({
      available: !existingUser,
      username: normalizedUsername,
      error: existingUser ? 'Username is already taken' : null
    }), {
      headers: { 'Content-Type': 'application/json' },
    });

  } catch (error) {
    console.error('Check username error:', error);
    return new Response(JSON.stringify({ error: 'Internal server error' }), {
      status: 500,
      headers: { 'Content-Type': 'application/json' },
    });
  }
}

/**
 * Search for CryptID users by username
 * GET /auth/users/search?q=<query>
 *
 * This endpoint allows searching for users by CryptID username.
 * Used for granting permissions to users by username.
 */
export async function handleSearchUsers(
  request: Request,
  env: Environment
): Promise<Response> {
  try {
    const url = new URL(request.url);
    const query = url.searchParams.get('q');

    if (!query || query.length < 2) {
      return new Response(JSON.stringify({ error: 'Query must be at least 2 characters' }), {
        status: 400,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    const db = env.CRYPTID_DB;
    if (!db) {
      return new Response(JSON.stringify({ error: 'Database not configured' }), {
        status: 503,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // Search users by username (case-insensitive)
    const users = await db.prepare(`
      SELECT id, cryptid_username, email, email_verified, created_at
      FROM users
      WHERE cryptid_username LIKE ?
      ORDER BY cryptid_username
      LIMIT 20
    `).bind(`%${query}%`).all<{
      id: string;
      cryptid_username: string;
      email: string;
      email_verified: number;
      created_at: string;
    }>();

    return new Response(JSON.stringify({
      users: (users.results || []).map(u => ({
        id: u.id,
        username: u.cryptid_username,
        emailVerified: u.email_verified === 1,
        createdAt: u.created_at
      }))
    }), {
      headers: { 'Content-Type': 'application/json' },
    });

  } catch (error) {
    console.error('Search users error:', error);
    return new Response(JSON.stringify({ error: 'Internal server error' }), {
      status: 500,
      headers: { 'Content-Type': 'application/json' },
    });
  }
}

/**
 * List all CryptID users (admin endpoint)
 * GET /admin/users
 * Query params: ?limit=<number>&offset=<number>
 *
 * This endpoint requires admin authentication.
 * For now, we'll require a simple admin secret header.
 */
export async function handleListAllUsers(
  request: Request,
  env: Environment
): Promise<Response> {
  try {
    // Check for admin secret (simple auth for now)
    const adminSecret = request.headers.get('X-Admin-Secret');
    if (!adminSecret || adminSecret !== env.ADMIN_SECRET) {
      return new Response(JSON.stringify({ error: 'Unauthorized - admin access required' }), {
        status: 401,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    const db = env.CRYPTID_DB;
    if (!db) {
      return new Response(JSON.stringify({ error: 'Database not configured' }), {
        status: 503,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    const url = new URL(request.url);
    const limit = Math.min(parseInt(url.searchParams.get('limit') || '100'), 1000);
    const offset = parseInt(url.searchParams.get('offset') || '0');

    // Get total count
    const countResult = await db.prepare('SELECT COUNT(*) as count FROM users').first<{ count: number }>();
    const totalCount = countResult?.count || 0;

    // Get users with device count
    const users = await db.prepare(`
      SELECT
        u.id,
        u.cryptid_username,
        u.email,
        u.email_verified,
        u.created_at,
        u.updated_at,
        (SELECT COUNT(*) FROM device_keys dk WHERE dk.user_id = u.id) as device_count
      FROM users u
      ORDER BY u.created_at DESC
      LIMIT ? OFFSET ?
    `).bind(limit, offset).all<{
      id: string;
      cryptid_username: string;
      email: string;
      email_verified: number;
      created_at: string;
      updated_at: string;
      device_count: number;
    }>();

    return new Response(JSON.stringify({
      users: (users.results || []).map(u => ({
        id: u.id,
        username: u.cryptid_username,
        email: u.email,
        emailVerified: u.email_verified === 1,
        deviceCount: u.device_count,
        createdAt: u.created_at,
        updatedAt: u.updated_at
      })),
      pagination: {
        total: totalCount,
        limit,
        offset,
        hasMore: offset + limit < totalCount
      }
    }), {
      headers: { 'Content-Type': 'application/json' },
    });

  } catch (error) {
    console.error('List all users error:', error);
    return new Response(JSON.stringify({ error: 'Internal server error' }), {
      status: 500,
      headers: { 'Content-Type': 'application/json' },
    });
  }
}
