/**
 * System Task Manager
 * Manages automated system tasks with enable/disable, scheduling, and alerting
 */

import { prisma } from '@/lib/db';
import { auditLogger } from './audit-logger';
import { VOCAB } from '@/lib/white-label/constants';

// Task definitions with metadata
export const SYSTEM_TASKS = {
  // Finance & AR
  dunning_runner: {
    key: 'dunning_runner',
    name: 'Daily Dunning Runner',
    description: 'Scan overdue AR records, escalate dunning levels, send automated payment reminders',
    category: 'finance',
    schedule: '0 9 * * *', // 9 AM daily
    isDefaultEnabled: true,
    isCritical: false,
  },
  bank_reconciliation: {
    key: 'bank_reconciliation',
    name: 'Nightly Bank Reconciliation',
    description: 'Match Stripe payouts against AR records, flag discrepancies for human review',
    category: 'finance',
    schedule: '0 2 * * *', // 2 AM daily
    isDefaultEnabled: true,
    isCritical: false,
  },
  cash_flow_forecast: {
    key: 'cash_flow_forecast',
    name: 'Weekly Cash Flow Forecast',
    description: 'Aggregate AR aging + expected payouts, email executive summary',
    category: 'finance',
    schedule: '0 8 * * 1', // Monday 8 AM
    isDefaultEnabled: false,
    isCritical: false,
  },
  ap_aging_alert: {
    key: 'ap_aging_alert',
    name: 'AP Aging Alert',
    description: 'Flag bills approaching due dates, prevent late payment fees',
    category: 'finance',
    schedule: '0 7 * * *', // 7 AM daily
    isDefaultEnabled: false,
    isCritical: false,
  },

  // Security & Compliance
  heartbeat_monitor: {
    key: 'heartbeat_monitor',
    name: 'Heartbeat Monitor',
    description: 'Continuous pulse check on critical services, alert on failure',
    category: 'security',
    schedule: '*/15 * * * *', // Every 15 minutes
    isDefaultEnabled: true,
    isCritical: true,
  },
  audit_log_digest: {
    key: 'audit_log_digest',
    name: 'Audit Log Digest',
    description: 'Daily summary of all agent actions, flagged anomalies',
    category: 'security',
    schedule: '0 6 * * *', // 6 AM daily
    isDefaultEnabled: false,
    isCritical: false,
  },
  access_review_reminder: {
    key: 'access_review_reminder',
    name: 'Access Review Reminder',
    description: 'Monthly prompt to review user permissions/RBAC',
    category: 'security',
    schedule: '0 9 1 * *', // 1st of month 9 AM
    isDefaultEnabled: false,
    isCritical: false,
  },
  compliance_checkpoint: {
    key: 'compliance_checkpoint',
    name: 'Compliance Checkpoint',
    description: 'Weekly scan for policy drift, expired certs, config changes',
    category: 'security',
    schedule: '0 10 * * 5', // Friday 10 AM
    isDefaultEnabled: false,
    isCritical: false,
  },

  // Marketing & Growth
  design_partner_digest: {
    key: 'design_partner_digest',
    name: 'Design Partner Pipeline Digest',
    description: 'Daily email summarizing new applications, follow-up queue',
    category: 'marketing',
    schedule: '0 8 * * *', // 8 AM daily
    isDefaultEnabled: false,
    isCritical: false,
  },
  competitor_intel_scan: {
    key: 'competitor_intel_scan',
    name: 'Competitor Intel Scan',
    description: 'Weekly scrape of competitor pricing/feature changes',
    category: 'marketing',
    schedule: '0 7 * * 1', // Monday 7 AM
    isDefaultEnabled: false,
    isCritical: false,
  },
  content_calendar_reminder: {
    key: 'content_calendar_reminder',
    name: 'Content Calendar Reminder',
    description: 'Weekly nudge with upcoming blog/social deadlines',
    category: 'marketing',
    schedule: '0 9 * * 1', // Monday 9 AM
    isDefaultEnabled: false,
    isCritical: false,
  },
  lead_scoring_update: {
    key: 'lead_scoring_update',
    name: 'Lead Scoring Update',
    description: 'Nightly recalc of lead scores based on engagement signals',
    category: 'marketing',
    schedule: '0 3 * * *', // 3 AM daily
    isDefaultEnabled: false,
    isCritical: false,
  },

  // Operations & Inventory
  inventory_reorder_trigger: {
    key: 'inventory_reorder_trigger',
    name: 'Inventory Reorder Trigger',
    description: 'Check stock levels, auto-generate POs when below threshold',
    category: 'operations',
    schedule: '0 8 * * *', // 8 AM daily
    isDefaultEnabled: false,
    isCritical: false,
  },
  order_fulfillment_health: {
    key: 'order_fulfillment_health',
    name: 'Order Fulfillment Health',
    description: 'Daily summary of stuck/delayed orders',
    category: 'operations',
    schedule: '0 7 * * *', // 7 AM daily
    isDefaultEnabled: false,
    isCritical: false,
  },
  vendor_performance_report: {
    key: 'vendor_performance_report',
    name: 'Vendor Performance Report',
    description: 'Weekly scorecard on supplier SLAs',
    category: 'operations',
    schedule: '0 9 * * 1', // Monday 9 AM
    isDefaultEnabled: false,
    isCritical: false,
  },

  // Data & Backup
  database_snapshot_verify: {
    key: 'database_snapshot_verify',
    name: 'Database Snapshot Verify',
    description: 'Nightly logical backup verification',
    category: 'data',
    schedule: '0 4 * * *', // 4 AM daily
    isDefaultEnabled: false,
    isCritical: false,
  },
  data_retention_cleanup: {
    key: 'data_retention_cleanup',
    name: 'Data Retention Cleanup',
    description: 'Monthly purge of soft-deleted records past retention window',
    category: 'data',
    schedule: '0 3 1 * *', // 1st of month 3 AM
    isDefaultEnabled: false,
    isCritical: false,
  },
  schema_drift_check: {
    key: 'schema_drift_check',
    name: 'Schema Drift Check',
    description: 'Alert if production schema diverges from expected state',
    category: 'data',
    schedule: '0 5 * * *', // 5 AM daily
    isDefaultEnabled: false,
    isCritical: false,
  },

  // HR
  payroll_reminder: {
    key: 'payroll_reminder',
    name: 'Payroll Processing Reminder',
    description: 'Reminder before payroll processing deadline',
    category: 'hr',
    schedule: '0 9 1,15 * *', // 1st and 15th 9 AM
    isDefaultEnabled: false,
    isCritical: false,
  },
  leave_balance_alert: {
    key: 'leave_balance_alert',
    name: 'Leave Balance Alert',
    description: 'Monthly summary of PTO balances and expirations',
    category: 'hr',
    schedule: '0 9 1 * *', // 1st of month 9 AM
    isDefaultEnabled: false,
    isCritical: false,
  },

  // Executive Intelligence
  weekly_syndicate_brief: {
    key: 'weekly_syndicate_brief',
    name: `Weekly ${VOCAB.brief}`,
    description: 'Auto-generated executive summary with metrics and insights',
    category: 'executive',
    schedule: '0 7 * * 1', // Monday 7 AM
    isDefaultEnabled: false,
    isCritical: false,
  },
  anomaly_digest: {
    key: 'anomaly_digest',
    name: 'Anomaly Digest',
    description: 'Daily roll-up of pattern engine flags',
    category: 'executive',
    schedule: '0 8 * * *', // 8 AM daily
    isDefaultEnabled: false,
    isCritical: false,
  },
} as const;

