import { eq, and, lt, sql } from 'drizzle-orm';
import { db } from '../db/index';
import { tasks } from '../db/schema';
import { eventBus } from '../events/bus';
import { createNotification } from './notification.service';

// ═══════════════════════════════════════════════════════════
// Escalation Service — SLA monitoring + escalation ladder
// ═══════════════════════════════════════════════════════════

/**
 * Escalation ladder:
 *
 * Level 0 (T+0min)     Task created → Notification to assignee (dashboard + Discord)
 * Level 1 (T+30min)    Reminder #1  → Dashboard badge + Discord DM
 * Level 2 (T+2h)       Reminder #2  → Discord channel mention + email
 * Level 3 (T+4h)       SLA Warning  → Discord @here + push notification
 * Level 4 (T+SLA)      SLA Breach   → Auto-escalate to admin, Discord @everyone
 * Level 5 (T+SLA+2h)   Critical     → SMS/phone notification to owner
 */

export const ESCALATION_LEVELS = [
  {
    level: 0,
    label: 'initial',
    channels: ['dashboard', 'discord'],
    description: 'Task created — initial notification',
  },
  {
    level: 1,
    label: 'reminder',
    channels: ['dashboard', 'discord'],
    description: 'First reminder (30min)',
    minutesBefore: 30 * 60, // 30h before deadline (early)
  },
  {
    level: 2,
    label: 'warning',
    channels: ['dashboard', 'discord', 'email'],
    description: 'Second reminder with email (2h before)',
    minutesBefore: 120,
  },
  {
    level: 3,
    label: 'urgent',
    channels: ['dashboard', 'discord', 'push'],
    description: 'SLA warning — urgent (30min before)',
    minutesBefore: 30,
  },
  {
    level: 4,
    label: 'breach',
    channels: ['dashboard', 'discord', 'email', 'sms'],
    description: 'SLA breached — auto-escalate to admin',
    minutesBefore: 0,
  },
  {
    level: 5,
    label: 'critical',
    channels: ['dashboard', 'discord', 'email', 'sms', 'push'],
    description: 'Critical — owner notification (2h after breach)',
    minutesBefore: -120,
  },
] as const;

/**
 * Get the escalation level for a given minutes-until-deadline
 */
function getEscalationLevel(minutesRemaining: number): number {
  if (minutesRemaining <= -120) return 5; // 2h+ past breach
  if (minutesRemaining <= 0) return 4;    // breached
  if (minutesRemaining <= 30) return 3;   // urgent
  if (minutesRemaining <= 120) return 2;  // warning
  return 1;                               // reminder
}

/**
 * Check for tasks approaching or exceeding SLA deadlines.
 * Should be called periodically (e.g., every minute via cron/interval).
 */
export async function checkSLAs(): Promise<void> {
  const now = new Date();

  // Find pending tasks with SLA deadlines
  const pendingTasks = await db
    .select()
    .from(tasks)
    .where(
      and(
        sql`${tasks.status} IN ('pending', 'claimed')`,
        sql`${tasks.slaDeadline} IS NOT NULL`
      )
    );

  for (const task of pendingTasks) {
    if (!task.slaDeadline) continue;

    const deadline = new Date(task.slaDeadline);
    const minutesUntilDeadline = (deadline.getTime() - now.getTime()) / (1000 * 60);
    const currentLevel = task.escalationLevel || 0;
    const targetLevel = getEscalationLevel(minutesUntilDeadline);

    // Only escalate upward, never downward
    if (targetLevel <= currentLevel) continue;

    // Handle each escalation level
    if (targetLevel >= 4 && !task.slaBreached) {
      await handleSLABreach(task, minutesUntilDeadline, targetLevel);
    } else if (targetLevel === 3 && task.slaWarningsSent === 0) {
      await handleSLAWarning(task, minutesUntilDeadline);
    } else if (targetLevel >= 2 && currentLevel < 2) {
      await handleEscalation(task, minutesUntilDeadline, targetLevel);
    }
  }
}

async function handleEscalation(
  task: typeof tasks.$inferSelect,
  minutesRemaining: number,
  level: number
) {
  const now = new Date();
  const levelConfig = ESCALATION_LEVELS[level] || ESCALATION_LEVELS[1];

  await db
    .update(tasks)
    .set({ escalationLevel: level, updatedAt: now })
    .where(eq(tasks.id, task.id));

  if (task.assigneeId) {
    await createNotification({
      userId: task.assigneeId,
      type: 'sla_warning',
      title: `📋 Reminder: ${task.title}`,
      body: `${Math.round(minutesRemaining)} minutes remaining. ${levelConfig.description}`,
      channels: [...levelConfig.channels],
      entityType: 'task',
      entityId: task.id,
      actionUrl: `/tasks/${task.id}`,
    });
  }
}

