import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
import { db, dbGet, dbAll, contacts, emailSubscribers, legalSettings } from '../db/index.js';
import { eq, desc } from 'drizzle-orm';
import { requireAuth } from '../lib/auth.js';
import { generateId, getNow, sanitizeHtml } from '../lib/utils.js';
import { emailService } from '../lib/email.js';
import { rateLimitMiddleware } from '../lib/rateLimit.js';

const contactsRouter = new Hono();

// Per-IP rate limit for public, unauthenticated write endpoints (contact form,
// newsletter subscribe/unsubscribe) to prevent spam and email flooding.
const publicFormLimit = rateLimitMiddleware({ max: 5, windowMs: 10 * 60 * 1000, prefix: 'contacts' });

// ==================== Sanitization Helpers ====================

/**
 * Sanitize email header values to prevent email header injection
 * Strips newlines and carriage returns that could be used to inject headers
 */
function sanitizeHeaderValue(str: string): string {
  return str.replace(/[\r\n]/g, '').trim();
}

const createContactSchema = z.object({
  name: z.string().min(2).max(200),
  email: z.string().email().max(254),
  message: z.string().min(10).max(5000),
});

const subscribeSchema = z.object({
  email: z.string().email().max(254),
  name: z.string().max(200).optional(),
});

const updateContactSchema = z.object({
  status: z.enum(['new', 'read', 'replied']),
});

// Submit contact form (public)
contactsRouter.post('/', publicFormLimit, zValidator('json', createContactSchema), async (c) => {
  const data = c.req.valid('json');
  const now = getNow();
  const id = generateId();
  
  // Sanitize header-sensitive values to prevent email header injection
  const sanitizedEmail = sanitizeHeaderValue(data.email);
  const sanitizedName = sanitizeHeaderValue(data.name);
  
  const newContact = {
    id,
    name: sanitizedName,
    email: sanitizedEmail,
    message: data.message,
    status: 'new' as const,
    createdAt: now,
  };
  
  // Always store the message in admin, regardless of email outcome
  await (db as any).insert(contacts).values(newContact);
  
  // Send email notification to support email (non-blocking)
  try {
    // Retrieve support_email from legal_settings
    const settings = await dbGet<any>(
      (db as any).select().from(legalSettings).limit(1)
    );
    
    const supportEmail = settings?.supportEmail;
    
    if (supportEmail) {
      const websiteUrl = process.env.FRONTEND_URL || 'https://spanglish.com';
      
      // Sanitize all values for HTML display
      const safeName = sanitizeHtml(sanitizedName);
      const safeEmail = sanitizeHtml(sanitizedEmail);
      const safeMessage = sanitizeHtml(data.message);
      
      const subject = `New Contact Form Message – ${websiteUrl}`;
      
      const bodyHtml = `
        <p><strong>${safeName}</strong> (${safeEmail}) sent a message:</p>
        <div style="padding: 16px 20px; background-color: #f8fafc; border-left: 4px solid #3b82f6; margin: 16px 0; white-space: pre-wrap; font-size: 15px; line-height: 1.6;">${safeMessage}</div>
        <p style="color: #64748b; font-size: 13px;">Reply directly to this email to respond to ${safeName}.</p>
      `;
      
      const bodyText = [
        `${sanitizedName} (${sanitizedEmail}) sent a message:`,
        '',
        data.message,
        '',
        `Reply directly to this email to respond to ${sanitizedName}.`,
      ].join('\n');
      
      const emailResult = await emailService.sendCustomEmail({
        to: supportEmail,
        subject,
        bodyHtml,
        bodyText,
        replyTo: sanitizedEmail,
      });
      
      if (!emailResult.success) {
        console.error('[Contact Form] Failed to send email notification:', emailResult.error);
      }
    } else {
      console.warn('[Contact Form] No support email configured in legal settings – skipping email notification');
    }
  } catch (emailError: any) {
    // Log the error but do NOT break the contact form UX
    console.error('[Contact Form] Error sending email notification:', emailError?.message || emailError);
  }
  
  return c.json({ message: 'Message sent successfully' }, 201);
});