export type TaskKey = keyof typeof SYSTEM_TASKS;

/**
 * Initialize all system tasks in the database
 */
export async function initializeSystemTasks(): Promise<void> {
  for (const task of Object.values(SYSTEM_TASKS)) {
    await prisma.systemTask.upsert({
      where: { taskKey: task.key },
      update: {
        name: task.name,
        description: task.description,
        category: task.category,
        schedule: task.schedule,
        isCritical: task.isCritical,
      },
      create: {
        taskKey: task.key,
        name: task.name,
        description: task.description,
        category: task.category,
        schedule: task.schedule,
        isEnabled: task.isDefaultEnabled,
        isDefaultEnabled: task.isDefaultEnabled,
        isCritical: task.isCritical,
      },
    });
  }

  await auditLogger.logSystemEvent('system_tasks_initialized', {
    taskCount: Object.keys(SYSTEM_TASKS).length,
  });
}

/**
 * Get all system tasks
 */
export async function getSystemTasks(category?: string) {
  const where = category ? { category } : {};
  return prisma.systemTask.findMany({
    where,
    orderBy: [{ category: 'asc' }, { name: 'asc' }],
    include: {
      _count: { select: { runs: true, subscriptions: true } },
    },
  });
}

/**
 * Get a single system task
 */
export async function getSystemTask(taskKey: string) {
  return prisma.systemTask.findUnique({
    where: { taskKey },
    include: {
      runs: {
        orderBy: { startedAt: 'desc' },
        take: 10,
      },
      subscriptions: {
        include: {
          user: { select: { id: true, name: true, email: true } },
        },
      },
    },
  });
}

