/**
 * Session Manager - Handles Ignite session lifecycle with memory persistence
 */

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

class SessionManager {
  constructor(memoryBridge) {
    this.memoryBridge = memoryBridge;
    this.activeSessions = new Map();
    this.sessionMetadata = new Map();
  }

  /**
   * Start a new Ignite session with memory tracking
   */
  async startSession(context = {}) {
    const sessionId = this.generateSessionId(context);

    // Initialize memory session
    const memorySession = await this.memoryBridge.getSession({
      sessionId,
      project: context.project,
      phase: context.phase,
      agent: context.agent,
      command: context.command,
      userPrompt: context.userPrompt
    });

    // Store session metadata
    const sessionData = {
      id: sessionId,
      memorySessionId: memorySession?.id,
      project: context.project,
      phase: context.phase,
      agent: context.agent,
      command: context.command,
      startTime: new Date().toISOString(),
      observations: [],
      context: {}
    };

    this.activeSessions.set(sessionId, sessionData);
    this.sessionMetadata.set(sessionId, {
      promptCount: 0,
      tokenCount: 0,
      observationCount: 0
    });

    // Inject relevant memory context
    if (context.injectContext !== false) {
      const memoryContext = await this.memoryBridge.getContext({
        project: context.project,
        phase: context.phase
      });
      sessionData.context = memoryContext;
    }

    return sessionData;
  }

  /**
   * Generate unique session ID
   */
  generateSessionId(context) {
    const timestamp = Date.now();
    const project = context.project || 'default';
    const phase = context.phase || 'unknown';
    const agent = context.agent || 'general';

    return `${project}-${phase}-${agent}-${timestamp}`;
  }

  /**
   * Get active session
   */
  getSession(sessionId) {
    return this.activeSessions.get(sessionId);
  }

  /**
   * Update session with new data
   */
  updateSession(sessionId, updates) {
    const session = this.activeSessions.get(sessionId);
    if (!session) {
      throw new Error(`Session ${sessionId} not found`);
    }

    Object.assign(session, updates);
    return session;
  }

  /**
   * Add observation to session
   */
  async addObservation(sessionId, observation) {
    const session = this.activeSessions.get(sessionId);
    if (!session) {
      throw new Error(`Session ${sessionId} not found`);
    }

    // Store observation in memory
    const stored = await this.memoryBridge.storeObservation({
      sessionId,
      project: session.project,
      phase: session.phase,
      agent: session.agent,
      ...observation
    });

    // Track locally
    session.observations.push({
      ...observation,
      timestamp: new Date().toISOString(),
      memoryId: stored?.id
    });

    // Update metadata
    const metadata = this.sessionMetadata.get(sessionId);
    metadata.observationCount++;

    return stored;
  }

  /**
   * Capture agent execution as observation
   */
  async captureAgentExecution(sessionId, agentData) {
    const { agent, input, output, files } = agentData;

    // Extract structured observations from agent output
    const observations = this.extractObservations(output);

    // Store each observation
    for (const obs of observations) {
      await this.addObservation(sessionId, {
        type: obs.type || 'discovery',
        title: obs.title || `${agent} execution`,
        content: obs.content || output,
        agent,
        filesRead: files?.read || [],
        filesModified: files?.modified || [],
        concepts: obs.concepts || [],
        facts: obs.facts || []
      });
    }

    return observations;
  }

