'use strict';

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

/**
 * Parse YAML frontmatter from a markdown string.
 * Returns { frontmatter: {}, body: string }.
 */
function parseFrontmatter(content) {
  const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n([\s\S]*))?$/);
  if (!match) {
    return { frontmatter: {}, body: content };
  }

  const frontmatter = {};
  for (const line of match[1].split('\n')) {
    const colonIdx = line.indexOf(':');
    if (colonIdx === -1) continue;

    const key = line.slice(0, colonIdx).trim();
    let value = line.slice(colonIdx + 1).trim();

    // Handle JSON arrays (e.g. tools: ["Read", "Grep"])
    if (value.startsWith('[') && value.endsWith(']')) {
      try {
        value = JSON.parse(value);
      } catch {
        // keep as string
      }
    }

    // Strip surrounding quotes
    if (typeof value === 'string' && value.startsWith('"') && value.endsWith('"')) {
      value = value.slice(1, -1);
    }

    frontmatter[key] = value;
  }

  return { frontmatter, body: match[2] || '' };
}

/**
 * Extract the first meaningful paragraph from agent body as a summary.
 * Skips headings, list items, code blocks, and table rows.
 */
function extractSummary(body, maxSentences = 1) {
  const lines = body.split('\n');
  const paragraphs = [];
  let current = [];
  let inCodeBlock = false;

  for (const line of lines) {
    const trimmed = line.trim();

    // Track fenced code blocks
    if (trimmed.startsWith('```')) {
      inCodeBlock = !inCodeBlock;
      continue;
    }
    if (inCodeBlock) continue;

    if (trimmed === '') {
      if (current.length > 0) {
        paragraphs.push(current.join(' '));
        current = [];
      }
      continue;
    }

    // Skip headings, list items (bold, plain, asterisk), numbered lists, table rows
    if (
      trimmed.startsWith('#') ||
      trimmed.startsWith('- ') ||
      trimmed.startsWith('* ') ||
      /^\d+\.\s/.test(trimmed) ||
      trimmed.startsWith('|')
    ) {
      if (current.length > 0) {
        paragraphs.push(current.join(' '));
        current = [];
      }
      continue;
    }

    current.push(trimmed);
  }
  if (current.length > 0) {
    paragraphs.push(current.join(' '));
  }

  const firstParagraph = paragraphs.find(p => p.length > 0);
  if (!firstParagraph) return '';

  const sentences = firstParagraph.match(/[^.!?]+[.!?]+/g) || [firstParagraph];
  return sentences.slice(0, maxSentences).map(s => s.trim()).join(' ').trim();
}

/**
 * Load and parse a single agent file.
 */
function loadAgent(filePath) {
  const content = fs.readFileSync(filePath, 'utf8');
  const { frontmatter, body } = parseFrontmatter(content);
  const fileName = path.basename(filePath, '.md');

  return {
    fileName,
    name: frontmatter.name || fileName,
    description: frontmatter.description || '',
    tools: Array.isArray(frontmatter.tools) ? frontmatter.tools : [],
    model: frontmatter.model || 'sonnet',
    body,
    byteSize: Buffer.byteLength(content, 'utf8'),
  };
}

/**
 * Load all agents from a directory.
 */
function loadAgents(agentsDir) {
  if (!fs.existsSync(agentsDir)) return [];

  return fs.readdirSync(agentsDir)
    .filter(f => f.endsWith('.md'))
    .sort()
    .map(f => loadAgent(path.join(agentsDir, f)));
}

/**
 * Compress an agent to catalog entry (metadata only).
 */
function compressToCatalog(agent) {
  return {
    name: agent.name,
    description: agent.description,
    tools: agent.tools,
    model: agent.model,
  };
}

/**
 * Compress an agent to summary entry (metadata + first paragraph).
 */
function compressToSummary(agent) {
  return {
    ...compressToCatalog(agent),
    summary: extractSummary(agent.body),
  };
}

const allowedModes = ['catalog', 'summary', 'full'];

/**
 * Build a compressed catalog from a directory of agents.
 *
 * Modes:
 *  - 'catalog': name, description, tools, model only (~2-3k tokens for 27 agents)
 *  - 'summary': catalog + first paragraph summary (~4-5k tokens)
 *  - 'full':    no compression, full body included
 *
 * Returns { agents: [], stats: { totalAgents, originalBytes, compressedBytes, compressedTokenEstimate, mode } }
 */
function buildAgentCatalog(agentsDir, options = {}) {
  const mode = options.mode || 'catalog';

  if (!allowedModes.includes(mode)) {
    throw new Error(`Invalid mode "${mode}". Allowed modes: ${allowedModes.join(', ')}`);
  }

  const filter = options.filter || null;

  let agents = loadAgents(agentsDir);

  if (typeof filter === 'function') {
    agents = agents.filter(filter);
  }

  const originalBytes = agents.reduce((sum, a) => sum + a.byteSize, 0);

  let compressed;
  if (mode === 'catalog') {
    compressed = agents.map(compressToCatalog);
  } else if (mode === 'summary') {
    compressed = agents.map(compressToSummary);
  } else {
    compressed = agents.map(a => ({
      name: a.name,
      description: a.description,
      tools: a.tools,
      model: a.model,
      body: a.body,
    }));
  }

  const compressedJson = JSON.stringify(compressed);
  // Rough token estimate: ~4 chars per token for English text
  const compressedTokenEstimate = Math.ceil(compressedJson.length / 4);

  return {
    agents: compressed,
    stats: {
      totalAgents: agents.length,
      originalBytes,
      compressedBytes: Buffer.byteLength(compressedJson, 'utf8'),
      compressedTokenEstimate,
      mode,
    },
  };
}

/**
 * Lazy-load a single agent's full content by name.
 * Returns null if not found.
 */
function lazyLoadAgent(agentsDir, agentName) {
  // Validate agentName: only allow alphanumeric, hyphen, underscore
  if (!/^[\w-]+$/.test(agentName)) {
    return null;
  }

  const filePath = path.resolve(agentsDir, `${agentName}.md`);

  // Verify the resolved path is still within agentsDir
  const resolvedAgentsDir = path.resolve(agentsDir);
  if (!filePath.startsWith(resolvedAgentsDir + path.sep)) {
    return null;
  }

  if (!fs.existsSync(filePath)) return null;
  return loadAgent(filePath);
}

module.exports = {
  buildAgentCatalog,
  compressToCatalog,
  compressToSummary,
  extractSummary,
  lazyLoadAgent,
  loadAgent,
  loadAgents,
  parseFrontmatter,
};
