/**
 * Automation Background Worker
 *
 * Processes workflow instances, handles delayed execution, and manages triggers.
 * Runs as a background process that polls for work.
 */

import { getModels } from '../models';
import { ExecutionEngine } from '../services/automation/ExecutionEngine';
import { DelayScheduler } from '../services/automation/DelayScheduler';
import { InstanceManager } from '../services/automation/InstanceManager';
import { TriggerService } from '../services/automation/TriggerService';

// ============================================
// WORKER CONFIGURATION
// ============================================

interface WorkerConfig {
  // Polling interval in milliseconds
  pollInterval: number;
  // Maximum instances to process per batch
  batchSize: number;
  // Enable verbose logging
  verbose: boolean;
}

const DEFAULT_CONFIG: WorkerConfig = {
  pollInterval: 60000, // 1 minute
  batchSize: 100,
  verbose: process.env.NODE_ENV === 'development',
};

// ============================================
// AUTOMATION WORKER CLASS
// ============================================

export class AutomationWorker {
  private config: WorkerConfig;
  private executionEngine: ExecutionEngine;
  private delayScheduler: DelayScheduler;
  private instanceManager: InstanceManager;
  private triggerService: TriggerService;
  private isRunning: boolean = false;
  private intervalId: NodeJS.Timeout | null = null;

  constructor(config: Partial<WorkerConfig> = {}) {
    this.config = { ...DEFAULT_CONFIG, ...config };
    this.executionEngine = new ExecutionEngine();
    this.delayScheduler = new DelayScheduler();
    this.instanceManager = new InstanceManager();
    this.triggerService = new TriggerService();
  }

  /**
   * Start the worker
   */
  start(): void {
    if (this.isRunning) {
      console.log('[AutomationWorker] Worker is already running');
      return;
    }

    this.isRunning = true;
    console.log('[AutomationWorker] Starting worker...');

    // Run immediately
    this.runTick();

    // Schedule periodic runs
    this.intervalId = setInterval(() => {
      this.runTick();
    }, this.config.pollInterval);

    console.log(`[AutomationWorker] Worker started with ${this.config.pollInterval}ms poll interval`);
  }

  /**
   * Stop the worker
   */
  stop(): void {
    if (!this.isRunning) {
      return;
    }

    this.isRunning = false;

    if (this.intervalId) {
      clearInterval(this.intervalId);
      this.intervalId = null;
    }

    console.log('[AutomationWorker] Worker stopped');
  }

  /**
   * Run a single tick of the worker
   */
  async runTick(): Promise<void> {
    if (this.config.verbose) {
      console.log('[AutomationWorker] Running tick...');
    }

    try {
      // Process delayed instances
      await this.processDelayedInstances();

      // Process pending instances
      await this.processPendingInstances();

      // Process scheduled triggers (birthdays, anniversaries, etc.)
      await this.processScheduledTriggers();

      if (this.config.verbose) {
        console.log('[AutomationWorker] Tick completed');
      }
    } catch (error) {
      console.error('[AutomationWorker] Error during tick:', error);
    }
  }

  /**
   * Process instances scheduled to resume
   */
  private async processDelayedInstances(): Promise<void> {
    try {
      const dueInstanceIds = await this.delayScheduler.getDueInstances();

      if (dueInstanceIds.length === 0) {
        return;
      }

      console.log(`[AutomationWorker] Processing ${dueInstanceIds.length} delayed instances`);

      for (const instanceId of dueInstanceIds) {
        try {
          // Resume execution
          const result = await this.executionEngine.processInstance(instanceId);

          if (this.config.verbose) {
            console.log(`[AutomationWorker] Instance ${instanceId}: ${result.status}`);
          }
        } catch (error) {
          console.error(`[AutomationWorker] Error processing delayed instance ${instanceId}:`, error);
        }
      }
    } catch (error) {
      console.error('[AutomationWorker] Error processing delayed instances:', error);
    }
  }

