import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
import { db, dbGet, dbAll, emailTemplates, emailLogs, events, tickets } from '../db/index.js';
import { eq, desc, and, or, sql } from 'drizzle-orm';
import { requireAuth } from '../lib/auth.js';
import { getNow, generateId } from '../lib/utils.js';
import emailService from '../lib/email.js';
import { getTemplateVariables, defaultTemplates } from '../lib/emailTemplates.js';
import { getQueueStatus } from '../lib/emailQueue.js';

const emailsRouter = new Hono();

const slugPattern = /^[a-z0-9-]+$/;

const createTemplateSchema = z.object({
  name: z.string().min(1).max(255),
  slug: z.string().min(1).max(100).regex(slugPattern),
  subject: z.string().min(1).max(500),
  subjectEs: z.string().max(500).optional().nullable(),
  bodyHtml: z.string().min(1).max(200_000),
  bodyHtmlEs: z.string().max(200_000).optional().nullable(),
  bodyText: z.string().max(200_000).optional().nullable(),
  bodyTextEs: z.string().max(200_000).optional().nullable(),
  description: z.string().max(2000).optional().nullable(),
  variables: z.array(z.any()).max(100).optional(),
});

const updateTemplateSchema = createTemplateSchema.partial().extend({
  isActive: z.boolean().optional(),
});

const sendCustomEmailSchema = z.object({
  to: z.string().email().max(254),
  toName: z.string().max(200).optional(),
  subject: z.string().min(1).max(500),
  bodyHtml: z.string().min(1).max(200_000),
  bodyText: z.string().max(200_000).optional(),
  eventId: z.string().optional(),
});

const emailLogsQuerySchema = z.object({
  limit: z.coerce.number().int().min(1).max(100).optional().default(50),
  offset: z.coerce.number().int().min(0).optional().default(0),
});

// Safely parse a stored JSON variables column; a corrupt row must not 500 the route.
function safeParseVariables(raw: any): any[] {
  if (!raw) return [];
  try {
    const parsed = JSON.parse(raw);
    return Array.isArray(parsed) ? parsed : [];
  } catch {
    return [];
  }
}

// ==================== Template Routes ====================

// Get all email templates
emailsRouter.get('/templates', requireAuth(['admin', 'organizer']), async (c) => {
  const templates = await dbAll<any>(
    (db as any).select().from(emailTemplates).orderBy(desc((emailTemplates as any).createdAt))
  );
  
  // Parse variables JSON for each template
  const parsedTemplates = templates.map((t: any) => ({
    ...t,
    variables: safeParseVariables(t.variables),
    isSystem: Boolean(t.isSystem),
    isActive: Boolean(t.isActive),
  }));
  
  return c.json({ templates: parsedTemplates });
});

// Get single email template
emailsRouter.get('/templates/:id', requireAuth(['admin', 'organizer']), async (c) => {
  const { id } = c.req.param();
  
  const template = await dbGet<any>(
    (db as any)
      .select()
      .from(emailTemplates)
      .where(eq((emailTemplates as any).id, id))
  );
  
  if (!template) {
    return c.json({ error: 'Template not found' }, 404);
  }
  
  return c.json({ 
    template: {
      ...template,
      variables: safeParseVariables(template.variables),
      isSystem: Boolean(template.isSystem),
      isActive: Boolean(template.isActive),
    }
  });
});

