import { createModuleLogger } from '../../utils/logger.js';
import { dataStore } from '../data-store/index.js';
import { createOpenRouterClient } from '../openrouter/client.js';
import type { ChatMessage } from '../openrouter/client.js';
import { queue } from '../queue-system/index.js';
import { conversationManager } from './history.js';
import { buildSystemPrompt } from './system-prompt.js';
import { salesRedirector } from './sales-redirect.js';
import { calculateDelay, DEFAULT_TIMER_CONFIG } from '../timer-system/index.js';
import { getContextAwarePrompt } from '../prompt-library/index.js';
import { generateImagePrompt } from '../image-engine/ai-prompt-generator.js';
import type { ConversationHistory } from '../../types/conversation.js';

const log = createModuleLogger('conversation-engine');

const DEFAULT_BUFFER_MS = 3000;
/** Minimum messages between photo sends per user */
const PHOTO_COOLDOWN_MESSAGES = 5;

interface BufferEntry {
  texts: string[];
  timer: ReturnType<typeof setTimeout>;
  userName?: string;
}

export class ConversationEngine {
  /** characterId:userId → buffered messages */
  private buffers = new Map<string, BufferEntry>();
  /** characterId:userId → custom buffer duration */
  private bufferDurations = new Map<string, number>();

  private static bufferKey(characterId: string, userId: number): string {
    return `${characterId}:${userId}`;
  }

  /**
   * Main entry point for incoming messages.
   * Buffers consecutive messages and merges them before generating a response.
   */
  async handleIncomingMessage(
    characterId: string,
    userId: number,
    text: string,
    userName?: string,
  ): Promise<void> {
    const key = ConversationEngine.bufferKey(characterId, userId);
    const bufferMs = this.bufferDurations.get(key) ?? DEFAULT_BUFFER_MS;

    const existing = this.buffers.get(key);
    if (existing) {
      // Append to existing buffer and reset timer
      existing.texts.push(text);
      if (userName) existing.userName = userName;
      clearTimeout(existing.timer);
      existing.timer = setTimeout(() => this.flushBuffer(characterId, userId), bufferMs);
      log.info({ characterId, userId, buffered: existing.texts.length }, 'Message buffered');
      return;
    }

    // Create new buffer entry
    const entry: BufferEntry = {
      texts: [text],
      userName,
      timer: setTimeout(() => this.flushBuffer(characterId, userId), bufferMs),
    };
    this.buffers.set(key, entry);
    log.info({ characterId, userId }, 'New message buffer started');
  }

  /**
   * Sets the buffer time for message merging for a specific character-user pair.
   */
  setMessageBuffer(characterId: string, userId: number, bufferMs: number): void {
    const key = ConversationEngine.bufferKey(characterId, userId);
    this.bufferDurations.set(key, Math.max(0, bufferMs));
  }