// Subscribe to newsletter (public)
contactsRouter.post('/subscribe', publicFormLimit, zValidator('json', subscribeSchema), async (c) => {
  const data = c.req.valid('json');
  
  // Check if already subscribed
  const existing = await dbGet<any>(
    (db as any)
      .select()
      .from(emailSubscribers)
      .where(eq((emailSubscribers as any).email, data.email))
  );
  
  if (existing) {
    if (existing.status === 'unsubscribed') {
      // Resubscribe
      await (db as any)
        .update(emailSubscribers)
        .set({ status: 'active' })
        .where(eq((emailSubscribers as any).id, existing.id));
      
      return c.json({ message: 'Successfully resubscribed' });
    }
    return c.json({ message: 'Already subscribed' });
  }
  
  const now = getNow();
  const id = generateId();
  
  const newSubscriber = {
    id,
    email: data.email,
    name: data.name || null,
    status: 'active' as const,
    createdAt: now,
  };
  
  await (db as any).insert(emailSubscribers).values(newSubscriber);
  
  return c.json({ message: 'Successfully subscribed' }, 201);
});

// Unsubscribe from newsletter (public)
contactsRouter.post('/unsubscribe', publicFormLimit, zValidator('json', z.object({ email: z.string().email().max(254) })), async (c) => {
  const { email } = c.req.valid('json');
  
  const existing = await dbGet<any>(
    (db as any).select().from(emailSubscribers).where(eq((emailSubscribers as any).email, email))
  );
  
  // Always return the same response whether or not the address exists, to avoid
  // leaking which emails are subscribed (enumeration).
  if (existing && existing.status !== 'unsubscribed') {
    await (db as any)
      .update(emailSubscribers)
      .set({ status: 'unsubscribed' })
      .where(eq((emailSubscribers as any).id, existing.id));
  }
  
  return c.json({ message: 'If this email was subscribed, it has been unsubscribed.' });
});

// Get all contacts (admin)
contactsRouter.get('/', requireAuth(['admin', 'organizer']), async (c) => {
  const status = c.req.query('status');
  const limit = Math.min(Math.max(parseInt(c.req.query('limit') || '100', 10) || 100, 1), 500);
  const offset = Math.max(parseInt(c.req.query('offset') || '0', 10) || 0, 0);
  
  let query = (db as any).select().from(contacts);
  
  if (status) {
    query = query.where(eq((contacts as any).status, status));
  }
  
  const result = await dbAll(
    query.orderBy(desc((contacts as any).createdAt)).limit(limit).offset(offset)
  );
  
  return c.json({ contacts: result });
});

// Get single contact (admin)
contactsRouter.get('/:id', requireAuth(['admin', 'organizer']), async (c) => {
  const id = c.req.param('id');
  
  const contact = await dbGet(
    (db as any)
      .select()
      .from(contacts)
      .where(eq((contacts as any).id, id))
  );
  
  if (!contact) {
    return c.json({ error: 'Contact not found' }, 404);
  }
  
  return c.json({ contact });
});

// Update contact status (admin)
contactsRouter.put('/:id', requireAuth(['admin', 'organizer']), zValidator('json', updateContactSchema), async (c) => {
  const id = c.req.param('id');
  const data = c.req.valid('json');
  
  const existing = await dbGet<any>(
    (db as any).select().from(contacts).where(eq((contacts as any).id, id))
  );
  
  if (!existing) {
    return c.json({ error: 'Contact not found' }, 404);
  }
  
  await (db as any)
    .update(contacts)
    .set({ status: data.status })
    .where(eq((contacts as any).id, id));
  
  const updated = await dbGet<any>(
    (db as any).select().from(contacts).where(eq((contacts as any).id, id))
  );
  
  return c.json({ contact: updated });
});

// Delete contact (admin)
contactsRouter.delete('/:id', requireAuth(['admin']), async (c) => {
  const id = c.req.param('id');
  
  await (db as any).delete(contacts).where(eq((contacts as any).id, id));
  
  return c.json({ message: 'Contact deleted successfully' });
});

// Get all subscribers (admin)
contactsRouter.get('/subscribers/list', requireAuth(['admin', 'marketing']), async (c) => {
  const status = c.req.query('status');
  const limit = Math.min(Math.max(parseInt(c.req.query('limit') || '100', 10) || 100, 1), 1000);
  const offset = Math.max(parseInt(c.req.query('offset') || '0', 10) || 0, 0);
  
  let query = (db as any).select().from(emailSubscribers);
  
  if (status) {
    query = query.where(eq((emailSubscribers as any).status, status));
  }
  
  const result = await dbAll(
    query.orderBy(desc((emailSubscribers as any).createdAt)).limit(limit).offset(offset)
  );
  
  return c.json({ subscribers: result });
});

export default contactsRouter;