// Create new email template
emailsRouter.post('/templates', requireAuth(['admin']), zValidator('json', createTemplateSchema), async (c) => {
  const body = c.req.valid('json');
  const { name, slug, subject, subjectEs, bodyHtml, bodyHtmlEs, bodyText, bodyTextEs, description, variables } = body;
  
  // Check if slug already exists
  const existing = await dbGet<any>(
    (db as any).select().from(emailTemplates).where(eq((emailTemplates as any).slug, slug))
  );
  
  if (existing) {
    return c.json({ error: 'Template with this slug already exists' }, 400);
  }
  
  const now = getNow();
  const template = {
    id: generateId(),
    name,
    slug,
    subject,
    subjectEs: subjectEs || null,
    bodyHtml,
    bodyHtmlEs: bodyHtmlEs || null,
    bodyText: bodyText || null,
    bodyTextEs: bodyTextEs || null,
    description: description || null,
    variables: variables ? JSON.stringify(variables) : null,
    isSystem: 0,
    isActive: 1,
    createdAt: now,
    updatedAt: now,
  };
  
  await (db as any).insert(emailTemplates).values(template);
  
  return c.json({ 
    template: {
      ...template,
      variables: variables || [],
      isSystem: false,
      isActive: true,
    },
    message: 'Template created successfully' 
  }, 201);
});

// Update email template
emailsRouter.put('/templates/:id', requireAuth(['admin']), zValidator('json', updateTemplateSchema), async (c) => {
  const { id } = c.req.param();
  const body = c.req.valid('json');
  
  const existing = await dbGet<any>(
    (db as any)
      .select()
      .from(emailTemplates)
      .where(eq((emailTemplates as any).id, id))
  );
  
  if (!existing) {
    return c.json({ error: 'Template not found' }, 404);
  }
  
  const updateData: any = { updatedAt: getNow() };
  
  // System templates cannot have their slug or isSystem flag changed; only the
  // editable fields below are applied.
  const allowedFields = ['name', 'subject', 'subjectEs', 'bodyHtml', 'bodyHtmlEs', 'bodyText', 'bodyTextEs', 'description', 'variables', 'isActive'];
  if (!existing.isSystem) {
    allowedFields.push('slug');
  }
  
  for (const field of allowedFields) {
    const value = (body as Record<string, unknown>)[field];
    if (value !== undefined) {
      if (field === 'variables') {
        updateData[field] = JSON.stringify(value);
      } else if (field === 'isActive') {
        updateData[field] = value ? 1 : 0;
      } else {
        updateData[field] = value;
      }
    }
  }
  
  await (db as any)
    .update(emailTemplates)
    .set(updateData)
    .where(eq((emailTemplates as any).id, id));
  
  const updated = await dbGet<any>(
    (db as any)
      .select()
      .from(emailTemplates)
      .where(eq((emailTemplates as any).id, id))
  );
  
  return c.json({ 
    template: {
      ...updated,
      variables: safeParseVariables(updated.variables),
      isSystem: Boolean(updated.isSystem),
      isActive: Boolean(updated.isActive),
    },
    message: 'Template updated successfully' 
  });
});

// Delete email template (only non-system templates)
emailsRouter.delete('/templates/:id', requireAuth(['admin']), async (c) => {
  const { id } = c.req.param();
  
  const template = await dbGet<any>(
    (db as any).select().from(emailTemplates).where(eq((emailTemplates as any).id, id))
  );
  
  if (!template) {
    return c.json({ error: 'Template not found' }, 404);
  }
  
  if (template.isSystem) {
    return c.json({ error: 'Cannot delete system templates' }, 400);
  }
  
  await (db as any)
    .delete(emailTemplates)
    .where(eq((emailTemplates as any).id, id));
  
  return c.json({ message: 'Template deleted successfully' });
});

// Get available template variables
emailsRouter.get('/templates/:slug/variables', requireAuth(['admin', 'organizer']), async (c) => {
  const { slug } = c.req.param();
  const variables = getTemplateVariables(slug);
  return c.json({ variables });
});

// ==================== Email Sending Routes ====================

