import cron from 'node-cron';
import { createModuleLogger } from '../../utils/index.js';
import type { CharacterEngine } from '../character-engine/index.js';
import type { ConversationEngine } from '../conversation-engine/index.js';
import type { ImageEngine } from '../image-engine/index.js';
import type { TelegramManager } from '../telegram-manager/index.js';
import type { PriorityQueue } from '../queue-system/index.js';
import type { TimerConfig } from '../timer-system/index.js';
import type { AnalyticsEngine } from '../analytics/index.js';
import type { SuccessfulPayment } from '../../types/telegram.js';

const log = createModuleLogger('automation');

export type AutomationStatus = 'active' | 'inactive' | 'error';

export class AutomationManager {
  private characterEngine: CharacterEngine;
  private conversationEngine: ConversationEngine;
  private imageEngine: ImageEngine;
  private telegramManager: TelegramManager;
  private queueSystem: PriorityQueue;
  private timerConfig: TimerConfig;
  private analyticsEngine: AnalyticsEngine;

  private contentCronTask: cron.ScheduledTask | null = null;
  private statusMap: Map<string, AutomationStatus> = new Map();
  private running = false;

  constructor(
    characterEngine: CharacterEngine,
    conversationEngine: ConversationEngine,
    imageEngine: ImageEngine,
    telegramManager: TelegramManager,
    queueSystem: PriorityQueue,
    timerConfig: TimerConfig,
    analyticsEngine: AnalyticsEngine,
  ) {
    this.characterEngine = characterEngine;
    this.conversationEngine = conversationEngine;
    this.imageEngine = imageEngine;
    this.telegramManager = telegramManager;
    this.queueSystem = queueSystem;
    this.timerConfig = timerConfig;
    this.analyticsEngine = analyticsEngine;
  }

  // --- Character lifecycle automation ---

  async onCharacterCreated(characterId: string): Promise<void> {
    log.info({ characterId }, 'Character created — starting post-creation pipeline');
    this.setAutomationStatus(characterId, 'active');

    try {
      // 1. Setup Telegram channels
      const botClient = this.telegramManager.getBotClient(characterId);
      if (botClient) {
        const character = await this.characterEngine.getCharacter(characterId);
        const name = character?.identity?.name ?? characterId;
        const channelMgr = this.telegramManager.getChannelManager();
        await channelMgr.createChannels(
          characterId,
          botClient as any,
          `${name} — Channel`,
          `${name} — VIP`,
        );
        log.info({ characterId }, 'Telegram channels created');
      } else {
        log.warn({ characterId }, 'No bot client available — skipping channel setup');
      }

      // 2. Reference images are already generated during character creation pipeline
      log.info({ characterId }, 'Reference images generated as part of character creation');

      // 3. Conversation engine is stateless per-request — nothing to start
      log.info({ characterId }, 'Conversation engine ready for character');

      log.info({ characterId }, 'Post-creation pipeline completed');
    } catch (err: any) {
      log.error({ characterId, error: err.message }, 'Post-creation pipeline failed');
      this.setAutomationStatus(characterId, 'error');
    }
  }

  async onCharacterActivated(characterId: string): Promise<void> {
    log.info({ characterId }, 'Character activated');
    this.setAutomationStatus(characterId, 'active');
  }

  async onCharacterDeactivated(characterId: string): Promise<void> {
    log.info({ characterId }, 'Character deactivated');
    this.setAutomationStatus(characterId, 'inactive');
  }

  // --- Message handling automation ---

