// Context Manager - Maintains and provides game state

const fs = require('fs');
const path = require('path');

// Fix: Go up one level from server/agents to server, then to data
const DATA_DIR = path.join(__dirname, '..', '..', 'data');
const SESSIONS_DIR = path.join(DATA_DIR, 'sessions');

// Ensure sessions directory exists
if (!fs.existsSync(SESSIONS_DIR)) {
  fs.mkdirSync(SESSIONS_DIR, { recursive: true });
  console.log('[ContextManager] Created sessions directory:', SESSIONS_DIR);
}

class ContextManager {
  constructor() {
    this.contextCache = new Map();
  }

  // Load session from disk
  loadSession(sessionId) {
    const filePath = path.join(SESSIONS_DIR, `${sessionId}.json`);
    console.log(`[ContextManager] Loading session: ${filePath}`);
    
    if (fs.existsSync(filePath)) {
      const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
      console.log(`[ContextManager] Session loaded successfully`);
      return data;
    }
    
    console.error(`[ContextManager] Session file does not exist: ${filePath}`);
    console.error(`[ContextManager] SESSIONS_DIR: ${SESSIONS_DIR}`);
    console.error(`[ContextManager] sessionId: ${sessionId}`);
    
    // List what files ARE in the directory
    if (fs.existsSync(SESSIONS_DIR)) {
      const files = fs.readdirSync(SESSIONS_DIR);
      console.error(`[ContextManager] Files in SESSIONS_DIR:`, files);
    } else {
      console.error(`[ContextManager] SESSIONS_DIR does not exist!`);
    }
    
    return null;
  }

  // Save session to disk
  saveSession(sessionId, data) {
    const filePath = path.join(SESSIONS_DIR, `${sessionId}.json`);
    data.updatedAt = new Date().toISOString();
    fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
  }

  // Get full context for a session
  async getFullContext(sessionId) {
    const session = this.loadSession(sessionId);
    if (!session) {
      console.error(`[ContextManager] Session ${sessionId} not found!`);
      // Return default context instead of throwing
      return {
        session: { id: sessionId, title: 'Unknown', createdAt: new Date().toISOString() },
        character: null,
        characters: [],
        locations: [],
        inventory: [],
        quests: [],
        currentLocation: null,
        storyState: {},
        journal: {
          summary: '',
          keyEvents: [],
          characterArcs: {},
          locationHistory: []
        },
        recentMessages: [],
        goddessContext: null,
        systemPrompt: 'You are a skilled narrator for a text-based RPG.'
      };
    }

    const context = session.context || {
      characters: [],
      locations: [],
      inventory: [],
      quests: [],
      storyState: {}
    };

    const journal = session.journal || {
      summary: '',
      keyEvents: [],
      entries: [],
      characterArcs: {},
      locationHistory: []
    };

    // Get recent messages (last 10)
    const recentMessages = (session.messages || []).slice(-10);

    return {
      session: {
        id: session.id,
        title: session.title,
        createdAt: session.createdAt
      },
      character: session.character || null,
      characters: context.characters || [],
      locations: context.locations || [],
      inventory: context.inventory || [],
      quests: context.quests || [],
      currentLocation: context.currentLocation || null,
      storyState: context.storyState || {},
      journal: {
        summary: journal.summary || '',
        keyEvents: journal.keyEvents || [],
        characterArcs: journal.characterArcs || {},
        locationHistory: journal.locationHistory || []
      },
      recentMessages: recentMessages,
      goddessContext: session.goddessContext || null,
      systemPrompt: session.systemPrompt || ''
    };
  }

  // Normalize name for comparison (prevent duplicates)
  normalizeName(name) {
    return name.toLowerCase().replace(/\s+/g, '_').replace(/[^a-z0-9_]/g, '');
  }

  // Add or update character (prevents duplicates)
  addOrUpdateCharacter(sessionId, characterData) {
    const session = this.loadSession(sessionId);
    if (!session) throw new Error('Session not found');
    if (!session.context) session.context = { characters: [], locations: [], inventory: [], quests: [], storyState: {} };
    if (!session.context.characters) session.context.characters = [];

    const normalizedName = this.normalizeName(characterData.name);
    const existingIndex = session.context.characters.findIndex(
      c => this.normalizeName(c.name) === normalizedName
    );

    if (existingIndex >= 0) {
      // Merge with existing
      session.context.characters[existingIndex] = {
        ...session.context.characters[existingIndex],
        ...characterData,
        traits: [...new Set([...(session.context.characters[existingIndex].traits || []), ...(characterData.traits || [])])],
        relationships: { ...(session.context.characters[existingIndex].relationships || {}), ...(characterData.relationships || {}) }
      };
    } else {
      // Add new
      session.context.characters.push({
        name: characterData.name,
        description: characterData.description || '',
        traits: characterData.traits || [],
        status: characterData.status || 'Active',
        relationships: characterData.relationships || {}
      });
    }

    this.saveSession(sessionId, session);
    return session.context.characters;
  }

  // Add or update location
  addOrUpdateLocation(sessionId, locationData) {
    const session = this.loadSession(sessionId);
    if (!session) throw new Error('Session not found');
    if (!session.context) session.context = { characters: [], locations: [], inventory: [], quests: [], storyState: {} };
    if (!session.context.locations) session.context.locations = [];

    const normalizedName = this.normalizeName(locationData.name);
    const existingIndex = session.context.locations.findIndex(
      l => this.normalizeName(l.name) === normalizedName
    );

    const locationEntry = {
      name: locationData.name,
      description: locationData.description || '',
      atmosphere: locationData.atmosphere || 'Neutral',
      pointsOfInterest: locationData.pointsOfInterest || []
    };

    if (existingIndex >= 0) {
      session.context.locations[existingIndex] = { ...session.context.locations[existingIndex], ...locationEntry };
    } else {
      session.context.locations.push(locationEntry);
    }

    // Set as current location if not set
    if (!session.context.currentLocation) {
      session.context.currentLocation = locationData.name;
    }

    this.saveSession(sessionId, session);
    return session.context.locations;
  }

