import { readFile, readdir, access } from 'fs/promises';
import { join, basename } from 'path';
import { z } from 'zod';
import type { AgentMode } from './prompts/system.js';

/**
 * Custom agent frontmatter schema
 */
const CustomAgentFrontmatterSchema = z.object({
  description: z.string().describe('Short description of what this agent does'),
  model: z.string().optional().describe('Model to use (e.g., claude-sonnet-4-20250514)'),
  tools: z.array(z.string()).optional().describe('List of tools to enable'),
  mode: z.enum(['code', 'plan', 'explore']).optional().describe('Agent mode'),
  max_iterations: z.number().optional().describe('Maximum tool iterations'),
  rules: z.array(z.string()).optional().describe('Rules to apply'),
  skills: z.array(z.string()).optional().describe('Skills to enable'),
});

export type CustomAgentFrontmatter = z.infer<typeof CustomAgentFrontmatterSchema>;

/**
 * Custom agent definition loaded from markdown
 */
export interface CustomAgent {
  name: string;
  description: string;
  model?: string;
  tools: string[];
  mode: AgentMode;
  maxIterations: number;
  systemPrompt: string;
  rules: string[];
  skills: string[];
  filePath: string;
}

/**
 * Parse YAML frontmatter from markdown content
 */
function parseFrontmatter(content: string): {
  frontmatter: Record<string, unknown>;
  body: string;
} {
  const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
  const match = content.match(frontmatterRegex);

  if (!match) {
    return { frontmatter: {}, body: content };
  }

  const yamlContent = match[1];
  const body = match[2];

  // Simple YAML parser for common patterns
  const frontmatter: Record<string, unknown> = {};
  const lines = yamlContent.split('\n');

  let currentKey = '';
  let inArray = false;
  let arrayItems: string[] = [];

  for (const line of lines) {
    const trimmed = line.trim();
    if (!trimmed || trimmed.startsWith('#')) continue;

    // Check for array item
    if (inArray && trimmed.startsWith('-')) {
      const value = trimmed.slice(1).trim();
      // Remove quotes if present
      arrayItems.push(value.replace(/^["']|["']$/g, ''));
      continue;
    }

    // Save previous array if we were in one
    if (inArray && currentKey) {
      frontmatter[currentKey] = arrayItems;
      inArray = false;
      arrayItems = [];
    }

    // Check for key-value pair
    const colonIndex = trimmed.indexOf(':');
    if (colonIndex > 0) {
      const key = trimmed.slice(0, colonIndex).trim();
      let value = trimmed.slice(colonIndex + 1).trim();

      // Check if value starts an array
      if (value === '' || value === '[]') {
        currentKey = key;
        inArray = true;
        arrayItems = [];
        continue;
      }

      // Inline array
      if (value.startsWith('[') && value.endsWith(']')) {
        const items = value
          .slice(1, -1)
          .split(',')
          .map((s) => s.trim().replace(/^["']|["']$/g, ''));
        frontmatter[key] = items;
        continue;
      }

      // Remove quotes from string values
      value = value.replace(/^["']|["']$/g, '');

      // Parse numbers
      if (/^\d+$/.test(value)) {
        frontmatter[key] = parseInt(value, 10);
      } else if (value === 'true') {
        frontmatter[key] = true;
      } else if (value === 'false') {
        frontmatter[key] = false;
      } else {
        frontmatter[key] = value;
      }
    }
  }

  // Save final array if we were in one
  if (inArray && currentKey) {
    frontmatter[currentKey] = arrayItems;
  }

  return { frontmatter, body };
}

/**
 * Load a custom agent from a markdown file
 */
export async function loadCustomAgent(filePath: string): Promise<CustomAgent> {
  const content = await readFile(filePath, 'utf-8');
  const { frontmatter, body } = parseFrontmatter(content);

  // Validate frontmatter
  const parsed = CustomAgentFrontmatterSchema.safeParse(frontmatter);
  if (!parsed.success) {
    throw new Error(`Invalid agent frontmatter in ${filePath}: ${parsed.error.message}`);
  }

  const fm = parsed.data;
  const name = basename(filePath, '.md');

  return {
    name,
    description: fm.description,
    model: fm.model,
    tools: fm.tools ?? [],
    mode: fm.mode ?? 'code',
    maxIterations: fm.max_iterations ?? 50,
    systemPrompt: body.trim(),
    rules: fm.rules ?? [],
    skills: fm.skills ?? [],
    filePath,
  };
}

/**
 * Load all custom agents from a directory
 */
export async function loadCustomAgents(agentsDir: string): Promise<CustomAgent[]> {
  const agents: CustomAgent[] = [];

  try {
    await access(agentsDir);
  } catch {
    // Directory doesn't exist
    return agents;
  }

  const files = await readdir(agentsDir);

  for (const file of files) {
    if (!file.endsWith('.md')) continue;

    try {
      const agent = await loadCustomAgent(join(agentsDir, file));
      agents.push(agent);
    } catch (err) {
      console.warn(`Failed to load agent ${file}:`, err);
    }
  }

  return agents;
}

/**
 * Custom agent manager
 *
 * Loads and manages custom agents from .emdash/agents/ directory.
 */
export class CustomAgentManager {
  private agents = new Map<string, CustomAgent>();
  private repoRoot: string;
  private agentsDir: string;

  constructor(repoRoot: string) {
    this.repoRoot = repoRoot;
    this.agentsDir = join(repoRoot, '.emdash', 'agents');
  }

  /**
   * Load all custom agents
   */
  async loadAll(): Promise<void> {
    const agents = await loadCustomAgents(this.agentsDir);
    this.agents.clear();

    for (const agent of agents) {
      this.agents.set(agent.name, agent);
    }
  }

  /**
   * Get an agent by name
   */
  get(name: string): CustomAgent | undefined {
    return this.agents.get(name);
  }

  /**
   * Get all loaded agents
   */
  getAll(): CustomAgent[] {
    return Array.from(this.agents.values());
  }

  /**
   * List agent names
   */
  list(): string[] {
    return Array.from(this.agents.keys());
  }

  /**
   * Check if an agent exists
   */
  has(name: string): boolean {
    return this.agents.has(name);
  }

  /**
   * Reload agents from disk
   */
  async reload(): Promise<void> {
    await this.loadAll();
  }
}