/**
 * Enable or disable a system task
 */
export async function setTaskEnabled(
  taskKey: string,
  enabled: boolean,
  userId?: string
): Promise<boolean> {
  const task = await prisma.systemTask.findUnique({ where: { taskKey } });
  if (!task) return false;
  
  if (task.isCritical && !enabled) {
    throw new Error('Cannot disable critical system task');
  }

  const previousState = { isEnabled: task.isEnabled };
  
  await prisma.systemTask.update({
    where: { taskKey },
    data: { isEnabled: enabled },
  });

  await auditLogger
    .setContext({ userId })
    .logConfigChange('task', task.id, previousState, { isEnabled: enabled }, 
      `Task ${taskKey} ${enabled ? 'enabled' : 'disabled'}`);

  return true;
}

/**
 * Update task configuration
 */
export async function updateTaskConfig(
  taskKey: string,
  config: Record<string, any>,
  userId?: string
): Promise<boolean> {
  const task = await prisma.systemTask.findUnique({ where: { taskKey } });
  if (!task) return false;

  const previousConfig = task.config;

  await prisma.systemTask.update({
    where: { taskKey },
    data: { config },
  });

  await auditLogger
    .setContext({ userId })
    .logConfigChange('task', task.id, previousConfig as Record<string, any>, config,
      `Task ${taskKey} config updated`);

  return true;
}

/**
 * Start a task run
 */
export async function startTaskRun(
  taskKey: string,
  triggeredBy: 'scheduler' | 'manual' | 'webhook' = 'scheduler',
  triggeredById?: string,
  input?: Record<string, any>
): Promise<string | null> {
  const task = await prisma.systemTask.findUnique({ where: { taskKey } });
  if (!task || !task.isEnabled) return null;

  const run = await prisma.taskRunLog.create({
    data: {
      taskId: task.id,
      triggeredBy,
      triggeredById,
      input: input || {},
    },
  });

  await prisma.systemTask.update({
    where: { taskKey },
    data: {
      lastRunAt: new Date(),
      lastRunStatus: 'running',
      runCount: { increment: 1 },
    },
  });

  await auditLogger
    .setContext({ userId: triggeredById })
    .logTaskExecution(taskKey, run.id, 'start');

  return run.id;
}

/**
 * Complete a task run
 */
export async function completeTaskRun(
  runId: string,
  status: 'success' | 'failed' | 'cancelled',
  output?: {
    itemsProcessed?: number;
    itemsFailed?: number;
    error?: string;
    data?: Record<string, any>;
    alerts?: Array<{ severity: string; title: string; message: string; data?: any }>;
  }
): Promise<void> {
  const run = await prisma.taskRunLog.findUnique({
    where: { id: runId },
    include: { task: true },
  });
  if (!run) return;

  const duration = Date.now() - run.startedAt.getTime();

  await prisma.taskRunLog.update({
    where: { id: runId },
    data: {
      status,
      completedAt: new Date(),
      duration,
      output: output?.data ?? undefined,
      error: output?.error,
      itemsProcessed: output?.itemsProcessed || 0,
      itemsFailed: output?.itemsFailed || 0,
      alerts: output?.alerts || [],
    },
  });

  await prisma.systemTask.update({
    where: { id: run.taskId },
    data: {
      lastRunStatus: status,
      lastRunDuration: duration,
      lastRunError: output?.error,
      failCount: status === 'failed' ? { increment: 1 } : undefined,
    },
  });

  // Create task alerts
  if (output?.alerts && output.alerts.length > 0) {
    await prisma.taskAlert.createMany({
      data: output.alerts.map((a: any) => ({
        taskId: run.taskId,
        runId,
        severity: a.severity,
        title: a.title,
        message: a.message,
        data: a.data || {},
      })),
    });

    // Notify subscribed users
    await notifySubscribers(run.taskId, output.alerts);
  }

  // Notify on failure/success based on subscriptions
  if (status === 'failed') {
    await notifySubscribersOnFailure(run.taskId, run.task.taskKey, output?.error);
  } else if (status === 'success') {
    await notifySubscribersOnSuccess(run.taskId, run.task.taskKey);
  }

  await auditLogger.logTaskExecution(run.task.taskKey, runId, 
    status === 'success' ? 'complete' : 'fail', {
      executionTimeMs: duration,
      itemsProcessed: output?.itemsProcessed,
      error: output?.error,
    });
}