  /**
   * Extract structured observations from agent output
   */
  extractObservations(output) {
    const observations = [];

    // Look for marked observations in output
    const obsPattern = /<observation[^>]*>([\s\S]*?)<\/observation>/g;
    let match;

    while ((match = obsPattern.exec(output)) !== null) {
      try {
        // Parse observation attributes
        const content = match[1].trim();
        const typeMatch = match[0].match(/type="([^"]+)"/);
        const titleMatch = match[0].match(/title="([^"]+)"/);

        observations.push({
          type: typeMatch ? typeMatch[1] : 'discovery',
          title: titleMatch ? titleMatch[1] : null,
          content,
          facts: this.extractFacts(content),
          concepts: this.extractConcepts(content)
        });
      } catch (error) {
        console.error('Failed to parse observation:', error);
      }
    }

    // If no marked observations, create one from the whole output
    if (observations.length === 0 && output && output.length > 100) {
      observations.push({
        type: 'discovery',
        title: 'Agent execution output',
        content: output.substring(0, 1000),
        facts: [],
        concepts: []
      });
    }

    return observations;
  }

  /**
   * Extract facts from content
   */
  extractFacts(content) {
    const facts = [];

    // Look for bullet points or numbered lists
    const bulletPattern = /^[\s]*[-*•]\s+(.+)$/gm;
    const numberPattern = /^[\s]*\d+\.\s+(.+)$/gm;

    let match;
    while ((match = bulletPattern.exec(content)) !== null) {
      facts.push(match[1].trim());
    }
    while ((match = numberPattern.exec(content)) !== null) {
      facts.push(match[1].trim());
    }

    return facts.slice(0, 10); // Limit to 10 facts
  }

  /**
   * Extract concepts from content
   */
  extractConcepts(content) {
    const concepts = new Set();

    // Look for code terms (CamelCase, snake_case, etc)
    const codePattern = /\b[A-Z][a-z]+[A-Z]\w*\b|\b[a-z]+_[a-z]+\b/g;
    let match;
    while ((match = codePattern.exec(content)) !== null) {
      concepts.add(match[0]);
    }

    // Look for quoted terms
    const quotedPattern = /["'`]([^"'`]+)["'`]/g;
    while ((match = quotedPattern.exec(content)) !== null) {
      if (match[1].length < 50) {
        concepts.add(match[1]);
      }
    }

    return Array.from(concepts).slice(0, 15); // Limit to 15 concepts
  }

  /**
   * End session and generate summary
   */
  async endSession(sessionId, summary = null) {
    const session = this.activeSessions.get(sessionId);
    if (!session) {
      throw new Error(`Session ${sessionId} not found`);
    }

    // Generate summary if not provided
    if (!summary && session.observations.length > 0) {
      const summaryContent = {
        project: session.project,
        phase: session.phase,
        agent: session.agent,
        command: session.command,
        observationCount: session.observations.length,
        observations: session.observations.map(o => ({
          type: o.type,
          title: o.title,
          summary: o.content?.substring(0, 200)
        }))
      };

      summary = await this.memoryBridge.generateSummary(
        sessionId,
        JSON.stringify(summaryContent, null, 2)
      );
    }

    // Close memory session
    await this.memoryBridge.closeSession(sessionId);

    // Archive session data
    session.endTime = new Date().toISOString();
    session.summary = summary;
    session.metadata = this.sessionMetadata.get(sessionId);

    // Save to disk for debugging/audit
    await this.archiveSession(session);

    // Clean up
    this.activeSessions.delete(sessionId);
    this.sessionMetadata.delete(sessionId);

    return session;
  }

  /**
   * Archive session to disk
   */
  async archiveSession(session) {
    try {
      const archiveDir = path.join(
        process.env.HOME,
        '.ignite',
        'memory',
        'sessions',
        session.project || 'default'
      );

      await fs.mkdir(archiveDir, { recursive: true });

      const filename = `${session.id}.json`;
      const filepath = path.join(archiveDir, filename);

      await fs.writeFile(
        filepath,
        JSON.stringify(session, null, 2),
        'utf8'
      );
    } catch (error) {
      console.error('Failed to archive session:', error);
    }
  }

  /**
   * Get all active sessions
   */
  getActiveSessions() {
    return Array.from(this.activeSessions.values());
  }

  /**
   * Clean up stale sessions
   */
  async cleanupStaleSessions(maxAge = 3600000) { // 1 hour
    const now = Date.now();
    const stale = [];

    for (const [id, session] of this.activeSessions) {
      const startTime = new Date(session.startTime).getTime();
      if (now - startTime > maxAge) {
        stale.push(id);
      }
    }

    for (const id of stale) {
      console.log(`Cleaning up stale session: ${id}`);
      await this.endSession(id);
    }

    return stale.length;
  }
}

module.exports = SessionManager;