import { prisma } from '@/lib/db';
import { sendReminderForBooking } from '@/lib/services/demo-reminder.service';
import { sendDepositReminderForBooking } from '@/lib/services/deposit-reminder.service';
import { trialEmailsService } from '@/lib/services/trial-emails.service';
import { stripe } from '@/lib/stripe';
import { logger } from '@/lib/logger';
import { sendEmailImmediate } from '@/lib/email';
import { healthAlertsService } from '@/lib/services/health-alerts.service';
import { driftDetectionService } from '@/lib/services/drift-detection.service';

type TrialPayload = { subscriptionId: string; daysRemaining: 1 | 3 };
type DemoPayload = { bookingId: string };
type DepositReminderPayload = { bookingId: string };

/**
 * Email job payload for generic email sending via queue
 */
export type EmailPayload = {
  to: string;
  subject: string;
  html: string;
  text: string;
  category?: string; // For tracking/analytics
  metadata?: Record<string, unknown>;
  attachments?: Array<{ filename: string; content: string; encoding: 'base64' }>;
};

/**
 * Web vitals alert payload
 */
export type WebVitalsAlertPayload = {
  vitalName: 'LCP' | 'TTFB';
  alertLevel: 'warning' | 'critical';
  currentP90: number;
  threshold: number;
  sampleCount: number;
};

/**
 * Drift check job payload
 */
export type DriftCheckPayload = {
  countThreshold?: number;
  percentageThreshold?: number;
  sendAlertEmail?: boolean;
};

/**
 * DLQ archival job payload
 */
export type DlqArchivalPayload = {
  /** Force archival even if not scheduled */
  force?: boolean;
};

export async function handleDemoReminderJob(payload: DemoPayload): Promise<void> {
  const result = await sendReminderForBooking(payload.bookingId);

  if (!result.emailSent && !result.whatsAppSent) {
    throw new Error(result.error || 'Demo reminder failed for both channels');
  }
}

export async function handleDepositReminderJob(payload: DepositReminderPayload): Promise<void> {
  const result = await sendDepositReminderForBooking(payload.bookingId);

  if (!result.emailSent) {
    throw new Error(result.error || 'Deposit reminder email failed');
  }
}

async function getSubscriptionWithUser(subscriptionId: string) {
  return prisma.subscriptions.findUnique({
    where: { stripeSubscriptionId: subscriptionId },
    select: {
      id: true,
      userId: true,
      stripeSubscriptionId: true,
      stripePriceId: true,
      tierId: true,
      trialEndsAt: true,
      status: true,
      users: {
        select: {
          email: true,
          name: true,
          stripeCustomerId: true,
        },
      },
    },
  });
}

export async function handleTrialReminderJob(payload: TrialPayload): Promise<void> {
  const subscription = await getSubscriptionWithUser(payload.subscriptionId);

  if (!subscription || subscription.status !== 'trialing') {
    throw new Error('Subscription not in trialing state');
  }

  if (!subscription.trialEndsAt) {
    throw new Error('Missing trial end date');
  }

  let amount = 0;
  let currency = 'gbp';

  try {
    const price = await stripe.prices.retrieve(subscription.stripePriceId);
    amount = price.unit_amount ? price.unit_amount / 100 : 0;
    currency = price.currency || 'gbp';
  } catch (stripeError) {
    logger.warn('Failed to retrieve Stripe price for trial reminder', {
      priceId: subscription.stripePriceId,
      error: stripeError instanceof Error ? stripeError.message : String(stripeError),
    });
  }

  const hasPaymentMethod = await trialEmailsService.hasPaymentMethod(
    subscription.stripeSubscriptionId
  );

  if (!hasPaymentMethod) {
    await trialEmailsService.sendPaymentMethodMissingEmail({
      userEmail: subscription.users.email,
      userName: subscription.users.name || subscription.users.email,
      trialEndDate: subscription.trialEndsAt,
      planName: subscription.tierId,
      amount,
      currency,
    });
    return;
  }

  const emailSent = await trialEmailsService.sendTrialEndingEmail({
    userEmail: subscription.users.email,
    userName: subscription.users.name || subscription.users.email,
    daysRemaining: payload.daysRemaining,
    trialEndDate: subscription.trialEndsAt,
    planName: subscription.tierId,
    amount,
    currency,
    subscriptionId: subscription.stripeSubscriptionId,
  });

  if (!emailSent) {
    throw new Error('Trial reminder email not sent (likely already delivered)');
  }
}