  async handleIncomingMessage(characterId: string, userId: number, text: string): Promise<void> {
    const status = this.getAutomationStatus(characterId);
    if (status !== 'active') {
      log.warn({ characterId, status }, 'Ignoring message — automation not active');
      return;
    }

    try {
      // Send typing indicator immediately to show the bot is "thinking"
      const botClient = this.telegramManager.getBotClient(characterId);
      if (botClient) {
        try {
          await botClient.sendChatAction(userId, 'typing');
        } catch (err: any) {
          log.warn({ characterId, userId, error: err.message }, 'Failed to send typing indicator');
        }
      }

      // Pass to conversation engine (handles buffering, delay, AI response, queuing)
      await this.conversationEngine.handleIncomingMessage(characterId, userId, text);
    } catch (err: any) {
      log.error({ characterId, userId, error: err.message }, 'Failed to handle incoming message');
      this.setAutomationStatus(characterId, 'error');
    }
  }

  // --- Stars payment automation ---

  async handleStarsPayment(characterId: string, userId: number, payment: SuccessfulPayment): Promise<void> {
    const status = this.getAutomationStatus(characterId);
    if (status !== 'active') {
      log.warn({ characterId, status }, 'Ignoring payment — automation not active');
      return;
    }

    try {
      const botClient = this.telegramManager.getBotClient(characterId);
      if (!botClient) {
        throw new Error(`No bot client for character ${characterId}`);
      }

      const starsMgr = this.telegramManager.getStarsManager();
      await starsMgr.handleSuccessfulPayment(characterId, payment, userId, botClient);
      log.info({ characterId, userId, amount: payment.total_amount }, 'Stars payment processed');
    } catch (err: any) {
      log.error({ characterId, userId, error: err.message }, 'Failed to process Stars payment');
      this.setAutomationStatus(characterId, 'error');
    }
  }

  // --- Content publishing automation ---

  async runContentCycle(characterId: string): Promise<void> {
    const status = this.getAutomationStatus(characterId);
    if (status !== 'active') {
      log.info({ characterId, status }, 'Skipping content cycle — not active');
      return;
    }

    try {
      log.info({ characterId }, 'Running content cycle');

      // 1. Generate image using first available template
      const templates = await this.imageEngine['templateManager']?.listTemplates?.();
      if (!templates || templates.length === 0) {
        log.warn({ characterId }, 'No templates available for content cycle');
        return;
      }

      const template = templates[Math.floor(Math.random() * templates.length)];
      const taskId = await this.imageEngine.generateImage(characterId, template.id);
      log.info({ characterId, taskId, templateId: template.id }, 'Image generation queued for content cycle');

      // Quality control and publishing happen via webhook callbacks
      // when the image generation completes
    } catch (err: any) {
      log.error({ characterId, error: err.message }, 'Content cycle failed');
    }
  }

  // --- Scheduling ---

  startAutomation(cronExpression?: string): void {
    if (this.contentCronTask) {
      this.contentCronTask.stop();
    }

    const expression = cronExpression || '0 */4 * * *'; // default: every 4 hours
    this.contentCronTask = cron.schedule(expression, async () => {
      log.info('Content cycle cron triggered');
      try {
        const characters = await this.characterEngine.listCharacters();
        const activeChars = characters.filter(
          (c) => c.status === 'active' && this.getAutomationStatus(c.id) === 'active',
        );

        for (const char of activeChars) {
          await this.runContentCycle(char.id);
        }
      } catch (err: any) {
        log.error({ error: err.message }, 'Content cycle cron failed');
      }
    });

    this.running = true;
    log.info({ cronExpression: expression }, 'Automation started');
  }

  stopAutomation(): void {
    if (this.contentCronTask) {
      this.contentCronTask.stop();
      this.contentCronTask = null;
    }
    this.running = false;
    log.info('Automation stopped');
  }

  isRunning(): boolean {
    return this.running;
  }

  // --- Status ---

  getAutomationStatus(characterId: string): AutomationStatus {
    return this.statusMap.get(characterId) ?? 'inactive';
  }

  setAutomationStatus(characterId: string, status: AutomationStatus): void {
    this.statusMap.set(characterId, status);
    log.info({ characterId, status }, 'Automation status updated');
  }

  getAllStatuses(): Map<string, AutomationStatus> {
    return new Map(this.statusMap);
  }
}