  /**
   * Process pending instances (new instances waiting to start)
   */
  private async processPendingInstances(): Promise<void> {
    const { AutomationInstance } = getModels();

    try {
      // Find pending instances (no scheduledFor = immediate, or scheduledFor in the past)
      const pendingInstances = await AutomationInstance.find({
        status: 'pending',
        $or: [
          { scheduledFor: { $lte: new Date() } },
          { scheduledFor: { $exists: false } },
          { scheduledFor: null },
        ],
      })
        .limit(this.config.batchSize)
        .lean();

      if (pendingInstances.length === 0) {
        return;
      }

      console.log(`[AutomationWorker] Processing ${pendingInstances.length} pending instances`);

      for (const instance of pendingInstances) {
        try {
          // Set status to running
          await AutomationInstance.findByIdAndUpdate(instance._id, {
            $set: { status: 'running' },
          });

          // Process instance
          const result = await this.executionEngine.processInstance(
            instance._id.toString()
          );

          if (this.config.verbose) {
            console.log(`[AutomationWorker] Instance ${instance._id}: ${result.status}`);
          }
        } catch (error) {
          console.error(`[AutomationWorker] Error processing pending instance ${instance._id}:`, error);

          // Mark as error
          await this.instanceManager.markAsError(
            instance._id.toString(),
            instance.currentNodeId,
            error instanceof Error ? error.message : 'Unknown error'
          );
        }
      }
    } catch (error) {
      console.error('[AutomationWorker] Error processing pending instances:', error);
    }
  }

  /**
   * Process scheduled triggers (birthdays, anniversaries, date-based)
   */
  private async processScheduledTriggers(): Promise<void> {
    const { AutomationWorkflow } = getModels();

    try {
      const now = new Date();

      // Find active workflows with date-based triggers
      const dateBasedWorkflows = await AutomationWorkflow.find({
        status: 'active',
        'trigger.type': { $in: ['birthday', 'anniversary', 'date_field', 'recurring_date'] },
        deletedAt: null,
      }).lean();

      for (const workflow of dateBasedWorkflows) {
        try {
          await this.processDateBasedTrigger(workflow, now);
        } catch (error) {
          console.error(`[AutomationWorker] Error processing date trigger for workflow ${workflow._id}:`, error);
        }
      }
    } catch (error) {
      console.error('[AutomationWorker] Error processing scheduled triggers:', error);
    }
  }

  /**
   * Process a date-based trigger
   */
  private async processDateBasedTrigger(workflow: any, now: Date): Promise<void> {
    const { AutomationInstance } = getModels();
    const triggerType = workflow.trigger.type;

    // For birthdays and anniversaries, find contacts whose date matches today
    // This is a simplified implementation - production would need proper date field handling

    if (this.config.verbose) {
      console.log(`[AutomationWorker] Processing ${triggerType} trigger for workflow ${workflow._id}`);
    }

    // The actual implementation would:
    // 1. Query contacts where birthday/anniversary matches today
    // 2. For each matching contact, check if they already have an instance
    // 3. Create instances for contacts that don't

    // Placeholder: This would integrate with the Contact model
    // const contacts = await findMatchingContacts(triggerType, now);
    // for (const contact of contacts) {
    //   await this.triggerService.processTrigger({
    //     type: triggerType,
    //     companyId: workflow.companyId,
    //     contact: extractContactData(contact),
    //     data: {},
    //     timestamp: now,
    //   });
    // }
  }

  /**
   * Get worker status
   */
  getStatus(): {
    isRunning: boolean;
    config: WorkerConfig;
    scheduledCount: number;
  } {
    return {
      isRunning: this.isRunning,
      config: this.config,
      scheduledCount: 0, // Would be populated by delayScheduler.getScheduledCount()
    };
  }
}

// ============================================
// SINGLETON EXPORT
// ============================================

let workerInstance: AutomationWorker | null = null;

/**
 * Get the singleton worker instance
 */
export function getAutomationWorker(config?: Partial<WorkerConfig>): AutomationWorker {
  if (!workerInstance) {
    workerInstance = new AutomationWorker(config);
  }
  return workerInstance;
}

/**
 * Start the automation worker (convenience function)
 */
export function startAutomationWorker(config?: Partial<WorkerConfig>): void {
  const worker = getAutomationWorker(config);
  worker.start();
}

/**
 * Stop the automation worker (convenience function)
 */
export function stopAutomationWorker(): void {
  if (workerInstance) {
    workerInstance.stop();
  }
}