// Send email using template to event attendees (non-blocking, queued)
emailsRouter.post('/send/event/:eventId', requireAuth(['admin', 'organizer']), async (c) => {
  const { eventId } = c.req.param();
  const user = (c as any).get('user');
  const body = await c.req.json();
  const { templateSlug, customVariables, recipientFilter } = body;
  
  if (!templateSlug || typeof templateSlug !== 'string') {
    return c.json({ error: 'Template slug is required' }, 400);
  }

  const allowedFilters = ['confirmed', 'pending', 'all', 'checked_in'];
  const filter = recipientFilter || 'confirmed';
  if (!allowedFilters.includes(filter)) {
    return c.json({ error: `Invalid recipientFilter. Allowed: ${allowedFilters.join(', ')}` }, 400);
  }
  
  // Queue emails for background processing instead of sending synchronously
  const result = await emailService.queueEventEmails({
    eventId,
    templateSlug,
    customVariables,
    recipientFilter: filter,
    sentBy: user?.id,
  });
  
  return c.json(result);
});

// Send custom email to specific recipients
emailsRouter.post('/send/custom', requireAuth(['admin', 'organizer']), zValidator('json', sendCustomEmailSchema), async (c) => {
  const user = (c as any).get('user');
  const { to, toName, subject, bodyHtml, bodyText, eventId } = c.req.valid('json');
  
  const result = await emailService.sendCustomEmail({
    to,
    toName,
    subject,
    bodyHtml,
    bodyText,
    eventId,
    sentBy: user?.id,
  });
  
  return c.json(result);
});

// Preview email (render template without sending)
emailsRouter.post('/preview', requireAuth(['admin', 'organizer']), async (c) => {
  const body = await c.req.json();
  const { templateSlug, variables, locale } = body;
  
  if (!templateSlug) {
    return c.json({ error: 'Template slug is required' }, 400);
  }
  
  const template = await emailService.getTemplate(templateSlug);
  if (!template) {
    return c.json({ error: 'Template not found' }, 404);
  }
  
  const { replaceTemplateVariables, wrapInBaseTemplate } = await import('../lib/emailTemplates.js');
  
  const allVariables = {
    ...emailService.getCommonVariables(),
    lang: locale || 'en',
    ...variables,
  };
  
  const subject = locale === 'es' && template.subjectEs 
    ? template.subjectEs 
    : template.subject;
  const bodyHtml = locale === 'es' && template.bodyHtmlEs 
    ? template.bodyHtmlEs 
    : template.bodyHtml;
  
  const finalSubject = replaceTemplateVariables(subject, allVariables);
  const finalBodyContent = replaceTemplateVariables(bodyHtml, allVariables, true);
  const finalBodyHtml = wrapInBaseTemplate(finalBodyContent, { ...allVariables, subject: finalSubject });
  
  return c.json({
    subject: finalSubject,
    bodyHtml: finalBodyHtml,
  });
});

// ==================== Email Logs Routes ====================

// Get email logs
emailsRouter.get('/logs', requireAuth(['admin', 'organizer']), async (c) => {
  const eventId = c.req.query('eventId');
  const status = c.req.query('status');
  const search = c.req.query('search');
  // Clamp pagination so a NaN / out-of-range value can't produce undefined query behaviour.
  const limit = Math.min(Math.max(parseInt(c.req.query('limit') || '50', 10) || 50, 1), 200);
  const offset = Math.max(parseInt(c.req.query('offset') || '0', 10) || 0, 0);
  
  let query = (db as any).select().from(emailLogs);
  
  const conditions = [];
  if (eventId) {
    conditions.push(eq((emailLogs as any).eventId, eventId));
  }
  if (status) {
    conditions.push(eq((emailLogs as any).status, status));
  }
  if (search && search.trim()) {
    const term = `%${search.trim().toLowerCase()}%`;
    conditions.push(or(
      sql`LOWER(${(emailLogs as any).recipientEmail}) LIKE ${term}`,
      sql`LOWER(COALESCE(${(emailLogs as any).recipientName}, '')) LIKE ${term}`,
      sql`LOWER(${(emailLogs as any).subject}) LIKE ${term}`,
    ));
  }
  
  if (conditions.length > 0) {
    query = query.where(and(...conditions));
  }
  
  const logs = await dbAll(
    query
      .orderBy(desc((emailLogs as any).createdAt))
      .limit(limit)
      .offset(offset)
  );
  
  // Get total count
  let countQuery = (db as any)
    .select({ count: sql<number>`count(*)` })
    .from(emailLogs);
  
  if (conditions.length > 0) {
    countQuery = countQuery.where(and(...conditions));
  }
  
  const totalResult = await dbGet<any>(countQuery);
  const total = totalResult?.count || 0;
  
  return c.json({ 
    logs,
    pagination: {
      total,
      limit,
      offset,
      hasMore: offset + logs.length < total,
    }
  });
});

