import { readFile, writeFile } from 'fs/promises';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { getConfig, updateConfig } from './config.mjs';
import { getDb } from './db.mjs';

const __dirname = dirname(fileURLToPath(import.meta.url));
const PERSONALITY_PATH = join(__dirname, 'personality.md');

const DEFAULT_TRAITS = {
  creativity: 0.5,
  verbosity: 0.5,
  formality: 0.3,
  humor: 0.4
};

const DEFAULT_PERSONA = {
  name: 'agent',
  greeting: 'Hello! How can I help you today?',
  avatarStyle: 'default',
  traits: { ...DEFAULT_TRAITS }
};

const TRAIT_DESCRIPTORS = {
  creativity: {
    low:  'Be precise and factual',
    high: 'Be creative and imaginative'
  },
  verbosity: {
    low:  'Be concise',
    high: 'Provide detailed explanations'
  },
  formality: {
    low:  'Be casual and friendly',
    high: 'Be professional and formal'
  },
  humor: {
    low:  'Be straightforward',
    high: 'Use humor and wit'
  }
};

/**
 * Read personality.md content from disk.
 * Returns empty string if file does not exist.
 */
async function readPersonalityFile() {
  try {
    return await readFile(PERSONALITY_PATH, 'utf-8');
  } catch {
    return '';
  }
}

/**
 * Load persona from personality.md and config.persona section.
 * Returns a persona object with personality text, name, greeting, avatarStyle, and traits.
 */
export async function loadPersona() {
  const personalityText = await readPersonalityFile();
  const config = getConfig();
  const stored = config.persona || {};

  return {
    name: stored.name || DEFAULT_PERSONA.name,
    greeting: stored.greeting || DEFAULT_PERSONA.greeting,
    avatarStyle: stored.avatarStyle || DEFAULT_PERSONA.avatarStyle,
    traits: {
      ...DEFAULT_TRAITS,
      ...(stored.traits || {})
    },
    personalityText
  };
}

/**
 * Merge updates into config.persona. If updates include `personalityText`,
 * also write that content to personality.md on disk.
 */
export async function updatePersona(updates) {
  const config = getConfig();
  const current = config.persona || {};

  // Separate personalityText (file-backed) from config-backed fields
  const { personalityText, ...configUpdates } = updates;

  // Deep-merge traits if provided
  const mergedTraits = {
    ...DEFAULT_TRAITS,
    ...(current.traits || {}),
    ...(configUpdates.traits || {})
  };

  // Clamp trait values to 0-1 range
  for (const key of Object.keys(mergedTraits)) {
    if (typeof mergedTraits[key] === 'number') {
      mergedTraits[key] = Math.max(0, Math.min(1, mergedTraits[key]));
    }
  }

  const merged = {
    ...current,
    ...configUpdates,
    traits: mergedTraits
  };

  // Remove personalityText from config (it lives in the file)
  delete merged.personalityText;

  updateConfig({ persona: merged });

  // Write personality.md if text was provided
  if (typeof personalityText === 'string') {
    await writeFile(PERSONALITY_PATH, personalityText, 'utf-8');
  }

  return { ...merged, personalityText: personalityText ?? (await readPersonalityFile()) };
}

/**
 * Build a trait modifier line from a trait name and its 0-1 value.
 */
function traitDirective(traitName, value) {
  const descriptor = TRAIT_DESCRIPTORS[traitName];
  if (!descriptor) return '';
  if (value <= 0.3) return `- ${descriptor.low}`;
  if (value >= 0.7) return `- ${descriptor.high}`;
  return ''; // Mid-range: no explicit directive
}

/**
 * Return a formatted string suitable for injection into LLM system prompts.
 * Combines personality.md content with trait-based modifiers.
 */
export async function getPersonaForPrompt() {
  const persona = await loadPersona();
  const parts = [];

  // Include personality.md content
  if (persona.personalityText.trim()) {
    parts.push(persona.personalityText.trim());
  }

  // Build trait directives
  const directives = Object.entries(persona.traits)
    .map(([name, value]) => traitDirective(name, value))
    .filter(Boolean);

  if (directives.length > 0) {
    parts.push('## Communication Style\n' + directives.join('\n'));
  }

  return parts.join('\n\n');
}

// Cache for adaptive context (regenerate at most once per hour)
let _adaptiveCache = null;
let _adaptiveCacheTime = 0;
const ADAPTIVE_CACHE_TTL = 60 * 60 * 1000; // 1 hour

