/**
 * Campaign Scheduler Service
 * Handles scheduled email campaign sending with cron jobs
 */

import { CronJob } from 'cron';
import { getDb } from '../db.ts';
import { emailCampaigns } from '../../drizzle/schema.ts';
import { eq, lte, and } from 'drizzle-orm';
import { sendEmailCampaign, initializeBrevo } from './brevo.ts';
import { getTemplate, renderTemplate } from './emailTemplates.ts';

interface ScheduledCampaign {
  id: number;
  name: string;
  scheduledTime: Date;
  cronJob?: CronJob;
}

const scheduledCampaigns = new Map<number, ScheduledCampaign>();

/**
 * Initialize campaign scheduler
 * Loads all pending campaigns and sets up cron jobs
 */
export async function initializeCampaignScheduler() {
  try {
    console.log('[CampaignScheduler] Initializing...');

    const db = await getDb();
    if (!db) {
      console.error('[CampaignScheduler] Database unavailable');
      return;
    }

    // Initialize Brevo
    initializeBrevo({
      host: process.env.BREVO_SMTP_HOST || 'smtp-relay.brevo.com',
      port: parseInt(process.env.BREVO_SMTP_PORT || '587'),
      user: process.env.BREVO_SMTP_USER || '',
      pass: process.env.BREVO_SMTP_PASS || '',
    });

    // Load all scheduled campaigns
    const campaigns = await db
      .select()
      .from(emailCampaigns)
      .where(and(
        eq(emailCampaigns.status, 'scheduled'),
        lte(emailCampaigns.scheduledTime, new Date())
      ));

    console.log(`[CampaignScheduler] Found ${campaigns.length} pending campaigns`);

    for (const campaign of campaigns) {
      if (campaign.scheduledTime) {
        scheduleEmailCampaign(campaign.id, campaign.name, campaign.scheduledTime);
      }
    }

    console.log('[CampaignScheduler] Initialized successfully');
  } catch (error) {
    console.error('[CampaignScheduler] Initialization failed:', error);
  }
}

/**
 * Schedule an email campaign for sending
 */
export function scheduleEmailCampaign(
  campaignId: number,
  campaignName: string,
  scheduledTime: Date
) {
  try {
    // Calculate cron expression from scheduled time
    const cron = getCronExpression(scheduledTime);

    console.log(`[CampaignScheduler] Scheduling campaign ${campaignId} (${campaignName}) for ${scheduledTime}`);

    // Create cron job
    const job = new CronJob(cron, async () => {
      console.log(`[CampaignScheduler] Executing campaign ${campaignId}`);
      await executeCampaign(campaignId);
    });

    // Store scheduled campaign
    scheduledCampaigns.set(campaignId, {
      id: campaignId,
      name: campaignName,
      scheduledTime,
      cronJob: job,
    });

    // Start the job
    job.start();

    console.log(`[CampaignScheduler] Campaign ${campaignId} scheduled successfully`);
  } catch (error) {
    console.error(`[CampaignScheduler] Failed to schedule campaign ${campaignId}:`, error);
  }
}

/**
 * Execute a scheduled campaign
 */
async function executeCampaign(campaignId: number) {
  try {
    const db = await getDb();
    if (!db) {
      console.error('[CampaignScheduler] Database unavailable');
      return;
    }

    // Get campaign
    const campaign = await db.query.emailCampaigns.findFirst({
      where: eq(emailCampaigns.id, campaignId),
    });

    if (!campaign) {
      console.error(`[CampaignScheduler] Campaign ${campaignId} not found`);
      return;
    }

    console.log(`[CampaignScheduler] Sending campaign ${campaignId}: ${campaign.name}`);

    // Get recipients based on segment
    const { users } = await import('../db.ts');
    let recipientQuery = db.select({ email: users.email, id: users.id }).from(users);

    if (campaign.recipientSegment === 'vip') {
      recipientQuery = recipientQuery.where(eq(users.role, 'admin'));
    } else if (campaign.recipientSegment === 'new') {
      const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
      recipientQuery = recipientQuery.where(gte(users.createdAt, thirtyDaysAgo));
    }

    const recipients = await recipientQuery;

    if (recipients.length === 0) {
      console.warn(`[CampaignScheduler] No recipients found for campaign ${campaignId}`);
      return;
    }

    // Send campaign
    const recipientEmails = recipients.map(r => r.email);
    const sendResult = await sendEmailCampaign({
      campaignId: campaignId.toString(),
      subject: campaign.subject,
      htmlContent: campaign.template,
      recipients: recipientEmails,
      fromEmail: 'noreply@playcoinkrazy.com',
      fromName: 'CoinKrazy',
      trackingEnabled: true,
    });

    if (!sendResult.success) {
      console.error(`[CampaignScheduler] Failed to send campaign ${campaignId}: ${sendResult.error}`);
      return;
    }

    // Update campaign status
    await db
      .update(emailCampaigns)
      .set({
        status: 'sent',
        sentAt: new Date(),
        recipientCount: recipientEmails.length,
        updatedAt: new Date(),
      })
      .where(eq(emailCampaigns.id, campaignId));

    console.log(`[CampaignScheduler] Campaign ${campaignId} sent successfully to ${recipientEmails.length} recipients`);

    // Remove from scheduled campaigns
    const scheduled = scheduledCampaigns.get(campaignId);
    if (scheduled?.cronJob) {
      scheduled.cronJob.stop();
    }
    scheduledCampaigns.delete(campaignId);
  } catch (error) {
    console.error(`[CampaignScheduler] Error executing campaign ${campaignId}:`, error);
  }
}

/**
 * Convert scheduled time to cron expression
 * Supports one-time execution at specific time
 */
function getCronExpression(scheduledTime: Date): string {
  const second = scheduledTime.getSeconds();
  const minute = scheduledTime.getMinutes();
  const hour = scheduledTime.getHours();
  const day = scheduledTime.getDate();
  const month = scheduledTime.getMonth() + 1;

  // Cron format: second minute hour day month day-of-week
  return `${second} ${minute} ${hour} ${day} ${month} *`;
}

/**
 * Cancel a scheduled campaign
 */
export function cancelScheduledCampaign(campaignId: number): boolean {
  const scheduled = scheduledCampaigns.get(campaignId);

  if (!scheduled) {
    console.warn(`[CampaignScheduler] Campaign ${campaignId} not found in scheduled list`);
    return false;
  }

  if (scheduled.cronJob) {
    scheduled.cronJob.stop();
  }

  scheduledCampaigns.delete(campaignId);

  console.log(`[CampaignScheduler] Campaign ${campaignId} cancelled`);
  return true;
}

/**
 * Get all scheduled campaigns
 */
export function getScheduledCampaigns(): ScheduledCampaign[] {
  return Array.from(scheduledCampaigns.values());
}

/**
 * Shutdown campaign scheduler
 */
export function shutdownCampaignScheduler() {
  console.log('[CampaignScheduler] Shutting down...');

  scheduledCampaigns.forEach((campaign) => {
    if (campaign.cronJob) {
      campaign.cronJob.stop();
    }
  });

  scheduledCampaigns.clear();

  console.log('[CampaignScheduler] Shutdown complete');
}