// Get single email log
emailsRouter.get('/logs/:id', requireAuth(['admin', 'organizer']), async (c) => {
  const { id } = c.req.param();
  
  const log = await dbGet<any>(
    (db as any).select().from(emailLogs).where(eq((emailLogs as any).id, id))
  );
  
  if (!log) {
    return c.json({ error: 'Email log not found' }, 404);
  }
  
  return c.json({ log });
});

// Resend email from log
emailsRouter.post('/logs/:id/resend', requireAuth(['admin', 'organizer']), async (c) => {
  const { id } = c.req.param();
  
  const result = await emailService.resendFromLog(id);
  
  if (!result.success && result.error === 'Email log not found') {
    return c.json({ error: 'Email log not found' }, 404);
  }
  
  if (!result.success && result.error === 'Email log missing required data to resend') {
    return c.json({ error: result.error }, 400);
  }
  
  return c.json({ success: result.success, error: result.error });
});

// Get email stats
emailsRouter.get('/stats', requireAuth(['admin', 'organizer']), async (c) => {
  const eventId = c.req.query('eventId');
  
  let baseCondition = eventId ? eq((emailLogs as any).eventId, eventId) : undefined;
  
  const totalQuery = baseCondition
    ? (db as any).select({ count: sql<number>`count(*)` }).from(emailLogs).where(baseCondition)
    : (db as any).select({ count: sql<number>`count(*)` }).from(emailLogs);
  
  const total = (await dbGet<any>(totalQuery))?.count || 0;
  
  const sentCondition = baseCondition
    ? and(baseCondition, eq((emailLogs as any).status, 'sent'))
    : eq((emailLogs as any).status, 'sent');
  const sent = (await dbGet<any>((db as any).select({ count: sql<number>`count(*)` }).from(emailLogs).where(sentCondition)))?.count || 0;
  
  const failedCondition = baseCondition
    ? and(baseCondition, eq((emailLogs as any).status, 'failed'))
    : eq((emailLogs as any).status, 'failed');
  const failed = (await dbGet<any>((db as any).select({ count: sql<number>`count(*)` }).from(emailLogs).where(failedCondition)))?.count || 0;
  
  const pendingCondition = baseCondition
    ? and(baseCondition, eq((emailLogs as any).status, 'pending'))
    : eq((emailLogs as any).status, 'pending');
  const pending = (await dbGet<any>((db as any).select({ count: sql<number>`count(*)` }).from(emailLogs).where(pendingCondition)))?.count || 0;
  
  return c.json({
    stats: {
      total,
      sent,
      failed,
      pending,
    }
  });
});

// Seed default templates (admin only)
emailsRouter.post('/seed-templates', requireAuth(['admin']), async (c) => {
  await emailService.seedDefaultTemplates();
  return c.json({ message: 'Default templates seeded successfully' });
});

// ==================== Configuration Routes ====================

// Get email provider info
emailsRouter.get('/config', requireAuth(['admin']), async (c) => {
  const providerInfo = emailService.getProviderInfo();
  return c.json(providerInfo);
});

// Test email configuration
emailsRouter.post('/test', requireAuth(['admin']), async (c) => {
  const body = await c.req.json();
  const { to } = body;
  
  if (!to) {
    return c.json({ error: 'Recipient email (to) is required' }, 400);
  }
  
  const result = await emailService.testConnection(to);
  return c.json(result);
});

// Get email queue status
emailsRouter.get('/queue/status', requireAuth(['admin']), async (c) => {
  const status = await getQueueStatus();
  return c.json({ status });
});

export default emailsRouter;
