Clone
import QRCode from 'qrcode';
import { html } from './pages/home';
import { dashboardHtml } from './pages/dashboard';
import { loginHtml } from './pages/login';

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    const path = url.pathname;

    // Serve static assets
    if (path.endsWith('.js') || path.endsWith('.css')) {
      return serveStatic(path);
    }

    // API Routes
    if (path.startsWith('/api/')) {
      return handleAPI(request, env, path);
    }

    // Pages
    if (path === '/') {
      return new Response(html(), {
        headers: { 'Content-Type': 'text/html' }
      });
    }

    if (path === '/login') {
      return new Response(loginHtml(), {
        headers: { 'Content-Type': 'text/html' }
      });
    }

    if (path === '/dashboard') {
      return new Response(dashboardHtml(), {
        headers: { 'Content-Type': 'text/html' }
      });
    }

    // Check for short link redirect
    const slug = path.slice(1);
    if (slug && !slug.includes('/')) {
      const link = await env.DB.prepare(
        'SELECT url FROM links WHERE slug = ?'
      ).bind(slug).first();

      if (link) {
        // Update click count
        await env.DB.prepare(
          'UPDATE links SET clicks = clicks + 1 WHERE slug = ?'
        ).bind(slug).run();

        return Response.redirect(link.url, 301);
      }
    }

    return new Response('Not Found', { status: 404 });
  }
};

async function handleAPI(request, env, path) {
  const url = new URL(request.url);
  const method = request.method;

  // Auth endpoints
  if (path === '/api/auth/request' && method === 'POST') {
    const { email } = await request.json();
    
    // Check if email is authorized
    const authorized = env.AUTHORIZED_EMAILS?.split(',').map(e => e.trim()).includes(email);
    if (!authorized) {
      return jsonResponse({ error: 'Unauthorized email' }, 403);
    }

    // Generate token
    const token = crypto.randomUUID();
    const expires = new Date(Date.now() + 15 * 60 * 1000).toISOString();
    
    await env.DB.prepare(
      'INSERT INTO auth_tokens (token, email, expires_at) VALUES (?, ?, ?)'
    ).bind(token, email, expires).run();

    // Send email via Postmark
    const emailResponse = await fetch('https://api.postmarkapp.com/email', {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'X-Postmark-Server-Token': env.EMAIL_API_KEY
      },
      body: JSON.stringify({
        From: env.EMAIL_FROM || 'noreply@qrurl.us',
        To: email,
        Subject: 'Your QRurl Login Link',
        HtmlBody: `
          <h2>Login to QRurl</h2>
          <p>Click the link below to log in:</p>
          <a href="${url.origin}/api/auth/verify?token=${token}">Log In</a>
          <p>This link expires in 15 minutes.</p>
        `,
        TextBody: `Login to QRurl\n\nClick here: ${url.origin}/api/auth/verify?token=${token}`,
        MessageStream: 'outbound'
      })
    });

    if (!emailResponse.ok) {
      return jsonResponse({ error: 'Failed to send email' }, 500);
    }

    return jsonResponse({ success: true, message: 'Check your email' });
  }

  if (path === '/api/auth/verify' && method === 'GET') {
    const token = url.searchParams.get('token');
    
    const authToken = await env.DB.prepare(
      'SELECT * FROM auth_tokens WHERE token = ? AND used = 0 AND expires_at > datetime("now")'
    ).bind(token).first();

    if (!authToken) {
      return new Response('Invalid or expired token', { status: 401 });
    }

    // Mark token as used
    await env.DB.prepare(
      'UPDATE auth_tokens SET used = 1 WHERE token = ?'
    ).bind(token).run();

    // Create session
    const sessionId = crypto.randomUUID();
    const sessionExpires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
    
    await env.DB.prepare(
      'INSERT INTO sessions (id, email, expires_at) VALUES (?, ?, ?)'
    ).bind(sessionId, authToken.email, sessionExpires.toISOString()).run();

    // Redirect to dashboard with session cookie
    return new Response(null, {
      status: 302,
      headers: {
        'Location': '/dashboard',
        'Set-Cookie': `session=${sessionId}; Path=/; HttpOnly; Secure; SameSite=Strict; Expires=${sessionExpires.toUTCString()}`
      }
    });
  }

  // Link management
  if (path === '/api/links' && method === 'GET') {
    const session = getSession(request);
    const links = await env.DB.prepare(
      session 
        ? 'SELECT * FROM links WHERE user_email = ? ORDER BY created_at DESC'
        : 'SELECT * FROM links WHERE user_email IS NULL ORDER BY created_at DESC LIMIT 10'
    ).bind(session?.email).all();

    return jsonResponse(links.results || []);
  }

  if (path === '/api/links' && method === 'POST') {
    const { url: targetUrl, name, customSlug } = await request.json();
    
    if (!targetUrl) {
      return jsonResponse({ error: 'URL required' }, 400);
    }

    const session = getSession(request);
    const slug = customSlug || generateSlug();
    
    // Check if slug exists
    const existing = await env.DB.prepare(
      'SELECT slug FROM links WHERE slug = ?'
    ).bind(slug).first();

    if (existing) {
      return jsonResponse({ error: 'Slug already exists' }, 409);
    }

    await env.DB.prepare(
      'INSERT INTO links (slug, url, name, user_email) VALUES (?, ?, ?, ?)'
    ).bind(slug, targetUrl, name || null, session?.email || null).run();

    return jsonResponse({ slug, url: targetUrl });
  }

  // QR Code generation
  if (path.startsWith('/api/qr/')) {
    const slug = path.split('/')[3];
    const shortUrl = `${url.origin}/${slug}`;
    
    const qrDataUrl = await QRCode.toDataURL(shortUrl, {
      errorCorrectionLevel: 'H',
      width: 400,
      margin: 1
    });

    return new Response(qrDataUrl.split(',')[1], {
      headers: {
        'Content-Type': 'image/png',
        'Content-Encoding': 'base64'
      }
    });
  }

  return jsonResponse({ error: 'Not found' }, 404);
}

function jsonResponse(data, status = 200) {
  return new Response(JSON.stringify(data), {
    status,
    headers: { 'Content-Type': 'application/json' }
  });
}

function generateSlug(length = 6) {
  const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  let slug = '';
  for (let i = 0; i < length; i++) {
    slug += chars[Math.floor(Math.random() * chars.length)];
  }
  return slug;
}

function getSession(request) {
  const cookie = request.headers.get('Cookie');
  if (!cookie) return null;
  
  const match = cookie.match(/session=([^;]+)/);
  return match ? { id: match[1] } : null;
}

function serveStatic(path) {
  // Simple static file serving
  const contentType = path.endsWith('.js') ? 'application/javascript' : 'text/css';
  return new Response('', {
    headers: { 'Content-Type': contentType }
  });
}