import type { CharacterDNA, ConversationProfile, MasterJSONPrompt } from '../../types/character.js';
import { createModuleLogger } from '../../utils/logger.js';
import { dataStore } from '../data-store/index.js';
import { validateDNA, fillDefaults } from './dna-schema.js';
import { buildMasterPrompt } from './prompt-builder.js';
import { generateReferenceImages } from './reference-pipeline.js';
import { cloneCharacter } from './clone.js';
import { personaGenerator } from '../persona-generator/index.js';

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

export class CharacterEngine {
  /**
   * Full creation pipeline:
   * 1. Fill defaults & validate DNA
   * 2. Save character with 'creating' status
   * 3. Generate Master JSON Prompt
   * 4. Generate reference images (queued)
   * 5. Save conversation profile if provided
   * 6. Set status to 'active'
   */
  async createCharacter(
    partial: Partial<CharacterDNA>,
    profile?: Partial<ConversationProfile>,
  ): Promise<CharacterDNA> {
    // Step 1: Fill defaults and validate
    const dna = fillDefaults({ ...partial, status: 'creating' });
    const validation = validateDNA(dna);
    if (!validation.valid) {
      throw new Error(`Invalid character DNA: ${validation.errors.join('; ')}`);
    }

    log.info({ characterId: dna.id, name: dna.identity.name }, 'Creating character');

    // Step 2: Save character with 'creating' status
    await dataStore.saveCharacter(dna);

    try {
      // Step 3: Generate and save Master JSON Prompt
      const masterPrompt = buildMasterPrompt(dna);
      await dataStore.saveMasterPrompt(masterPrompt);

      // Step 4: Queue reference image generation
      await generateReferenceImages(dna.id, dna);

      // Step 5: Save conversation profile if provided
      let fullProfile: ConversationProfile | undefined;
      if (profile) {
        fullProfile = buildConversationProfile(dna.id, profile);
        await dataStore.saveConversationProfile(fullProfile);
      }

      // Step 5.5: Fire-and-forget persona generation
      personaGenerator.generate(dna, fullProfile)
        .then(async (persona) => {
          if (persona) {
            await dataStore.saveConversationPersona(dna.id, persona);
            log.info({ characterId: dna.id }, 'Persona generated and saved');
          }
        })
        .catch((err: unknown) => {
          const message = err instanceof Error ? err.message : String(err);
          log.error({ characterId: dna.id, error: message }, 'Persona generation failed (non-blocking)');
        });

      // Step 6: Set status to active
      dna.status = 'active';
      dna.updatedAt = new Date().toISOString();
      await dataStore.saveCharacter(dna);

      log.info({ characterId: dna.id }, 'Character created successfully');
      return dna;
    } catch (err: any) {
      log.error({ characterId: dna.id, err: err.message }, 'Character creation failed');
      throw err;
    }
  }

  /**
   * Update DNA fields for an existing character.
   */
  async updateCharacter(id: string, updates: Partial<CharacterDNA>): Promise<CharacterDNA> {
    const existing = await dataStore.getCharacter(id);
    if (!existing) {
      throw new Error(`Character not found: ${id}`);
    }

    const updated = mergeCharacterDNA(existing, updates);
    updated.updatedAt = new Date().toISOString();

    const validation = validateDNA(updated);
    if (!validation.valid) {
      throw new Error(`Invalid character DNA after update: ${validation.errors.join('; ')}`);
    }

    await dataStore.saveCharacter(updated);
    log.info({ characterId: id }, 'Character updated');
    return updated;
  }

  /**
   * Delete a character and all associated data.
   */
  async deleteCharacter(id: string): Promise<boolean> {
    const deleted = await dataStore.deleteCharacter(id);
    if (deleted) {
      log.info({ characterId: id }, 'Character deleted');
    }
    return deleted;
  }

  /**
   * Get character details by ID.
   */
  async getCharacter(id: string): Promise<CharacterDNA | null> {
    return dataStore.getCharacter(id);
  }

  /**
   * List all characters.
   */
  async listCharacters(): Promise<CharacterDNA[]> {
    return dataStore.listCharacters();
  }