/**
 * Analyze user feedback events from the last 30 days and return a short
 * context string describing the user's demonstrated preferences.
 * Result is cached for 1 hour to avoid repeated DB scans.
 */
export function getAdaptiveContext() {
  const now = Date.now();
  if (_adaptiveCache !== null && now - _adaptiveCacheTime < ADAPTIVE_CACHE_TTL) {
    return _adaptiveCache;
  }

  try {
    const db = getDb();
    const since = new Date(now - 30 * 24 * 60 * 60 * 1000).toISOString();

    // Fetch feedback events from last 30 days
    const feedbackRows = db.prepare(
      `SELECT content, metadata FROM events WHERE type = 'feedback' AND created_at > ? ORDER BY id DESC LIMIT 200`
    ).all(since);

    if (feedbackRows.length === 0) {
      _adaptiveCache = '';
      _adaptiveCacheTime = now;
      return '';
    }

    let positiveCount = 0;
    let negativeCount = 0;
    const positiveMessageIds = [];

    for (const row of feedbackRows) {
      const sentiment = row.content;
      if (sentiment === 'positive') {
        positiveCount++;
        try {
          const meta = typeof row.metadata === 'string' ? JSON.parse(row.metadata) : row.metadata;
          if (meta && meta.messageId) positiveMessageIds.push(meta.messageId);
        } catch {}
      } else if (sentiment === 'negative') {
        negativeCount++;
      }
    }

    const total = positiveCount + negativeCount;
    if (total < 3) {
      // Not enough feedback to draw conclusions
      _adaptiveCache = '';
      _adaptiveCacheTime = now;
      return '';
    }

    // Look up the agent messages that received positive feedback
    let conciseCount = 0;
    let detailedCount = 0;
    let codeCount = 0;
    let noCodeCount = 0;
    let casualCount = 0;
    let formalCount = 0;

    if (positiveMessageIds.length > 0) {
      const placeholders = positiveMessageIds.map(() => '?').join(',');
      const messages = db.prepare(
        `SELECT content FROM events WHERE id IN (${placeholders}) AND type = 'chat'`
      ).all(...positiveMessageIds);

      for (const msg of messages) {
        const text = msg.content || '';
        const wordCount = text.split(/\s+/).length;
        if (wordCount < 80) conciseCount++; else detailedCount++;
        if (/```|`[^`]+`/.test(text)) codeCount++; else noCodeCount++;
        // Casual markers: contractions, "hey", "sure!", "yeah"
        if (/\b(don't|can't|I'll|I've|it's|you're|hey|yeah|sure!)\b/i.test(text)) casualCount++;
        else formalCount++;
      }
    }

    // Build preference string from heuristics
    const parts = [];

    const verbPref = detailedCount > conciseCount * 1.5 ? 'detailed' :
                     conciseCount > detailedCount * 1.5 ? 'concise' : null;
    if (verbPref) parts.push(verbPref + ' responses');

    if (codeCount > noCodeCount) parts.push('with code examples');
    else if (noCodeCount > codeCount * 2) parts.push('without heavy code blocks');

    const tonePref = casualCount > formalCount * 1.5 ? 'casual' :
                     formalCount > casualCount * 1.5 ? 'formal' : null;
    if (tonePref) parts.push(tonePref + ' tone');

    if (parts.length === 0) {
      _adaptiveCache = '';
      _adaptiveCacheTime = now;
      return '';
    }

    const context = `## Learned Preferences\nBased on past feedback, this user tends to prefer ${parts.join(', ')}.`;
    _adaptiveCache = context;
    _adaptiveCacheTime = now;
    return context;
  } catch {
    return '';
  }
}

/**
 * Return the persona configuration object for UI/API consumers.
 */
export function getPersonaConfig() {
  const config = getConfig();
  const stored = config.persona || {};

  return {
    name: stored.name || DEFAULT_PERSONA.name,
    greeting: stored.greeting || DEFAULT_PERSONA.greeting,
    avatarStyle: stored.avatarStyle || DEFAULT_PERSONA.avatarStyle,
    traits: {
      creativity: DEFAULT_TRAITS.creativity,
      verbosity: DEFAULT_TRAITS.verbosity,
      formality: DEFAULT_TRAITS.formality,
      humor: DEFAULT_TRAITS.humor,
      ...(stored.traits || {})
    }
  };
}