  // Add item to inventory (prevents duplicates)
  addItemToInventory(sessionId, itemData) {
    const session = this.loadSession(sessionId);
    if (!session) throw new Error('Session not found');
    if (!session.context) session.context = { characters: [], locations: [], inventory: [], quests: [], storyState: {} };
    if (!session.context.inventory) session.context.inventory = [];

    const normalizedName = this.normalizeName(itemData.name);
    const exists = session.context.inventory.some(
      i => this.normalizeName(i.name) === normalizedName
    );

    if (!exists) {
      session.context.inventory.push({
        name: itemData.name,
        description: itemData.description || '',
        type: itemData.type || 'misc',
        effects: itemData.effects || ''
      });
    }

    this.saveSession(sessionId, session);
    return session.context.inventory;
  }

  // Remove item from inventory
  removeItemFromInventory(sessionId, itemName) {
    const session = this.loadSession(sessionId);
    if (!session) throw new Error('Session not found');
    if (!session.context || !session.context.inventory) return [];

    const normalizedName = this.normalizeName(itemName);
    session.context.inventory = session.context.inventory.filter(
      i => this.normalizeName(i.name) !== normalizedName
    );

    this.saveSession(sessionId, session);
    return session.context.inventory;
  }

  // Add or update quest
  addOrUpdateQuest(sessionId, questData) {
    const session = this.loadSession(sessionId);
    if (!session) throw new Error('Session not found');
    if (!session.context) session.context = { characters: [], locations: [], inventory: [], quests: [], storyState: {} };
    if (!session.context.quests) session.context.quests = [];

    const existingQuest = session.context.quests.find(q => q.title === questData.title);
    if (existingQuest) {
      existingQuest.status = questData.status || existingQuest.status;
      existingQuest.description = questData.description || existingQuest.description;
      existingQuest.objectives = questData.objectives || existingQuest.objectives;
    } else {
      session.context.quests.push({
        title: questData.title,
        description: questData.description || '',
        status: questData.status || 'Active',
        objectives: questData.objectives || []
      });
    }

    this.saveSession(sessionId, session);
    return session.context.quests;
  }

  // Add journal entry
  addJournalEntry(sessionId, entryData) {
    const session = this.loadSession(sessionId);
    if (!session) throw new Error('Session not found');
    if (!session.journal) session.journal = { summary: '', keyEvents: [], entries: [], characterArcs: {}, locationHistory: [] };

    // Add to key events if significant
    if (entryData.description) {
      session.journal.keyEvents.push({
        id: `event_${Date.now()}`,
        timestamp: new Date().toISOString(),
        description: entryData.description
      });
      // Keep only last 20
      if (session.journal.keyEvents.length > 20) {
        session.journal.keyEvents = session.journal.keyEvents.slice(-20);
      }
    }

    // Add to entries
    if (entryData.content) {
      session.journal.entries.push({
        id: `entry_${Date.now()}`,
        timestamp: new Date().toISOString(),
        type: entryData.type || 'story_update',
        content: entryData.content
      });
      // Keep only last 50
      if (session.journal.entries.length > 50) {
        session.journal.entries = session.journal.entries.slice(-50);
      }
    }

    // Update summary
    if (entryData.summaryUpdate) {
      session.journal.summary = entryData.summaryUpdate;
    }

    // Update character arcs
    if (entryData.characterArcNotes) {
      Object.entries(entryData.characterArcNotes).forEach(([charName, notes]) => {
        session.journal.characterArcs[charName] = {
          ...(session.journal.characterArcs[charName] || {}),
          notes,
          lastUpdated: new Date().toISOString()
        };
      });
    }

    this.saveSession(sessionId, session);
    return session.journal;
  }

  // Update story state
  updateStoryState(sessionId, stateUpdates) {
    const session = this.loadSession(sessionId);
    if (!session) throw new Error('Session not found');
    if (!session.context) session.context = { characters: [], locations: [], inventory: [], quests: [], storyState: {} };
    if (!session.context.storyState) session.context.storyState = {};

    session.context.storyState = {
      ...session.context.storyState,
      ...stateUpdates
    };

    this.saveSession(sessionId, session);
    return session.context.storyState;
  }

  // Apply context updates from agent response
  async applyContextUpdates(sessionId, updates) {
    if (!updates) return;

    // Update characters
    if (updates.characters && updates.characters.length > 0) {
      for (const char of updates.characters) {
        this.addOrUpdateCharacter(sessionId, char);
      }
    }

    // Update locations
    if (updates.locations && updates.locations.length > 0) {
      for (const loc of updates.locations) {
        this.addOrUpdateLocation(sessionId, loc);
      }
    }

    // Update inventory
    if (updates.inventory) {
      for (const item of updates.inventory) {
        if (item.action === 'add') {
          this.addItemToInventory(sessionId, item);
        } else if (item.action === 'remove') {
          this.removeItemFromInventory(sessionId, item.name);
        }
      }
    }

    // Update quests
    if (updates.quests && updates.quests.length > 0) {
      for (const quest of updates.quests) {
        this.addOrUpdateQuest(sessionId, quest);
      }
    }

    // Update journal
    if (updates.journal) {
      this.addJournalEntry(sessionId, updates.journal);
    }

    // Update story state
    if (updates.storyState) {
      this.updateStoryState(sessionId, updates.storyState);
    }
  }
}

module.exports = ContextManager;