  /**
   * Set character status to 'active'.
   */
  async activateCharacter(id: string): Promise<CharacterDNA> {
    return this.setStatus(id, 'active');
  }

  /**
   * Set character status to 'inactive'.
   */
  async deactivateCharacter(id: string): Promise<CharacterDNA> {
    return this.setStatus(id, 'inactive');
  }

  /**
   * Clone an existing character. Delegates to clone module.
   */
  async cloneCharacter(sourceId: string, overrides?: Partial<CharacterDNA>): Promise<CharacterDNA> {
    return cloneCharacter(sourceId, overrides);
  }

  private async setStatus(id: string, status: CharacterDNA['status']): Promise<CharacterDNA> {
    const existing = await dataStore.getCharacter(id);
    if (!existing) {
      throw new Error(`Character not found: ${id}`);
    }

    existing.status = status;
    existing.updatedAt = new Date().toISOString();
    await dataStore.saveCharacter(existing);
    log.info({ characterId: id, status }, 'Character status changed');
    return existing;
  }
}

/** Shallow-merge top-level DNA sections, preserving nested structure. */
function mergeCharacterDNA(base: CharacterDNA, updates: Partial<CharacterDNA>): CharacterDNA {
  const merged = { ...base };

  // Merge each section if provided
  if (updates.identity) merged.identity = { ...base.identity, ...updates.identity };
  if (updates.physicalMeasurements) {
    merged.physicalMeasurements = {
      ...base.physicalMeasurements,
      ...updates.physicalMeasurements,
      measurements: {
        ...base.physicalMeasurements.measurements,
        ...updates.physicalMeasurements.measurements,
      },
    };
  }
  if (updates.skinMap) merged.skinMap = { ...base.skinMap, ...updates.skinMap };
  if (updates.faceAnatomy) merged.faceAnatomy = { ...base.faceAnatomy, ...updates.faceAnatomy };
  if (updates.hairDNA) merged.hairDNA = { ...base.hairDNA, ...updates.hairDNA };
  if (updates.bodyBiometrics) merged.bodyBiometrics = { ...base.bodyBiometrics, ...updates.bodyBiometrics };
  if (updates.profileView) merged.profileView = { ...base.profileView, ...updates.profileView };
  if (updates.expressionMotion) merged.expressionMotion = { ...base.expressionMotion, ...updates.expressionMotion };
  if (updates.makeupStyle) {
    merged.makeupStyle = {
      ...base.makeupStyle,
      ...updates.makeupStyle,
      colors: {
        ...base.makeupStyle.colors,
        ...updates.makeupStyle.colors,
      },
    };
  }
  if (updates.fashionStyle) merged.fashionStyle = { ...base.fashionStyle, ...updates.fashionStyle };
  if (updates.technicalSettings) merged.technicalSettings = { ...base.technicalSettings, ...updates.technicalSettings };
  if (updates.status) merged.status = updates.status;

  return merged;
}

/** Build a full ConversationProfile with defaults. */
function buildConversationProfile(characterId: string, partial: Partial<ConversationProfile>): ConversationProfile {
  return {
    characterId,
    style: partial.style ?? 'neseli',
    customStyleDescription: partial.customStyleDescription,
    toneOfVoice: {
      formality: partial.toneOfVoice?.formality ?? 'casual',
      emoji_frequency: partial.toneOfVoice?.emoji_frequency ?? 'medium',
      message_length: partial.toneOfVoice?.message_length ?? 'medium',
      humor_level: partial.toneOfVoice?.humor_level ?? 'subtle',
      flirtation_level: partial.toneOfVoice?.flirtation_level ?? 'subtle',
    },
    languagePatterns: {
      common_phrases: partial.languagePatterns?.common_phrases ?? [],
      filler_words: partial.languagePatterns?.filler_words ?? [],
      reaction_templates: partial.languagePatterns?.reaction_templates ?? {},
    },
    salesBehavior: {
      min_messages_before_pitch: partial.salesBehavior?.min_messages_before_pitch ?? 10,
      cooldown_minutes: partial.salesBehavior?.cooldown_minutes ?? 60,
      pitch_style: partial.salesBehavior?.pitch_style ?? 'subtle',
    },
  };
}

export const characterEngine = new CharacterEngine();
