import type { CharacterDNA, ConversationProfile, ConversationPersona } from '../../types/character.js';
import { createModuleLogger } from '../../utils/logger.js';
import { createOpenRouterClient } from '../openrouter/client.js';
import { dataStore } from '../data-store/index.js';
import { buildPersonaGenerationPrompt } from './prompt-builder.js';

const log = createModuleLogger('persona-generator');

const MAX_ATTEMPTS = 3;

const REQUIRED_SECTIONS = [
  'character_profile',
  'language_rules',
  'conversation_scenarios',
  'contextual_expressions',
  'message_length_patterns',
  'do_not_use',
  'training_notes',
] as const;

export class PersonaGenerator {
  /**
   * CharacterDNA ve opsiyonel ConversationProfile'dan persona üretir.
   * Retry logic dahil. Başarısız olursa null döner.
   */
  async generate(
    dna: CharacterDNA,
    profile?: ConversationProfile,
  ): Promise<ConversationPersona | null> {
    const messages = buildPersonaGenerationPrompt(dna, profile);
    const client = createOpenRouterClient();

    for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
      try {
        log.info({ characterId: dna.id, attempt }, 'Generating persona');

        const response = await client.chatCompletion(messages, {
          max_tokens: 4096,
          temperature: 0.8,
        });

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

        const jsonStr = extractJSON(content);
        const parsed = JSON.parse(jsonStr);
        const persona = this.validatePersonaJSON(parsed);

        if (persona) {
          log.info({ characterId: dna.id }, 'Persona generated successfully');
          return persona;
        }

        log.warn({ characterId: dna.id, attempt }, 'Persona validation failed, retrying');
      } catch (err: unknown) {
        const message = err instanceof Error ? err.message : String(err);
        log.warn({ characterId: dna.id, attempt, error: message }, 'Persona generation attempt failed');
      }
    }

    log.error({ characterId: dna.id }, 'All persona generation attempts failed');
    return null;
  }

  /**
   * Mevcut karakter için personayı yeniden üretir.
   * Karakter bulunamazsa hata fırlatır.
   */
  async regenerate(characterId: string): Promise<ConversationPersona | null> {
    const dna = await dataStore.getCharacter(characterId);
    if (!dna) {
      throw new Error(`Character not found: ${characterId}`);
    }

    const profile = await dataStore.getConversationProfile(characterId);
    const persona = await this.generate(dna, profile ?? undefined);

    if (persona) {
      await dataStore.saveConversationPersona(characterId, persona);
    }

    return persona;
  }

  /**
   * AI yanıtını parse eder ve yapısal doğrulama yapar.
   * Geçersizse null döner.
   */
  validatePersonaJSON(raw: unknown): ConversationPersona | null {
    if (!raw || typeof raw !== 'object') return null;

    const obj = raw as Record<string, unknown>;

    // Check all required top-level sections exist
    for (const section of REQUIRED_SECTIONS) {
      if (!(section in obj) || obj[section] == null) {
        log.warn({ missingSection: section }, 'Persona validation: missing section');
        return null;
      }
    }

    // Validate language_rules has required entries
    const langRules = obj.language_rules as Record<string, unknown>;
    if (!langRules || typeof langRules !== 'object') return null;

    const generalRules = langRules.general_rules;
    if (!Array.isArray(generalRules) || generalRules.length < 1) {
      log.warn('Persona validation: general_rules empty');
      return null;
    }

    const abbreviations = langRules.abbreviations;
    if (!abbreviations || typeof abbreviations !== 'object' || Object.keys(abbreviations).length < 1) {
      log.warn('Persona validation: abbreviations empty');
      return null;
    }

    return raw as ConversationPersona;
  }
}

/**
 * JSON string'i response'dan çıkarır.
 * Markdown code block varsa temizler.
 */
function extractJSON(content: string): string {
  const trimmed = content.trim();
  const match = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/);
  if (match) return match[1].trim();
  return trimmed;
}

export const personaGenerator = new PersonaGenerator();