async function handleSLAWarning(
  task: typeof tasks.$inferSelect,
  minutesRemaining: number
) {
  const now = new Date();

  // Mark warning sent
  await db
    .update(tasks)
    .set({
      slaWarningsSent: 1,
      escalationLevel: 3,
      updatedAt: now,
    })
    .where(eq(tasks.id, task.id));

  // Send notification
  if (task.assigneeId) {
    await createNotification({
      userId: task.assigneeId,
      type: 'sla_warning',
      title: `⚠️ SLA Warning: ${task.title}`,
      body: `${Math.round(minutesRemaining)} minutes remaining before SLA breach`,
      channels: ['dashboard', 'discord', 'push'],
      entityType: 'task',
      entityId: task.id,
      actionUrl: `/tasks/${task.id}`,
    });
  }

  // Emit event
  await eventBus.emit({
    type: 'task.sla_warning',
    timestamp: now.toISOString(),
    data: {
      taskId: task.id,
      taskTitle: task.title,
      pipelineId: task.pipelineId,
      slaDeadline: task.slaDeadline?.toISOString() || '',
      minutesRemaining: Math.round(minutesRemaining),
      escalationLevel: 3,
    },
    actor: { type: 'system', id: 'sla-monitor' },
  });
}

async function handleSLABreach(
  task: typeof tasks.$inferSelect,
  minutesRemaining: number,
  level: number
) {
  const now = new Date();

  // Mark breached, escalate
  await db
    .update(tasks)
    .set({
      slaBreached: true,
      slaWarningsSent: 1,
      escalationLevel: level,
      updatedAt: now,
    })
    .where(eq(tasks.id, task.id));

  const levelConfig = ESCALATION_LEVELS[Math.min(level, 5)];

  // Send urgent notification
  if (task.assigneeId) {
    await createNotification({
      userId: task.assigneeId,
      type: 'sla_breach',
      title: `🚨 SLA BREACHED: ${task.title}`,
      body: `This task has exceeded its SLA deadline by ${Math.abs(Math.round(minutesRemaining))} minutes. Immediate action required.`,
      channels: [...levelConfig.channels],
      entityType: 'task',
      entityId: task.id,
      actionUrl: `/tasks/${task.id}`,
    });
  }

  // Emit event
  await eventBus.emit({
    type: 'task.sla_breached',
    timestamp: now.toISOString(),
    data: {
      taskId: task.id,
      taskTitle: task.title,
      pipelineId: task.pipelineId,
      slaDeadline: task.slaDeadline?.toISOString() || '',
      minutesRemaining: Math.round(minutesRemaining),
      escalationLevel: level,
    },
    actor: { type: 'system', id: 'sla-monitor' },
  });

  // Auto-escalate task status
  if (task.status === 'pending') {
    await db
      .update(tasks)
      .set({ status: 'escalated', updatedAt: now })
      .where(eq(tasks.id, task.id));

    await eventBus.emit({
      type: 'task.escalated',
      timestamp: now.toISOString(),
      data: {
        taskId: task.id,
        taskTitle: task.title,
        pipelineId: task.pipelineId,
        escalationLevel: level,
        reason: 'SLA breach — auto-escalated',
      },
      actor: { type: 'system', id: 'sla-monitor' },
    });
  }
}

/**
 * Start SLA monitoring loop (checks every 60 seconds)
 */
let slaInterval: ReturnType<typeof setInterval> | null = null;

export function startSLAMonitor(intervalMs = 60_000): void {
  if (slaInterval) return;
  console.log(`[SLA Monitor] Starting, checking every ${intervalMs / 1000}s`);

  // Run immediately on start
  checkSLAs().catch((err) => {
    console.error('[SLA Monitor] Initial check failed:', err);
  });

  slaInterval = setInterval(() => {
    checkSLAs().catch((err) => {
      console.error('[SLA Monitor] Check failed:', err);
    });
  }, intervalMs);
}

export function stopSLAMonitor(): void {
  if (slaInterval) {
    clearInterval(slaInterval);
    slaInterval = null;
    console.log('[SLA Monitor] Stopped');
  }
}