  /**
   * Generates an AI response using OpenRouter.
   * Builds system prompt from character DNA + profile + history,
   * then sends to OpenRouter for completion.
   */
  async generateResponse(characterId: string, userId: number): Promise<string> {
    const [dna, profile, history, persona, entities] = await Promise.all([
      dataStore.getCharacter(characterId),
      dataStore.getConversationProfile(characterId),
      conversationManager.getHistory(characterId, userId),
      dataStore.getConversationPersona(characterId),
      dataStore.getTelegramEntities(characterId),
    ]);

    if (!dna) throw new Error(`Character not found: ${characterId}`);
    if (!profile) throw new Error(`Conversation profile not found: ${characterId}`);

    // Build channel links from telegram entities
    let channelLinks: { normal?: string; vip?: string; vipPrice?: number } | undefined;
    if (entities && entities.length > 0) {
      const normalChannel = entities.find(e => e.type === 'channel' && !e.isVip && e.inviteLink);
      const vipChannel = entities.find(e => e.type === 'channel' && e.isVip && e.inviteLink);
      if (normalChannel || vipChannel) {
        channelLinks = {};
        if (normalChannel?.inviteLink) channelLinks.normal = normalChannel.inviteLink;
        if (vipChannel?.inviteLink) {
          channelLinks.vip = vipChannel.inviteLink;
          if (vipChannel.starPrice) channelLinks.vipPrice = vipChannel.starPrice;
        }
      }
    }

    const systemPrompt = buildSystemPrompt(dna, profile, history ?? undefined, persona ?? undefined, channelLinks);

    // Build message array from recent history
    const recentMessages = await conversationManager.getRecentMessages(characterId, userId, 72);
    const messages: ChatMessage[] = [
      { role: 'system', content: systemPrompt },
    ];

    for (const msg of recentMessages) {
      messages.push({ role: msg.role, content: msg.content });
    }

    // Check if we should add a sales pitch hint
    if (history && salesRedirector.shouldPitch(history, profile)) {
      const pitchHint = salesRedirector.generatePitchHint(profile);
      messages.push({
        role: 'system',
        content: `[Satış yönlendirme ipucu: ${pitchHint}]`,
      });
    }

    // Check photo cooldown — inject warning if recently sent
    if (history && this.isPhotoCooldownActive(history)) {
      messages.push({
        role: 'system',
        content: `[UYARI: Son ${PHOTO_COOLDOWN_MESSAGES} mesaj içinde zaten fotoğraf gönderildi. [FOTO] etiketi KULLANMA. Kullanıcı isterse "biraz sonra atarım", "bekle bi" gibi doğal bahaneler kullan.]`,
      });
    }

    const client = createOpenRouterClient();
    const response = await client.chatCompletion(messages, {
      temperature: 0.85,
      max_tokens: 120,
    });

    let content = response.choices?.[0]?.message?.content ?? '';

    // Strip HTML tags that AI sometimes injects (e.g. <div>, </div>, <br>, <p>)
    content = content.replace(/<\/?[^>]+(>|$)/g, '').trim();

    // Strip markdown formatting (bold, italic, headers)
    content = content.replace(/\*\*(.+?)\*\*/g, '$1');
    content = content.replace(/\*(.+?)\*/g, '$1');
    content = content.replace(/__(.+?)__/g, '$1');
    content = content.replace(/_(.+?)_/g, '$1');
    content = content.replace(/^#+\s*/gm, '');

    // Strip [FOTO] if cooldown is active (safety net)
    if (history && this.isPhotoCooldownActive(history)) {
      content = content.replace(/\[FOTO\]/gi, '').trim();
    }

    log.info({ characterId, userId, responseLength: content.length }, 'Response generated');
    return content;
  }

  /**
   * Checks if a photo was sent within the last N messages.
   */
  private isPhotoCooldownActive(history: ConversationHistory): boolean {
    const recent = history.messages.slice(-PHOTO_COOLDOWN_MESSAGES);
    return recent.some(m => m.role === 'assistant' && m.metadata?.photoSent);
  }

  /**
   * Parses [FOTO] tag from response text.
   * Returns { text, hasPhoto } where text has the tag removed.
   */
  private parsePhotoTag(text: string): { text: string; hasPhoto: boolean } {
    const hasPhoto = /\[FOTO\]/i.test(text);
    const cleanText = text.replace(/\[FOTO\]/gi, '').trim();
    return { text: cleanText, hasPhoto };
  }

  /**
   * Flushes the message buffer: merges texts, saves to history,
   * generates a response with delay, parses [FOTO] tags, and queues for sending.
   */
  private async flushBuffer(characterId: string, userId: number): Promise<void> {
    const key = ConversationEngine.bufferKey(characterId, userId);
    const entry = this.buffers.get(key);
    if (!entry) return;

    this.buffers.delete(key);
    const mergedText = entry.texts.join('\n');

    try {
      // Update user info if userName is available
      if (entry.userName) {
        await conversationManager.updateUserInfo(characterId, userId, { name: entry.userName });
      }

      // Save user message to history
      await conversationManager.addMessage(characterId, userId, 'user', mergedText);

      // Generate AI response
      const responseText = await this.generateResponse(characterId, userId);

      // Parse [FOTO] tag from response
      const { text: cleanText, hasPhoto } = this.parsePhotoTag(responseText);

      // Save assistant response to history (with photo metadata)
      const history = await conversationManager.addMessage(
        characterId,
        userId,
        'assistant',
        cleanText,
        hasPhoto ? { photoSent: true } : undefined,
      );

      // Record pitch if sales redirector suggested one
      const profile = await dataStore.getConversationProfile(characterId);
      if (profile && history) {
        const preHistory = await conversationManager.getHistory(characterId, userId);
        if (preHistory && salesRedirector.shouldPitch(preHistory, profile)) {
          const updated = salesRedirector.recordPitch(preHistory);
          await dataStore.saveConversationHistory(updated);
        }
      }

      // Calculate realistic delay before sending
      const hour = new Date().getHours();
      const delaySec = calculateDelay(DEFAULT_TIMER_CONFIG, cleanText.length, hour);
      const delayMs = Math.round(delaySec * 1000);

      log.info({ characterId, userId, delaySec: delaySec.toFixed(1), hasPhoto }, 'Scheduling response with delay');

      // Schedule response after delay
      setTimeout(async () => {
        try {
          // Queue the text response for sending via Telegram
          if (cleanText) {
            await queue.add('chat_response', characterId, {
              userId,
              text: cleanText,
              characterId,
            });
          }

          // Queue photo generation if [FOTO] was present
          if (hasPhoto) {
            await this.queuePhotoForUser(characterId, userId);
          }

          log.info({ characterId, userId, mergedCount: entry.texts.length }, 'Buffer flushed and response queued');
        } catch (err: any) {
          log.error({ characterId, userId, error: err.message }, 'Failed to queue delayed response');
        }
      }, delayMs);
    } catch (err: any) {
      log.error({ characterId, userId, error: err.message }, 'Failed to process buffered messages');
    }
  }

  /**
   * Queues a photo generation task for a user conversation.
   * Uses AI prompt generator trained on 900+ prompt library to create
   * character-personalized, context-aware image prompts.
   */
  private async queuePhotoForUser(characterId: string, userId: number): Promise<void> {
    try {
      const dna = await dataStore.getCharacter(characterId);
      if (!dna) return;

      // Get recent user messages for context
      const recentMessages = await conversationManager.getRecentMessages(characterId, userId, 24);
      const userTexts = recentMessages
        .filter(m => m.role === 'user')
        .slice(-5)
        .map(m => m.content);

      try {
        // AI-powered prompt generation from 900+ prompt library
        const generated = await generateImagePrompt(dna, {
          context: userTexts,
        });

        await queue.add('image_generation', characterId, {
          prompt: generated.prompt,
          model: 'nano-banana-2',
          aspectRatio: generated.aspectRatio,
          targetUserId: userId,
        });
        log.info({ characterId, userId, category: generated.category, safetyModified: generated.safetyModified }, 'AI-generated photo prompt queued');
      } catch (aiErr: any) {
        log.warn({ characterId, userId, error: aiErr.message }, 'AI prompt generation failed, falling back to library');

        // Fallback: prompt kütüphanesinden seç
        const prompt = await getContextAwarePrompt(dna, userTexts);
        if (prompt) {
          await queue.add('image_generation', characterId, {
            prompt: prompt.flatPrompt,
            model: 'nano-banana-2',
            aspectRatio: prompt.aspectRatio,
            targetUserId: userId,
          });
          log.info({ characterId, userId, promptId: prompt.originalId }, 'Fallback library prompt queued');
        } else {
          // Son çare: basit selfie prompt
          const subject = `${dna.identity.name}, ${dna.identity.age} year old ${dna.identity.nationality} woman`;
          await queue.add('image_generation', characterId, {
            prompt: `${subject}, casual selfie, smiling at camera, natural lighting, instagram style photo`,
            model: 'nano-banana-2',
            aspectRatio: '9:16',
            targetUserId: userId,
          });
        }
      }
    } catch (err: any) {
      log.error({ characterId, userId, error: err.message }, 'Failed to queue photo for user');
    }
  }

  /** Expose buffer state for testing */
  _getBuffer(characterId: string, userId: number): BufferEntry | undefined {
    return this.buffers.get(ConversationEngine.bufferKey(characterId, userId));
  }

  /** Clear all buffers (useful for cleanup) */
  _clearBuffers(): void {
    for (const entry of this.buffers.values()) {
      clearTimeout(entry.timer);
    }
    this.buffers.clear();
  }
}

/** Singleton instance */
export const conversationEngine = new ConversationEngine();