/**
 * Handle generic email job
 * Sends email via SMTP with retry support through job queue
 */
export async function handleEmailJob(payload: EmailPayload): Promise<void> {
  const { to, subject, html, text, category, metadata, attachments } = payload;

  logger.debug('Processing email job', { to, subject, category });

  const success = await sendEmailImmediate(to, { subject, html, text, attachments });

  if (!success) {
    throw new Error(`Failed to send email to ${to}`);
  }

  logger.info('Email job completed', { to, category, metadata });
}

/**
 * Handle web vitals alert job
 * Notifies administrators when LCP/TTFB exceeds thresholds
 */
export async function handleWebVitalsAlertJob(payload: WebVitalsAlertPayload): Promise<void> {
  const { vitalName, alertLevel, currentP90, threshold, sampleCount } = payload;

  logger.warn(`Web Vitals Alert: ${vitalName} ${alertLevel}`, {
    vitalName,
    alertLevel,
    currentP90,
    threshold,
    sampleCount,
  });

  // Send alert notification
  await healthAlertsService.sendWebVitalsAlert({
    vitalName,
    alertLevel,
    currentP90,
    threshold,
    sampleCount,
    timestamp: new Date().toISOString(),
  });
}

/**
 * Handle drift check job
 * Runs drift detection between Prisma and Supabase and sends alerts if thresholds exceeded
 */