/**
 * Subscribe user to task alerts
 */
export async function subscribeToTask(
  userId: string,
  taskId: string,
  options?: {
    notifyOnSuccess?: boolean;
    notifyOnFailure?: boolean;
    notifyOnAlert?: boolean;
    notifyChannels?: string[];
  }
): Promise<void> {
  await prisma.userTaskSubscription.upsert({
    where: { userId_taskId: { userId, taskId } },
    update: {
      isSubscribed: true,
      notifyOnSuccess: options?.notifyOnSuccess ?? false,
      notifyOnFailure: options?.notifyOnFailure ?? true,
      notifyOnAlert: options?.notifyOnAlert ?? true,
      notifyChannels: options?.notifyChannels || ['in_app'],
    },
    create: {
      userId,
      taskId,
      isSubscribed: true,
      notifyOnSuccess: options?.notifyOnSuccess ?? false,
      notifyOnFailure: options?.notifyOnFailure ?? true,
      notifyOnAlert: options?.notifyOnAlert ?? true,
      notifyChannels: options?.notifyChannels || ['in_app'],
    },
  });
}

/**
 * Unsubscribe user from task alerts
 */
export async function unsubscribeFromTask(
  userId: string,
  taskId: string
): Promise<void> {
  await prisma.userTaskSubscription.updateMany({
    where: { userId, taskId },
    data: { isSubscribed: false },
  });
}

/**
 * Auto-subscribe users based on role
 */
export async function autoSubscribeByRole(
  taskKey: string,
  roleNames: string[]
): Promise<number> {
  const task = await prisma.systemTask.findUnique({ where: { taskKey } });
  if (!task) return 0;

  // Get users with specified roles
  const userRoles = await prisma.userRole.findMany({
    where: {
      role: { name: { in: roleNames } },
    },
    include: { user: true },
  });

  const userIds = [...new Set(userRoles.map((ur: any) => ur.userId))];

  // Create subscriptions
  let count = 0;
  for (const userId of userIds) {
    await prisma.userTaskSubscription.upsert({
      where: { userId_taskId: { userId, taskId: task.id } },
      update: {},
      create: {
        userId,
        taskId: task.id,
        isSubscribed: true,
      },
    });
    count++;
  }

  return count;
}

// Internal: Notify subscribers on alerts
async function notifySubscribers(
  taskId: string,
  alerts: Array<{ severity: string; title: string; message: string; data?: any }>
): Promise<void> {
  const subscriptions = await prisma.userTaskSubscription.findMany({
    where: { taskId, isSubscribed: true, notifyOnAlert: true },
  });

  for (const sub of subscriptions) {
    for (const alert of alerts) {
      await prisma.userNotification.create({
        data: {
          userId: sub.userId,
          type: 'task_alert',
          title: alert.title,
          message: alert.message,
          data: { severity: alert.severity, taskId, ...alert.data },
        },
      });
    }
  }
}

// Internal: Notify subscribers on failure
async function notifySubscribersOnFailure(
  taskId: string,
  taskKey: string,
  error?: string
): Promise<void> {
  const subscriptions = await prisma.userTaskSubscription.findMany({
    where: { taskId, isSubscribed: true, notifyOnFailure: true },
  });

  for (const sub of subscriptions) {
    await prisma.userNotification.create({
      data: {
        userId: sub.userId,
        type: 'task_alert',
        title: `Task Failed: ${taskKey}`,
        message: error || 'Task execution failed',
        data: { severity: 'error', taskId, taskKey },
      },
    });
  }
}

// Internal: Notify subscribers on success
async function notifySubscribersOnSuccess(
  taskId: string,
  taskKey: string
): Promise<void> {
  const subscriptions = await prisma.userTaskSubscription.findMany({
    where: { taskId, isSubscribed: true, notifyOnSuccess: true },
  });

  for (const sub of subscriptions) {
    await prisma.userNotification.create({
      data: {
        userId: sub.userId,
        type: 'task_alert',
        title: `Task Completed: ${taskKey}`,
        message: 'Task executed successfully',
        data: { severity: 'info', taskId, taskKey },
      },
    });
  }
}