export async function handleDriftCheckJob(payload: DriftCheckPayload): Promise<void> {
  const { countThreshold, percentageThreshold, sendAlertEmail = true } = payload;

  logger.info('Running scheduled drift check job');

  // Apply custom thresholds if provided
  if (countThreshold || percentageThreshold) {
    driftDetectionService.updateConfig({
      ...(countThreshold ? { countThreshold } : {}),
      ...(percentageThreshold ? { percentageThreshold } : {}),
    });
  }

  // Run drift detection
  const report = await driftDetectionService.runDriftCheck();

  // Log the results
  logger.info('Drift check job completed', {
    status: report.status,
    tablesChecked: report.summary.totalTablesChecked,
    tablesWithDrift: report.summary.tablesWithDrift,
    totalMissingRecords: report.summary.totalMissingRecords,
    totalOrphanedRecords: report.summary.totalOrphanedRecords,
    highestDriftPercentage: report.summary.highestDriftPercentage,
    durationMs: report.durationMs,
  });

  // Send alert email if drift exceeds threshold
  if (sendAlertEmail && (report.status === 'warning' || report.status === 'critical')) {
    const adminEmail = process.env.ADMIN_ALERT_EMAIL;

    if (!adminEmail) {
      logger.warn('ADMIN_ALERT_EMAIL not configured, skipping drift alert email');
      return;
    }

    const severity = report.status === 'critical' ? 'CRITICAL' : 'WARNING';
    const tablesWithDrift = report.tables.filter((t) => t.exceedsThreshold);

    const tableRows = tablesWithDrift
      .map(
        (t) => `
        <tr style="background: ${t.missingInSupabase.length > 50 ? '#fef2f2' : '#fefce8'};">
          <td style="padding: 8px; border: 1px solid #e5e7eb;">${t.tableName}</td>
          <td style="padding: 8px; border: 1px solid #e5e7eb;">${t.prismaCount}</td>
          <td style="padding: 8px; border: 1px solid #e5e7eb;">${t.supabaseCount}</td>
          <td style="padding: 8px; border: 1px solid #e5e7eb; color: #dc2626; font-weight: bold;">${t.missingInSupabase.length}</td>
          <td style="padding: 8px; border: 1px solid #e5e7eb;">${t.driftPercentage.toFixed(2)}%</td>
        </tr>`
      )
      .join('');

    const subject = `[${severity}] Database Drift Detected - ${report.summary.totalMissingRecords} missing records`;

    const html = `
      <div style="font-family: sans-serif; max-width: 800px; margin: 0 auto;">
        <h2 style="color: ${report.status === 'critical' ? '#dc2626' : '#ca8a04'};">
          Database Drift Alert: ${severity}
        </h2>
        <p>Drift has been detected between Prisma and Supabase databases.</p>

        <h3>Summary</h3>
        <ul>
          <li><strong>Tables with drift:</strong> ${report.summary.tablesWithDrift} of ${report.summary.totalTablesChecked}</li>
          <li><strong>Total missing records:</strong> ${report.summary.totalMissingRecords}</li>
          <li><strong>Total orphaned records:</strong> ${report.summary.totalOrphanedRecords}</li>
          <li><strong>Highest drift:</strong> ${report.summary.highestDriftPercentage.toFixed(2)}%</li>
        </ul>

        <h3>Affected Tables</h3>
        <table style="width: 100%; border-collapse: collapse; margin: 20px 0;">
          <thead>
            <tr style="background: #f3f4f6;">
              <th style="padding: 8px; border: 1px solid #e5e7eb; text-align: left;">Table</th>
              <th style="padding: 8px; border: 1px solid #e5e7eb; text-align: left;">Prisma Count</th>
              <th style="padding: 8px; border: 1px solid #e5e7eb; text-align: left;">Supabase Count</th>
              <th style="padding: 8px; border: 1px solid #e5e7eb; text-align: left;">Missing</th>
              <th style="padding: 8px; border: 1px solid #e5e7eb; text-align: left;">Drift %</th>
            </tr>
          </thead>
          <tbody>
            ${tableRows}
          </tbody>
        </table>

        <h3>Recommended Actions</h3>
        <ol>
          <li>Run <code>npx tsx scripts/reconcile-drift.ts</code> to view detailed missing records</li>
          <li>Review recent deployments and sync operations</li>
          <li>Check the sync_queue table for failed operations</li>
        </ol>

        <p style="color: #666; font-size: 12px; margin-top: 30px;">
          Generated at: ${report.generatedAt}<br/>
          Duration: ${report.durationMs}ms
        </p>
      </div>
    `;

    const text = `
Database Drift Alert: ${severity}

Drift has been detected between Prisma and Supabase databases.

Summary:
- Tables with drift: ${report.summary.tablesWithDrift} of ${report.summary.totalTablesChecked}
- Total missing records: ${report.summary.totalMissingRecords}
- Total orphaned records: ${report.summary.totalOrphanedRecords}
- Highest drift: ${report.summary.highestDriftPercentage.toFixed(2)}%

Affected Tables:
${tablesWithDrift.map((t) => `- ${t.tableName}: ${t.missingInSupabase.length} missing (${t.driftPercentage.toFixed(2)}% drift)`).join('\n')}

Run: npx tsx scripts/reconcile-drift.ts
    `.trim();

    try {
      const emailSent = await sendEmailImmediate(adminEmail, {
        subject,
        html,
        text,
      });

      if (emailSent) {
        logger.info('Drift alert email sent', { adminEmail, severity });
      } else {
        logger.error('Failed to send drift alert email', undefined, { adminEmail });
      }
    } catch (error) {
      logger.error('Error sending drift alert email', error);
    }
  }
}

/**
 * Handle DLQ archival job
 * Archives items older than 30 days and checks SLA violations
 * Should be scheduled to run daily (e.g., at midnight)
 */
export async function handleDlqArchivalJob(payload: DlqArchivalPayload = {}): Promise<void> {
  const { runDlqArchivalJob } = await import('@/lib/sync-queue');

  logger.info('Starting DLQ archival job', { payload });

  const result = await runDlqArchivalJob();

  logger.info('DLQ archival job completed', {
    archived: result.archived,
    exceedingSla: result.exceedingSla,
    alertRequired: result.alertRequired,
  });

  // If there are SLA violations, send an alert
  if (result.alertRequired) {
    await healthAlertsService.sendDlqSlaAlert({
      exceedingSla: result.exceedingSla,
      archived: result.archived,
      timestamp: new Date().toISOString(),
    });
  }
}
