/**
 * Agent API
 *
 * High-level API for agents to explore and understand code.
 * Provides convenient methods for graph traversal and code analysis.
 */

import type { KuzuConnection } from '../graph/connection.js';
import type { EmbeddingService } from '../embeddings/service.js';
import { GraphQueries } from '../graph/queries.js';
import { SimilaritySearch } from './similarity.js';
import { ContextBuilder } from './context-builder.js';
import { FeatureContextBuilder } from './feature-context.js';
import type {
  PlanningContext,
  FeatureContext,
  SimilarEntity,
  ExplorationPlan,
  ExplorationStrategy,
  ExplorationStep,
} from './types.js';
import type { Logger } from '../utils/logger.js';

/**
 * Agent API Options
 */
export interface AgentAPIOptions {
  /** Maximum results for searches */
  maxResults?: number;
  /** Whether to expand graphs */
  expandGraphs?: boolean;
}

/**
 * Entity summary for quick overview
 */
export interface EntitySummary {
  qualifiedName: string;
  entityType: 'Function' | 'Class' | 'File';
  filePath: string;
  lineStart?: number;
  lineEnd?: number;
  docstring?: string;
  callers?: string[];
  callees?: string[];
  imports?: string[];
}

/**
 * Agent API
 *
 * Provides high-level methods for agents to:
 * - Find similar code
 * - Explore call graphs
 * - Understand dependencies
 * - Plan exploration strategies
 */
export class AgentAPI {
  private queries: GraphQueries;
  private similaritySearch: SimilaritySearch;
  private contextBuilder: ContextBuilder;
  private featureBuilder: FeatureContextBuilder;

  constructor(
    conn: KuzuConnection,
    embeddings?: EmbeddingService,
    logger?: Logger
  ) {
    this.queries = new GraphQueries(conn);
    this.similaritySearch = new SimilaritySearch(conn, embeddings, logger);
    this.contextBuilder = new ContextBuilder(conn, embeddings, logger);
    this.featureBuilder = new FeatureContextBuilder(conn, embeddings, logger);
  }

  /**
   * Find similar code to a natural language query
   */
  async findSimilar(query: string, limit = 10): Promise<SimilarEntity[]> {
    return this.similaritySearch.findSimilarCode(query, { limit });
  }

  /**
   * Get planning context for implementing a feature
   */
  async getPlanningContext(query: string): Promise<PlanningContext> {
    return this.contextBuilder.buildContext(query);
  }

  /**
   * Get feature context with expanded graphs
   */
  async getFeatureContext(query: string): Promise<FeatureContext> {
    return this.featureBuilder.buildFeatureContext(query);
  }

  /**
   * Get summary of an entity
   */
  async getEntitySummary(qualifiedName: string): Promise<EntitySummary | null> {
    // Try function first
    const fn = await this.queries.getFunction(qualifiedName);
    if (fn) {
      const callers = await this.queries.getCallers(qualifiedName);
      const callees = await this.queries.getCallGraph(qualifiedName, 1);
      return {
        qualifiedName: fn.qualifiedName,
        entityType: 'Function',
        filePath: fn.filePath,
        lineStart: fn.lineStart,
        lineEnd: fn.lineEnd,
        docstring: fn.docstring,
        callers: callers.map((c) => c.qualifiedName),
        callees: callees.map((c) => c.qualifiedName),
      };
    }

    // Try class
    const cls = await this.queries.getClass(qualifiedName);
    if (cls) {
      return {
        qualifiedName: cls.qualifiedName,
        entityType: 'Class',
        filePath: cls.filePath,
        lineStart: cls.lineStart,
        lineEnd: cls.lineEnd,
        docstring: cls.docstring,
      };
    }

    // Try file
    const file = await this.queries.getFile(qualifiedName);
    if (file) {
      const imports = await this.queries.getImports(qualifiedName);
      return {
        qualifiedName: file.path,
        entityType: 'File',
        filePath: file.path,
        imports: imports.map((m) => m.importPath),
      };
    }

    return null;
  }

  /**
   * Get callers of a function
   */
  async getCallers(qualifiedName: string, limit = 20): Promise<string[]> {
    const callers = await this.queries.getCallers(qualifiedName);
    return callers.slice(0, limit).map((c) => c.qualifiedName);
  }

  /**
   * Get callees of a function
   */
  async getCallees(qualifiedName: string, hops = 1): Promise<string[]> {
    const callees = await this.queries.getCallGraph(qualifiedName, hops);
    return callees.map((c) => c.qualifiedName);
  }

  /**
   * Get file dependencies
   */
  async getFileDependencies(filePath: string): Promise<{
    imports: string[];
    importedBy: string[];
  }> {
    const imports = await this.queries.getImports(filePath);
    const importedBy = await this.queries.getImporters(filePath);

    return {
      imports: imports.map((m) => m.importPath),
      importedBy: importedBy.map((f) => f.path),
    };
  }

  /**
   * Get class hierarchy
   */
  async getClassHierarchy(className: string): Promise<{
    parents: string[];
    children: string[];
  }> {
    const hierarchy = await this.queries.getClassHierarchy(className);

    return {
      parents: hierarchy.parents.map((p) => p.qualifiedName),
      children: hierarchy.children.map((c) => c.qualifiedName),
    };
  }

  /**
   * Find functions in a file
   */
  async getFunctionsInFile(filePath: string): Promise<EntitySummary[]> {
    const functions = await this.queries.getFunctionsInFile(filePath);
    return functions.map((fn) => ({
      qualifiedName: fn.qualifiedName,
      entityType: 'Function' as const,
      filePath: fn.filePath,
      lineStart: fn.lineStart,
      lineEnd: fn.lineEnd,
      docstring: fn.docstring,
    }));
  }

  /**
   * Get impact analysis for changing a file
   */
  async getImpactAnalysis(filePath: string): Promise<{
    directDependents: string[];
    transitiveDependents: string[];
    affectedFunctions: string[];
    riskLevel: 'low' | 'medium' | 'high';
  }> {
    return this.featureBuilder.getImpactAnalysis(filePath);
  }

  /**
   * Create an exploration plan for a strategy
   */
  createExplorationPlan(
    strategy: ExplorationStrategy,
    goal: string,
    context: PlanningContext
  ): ExplorationPlan {
    const steps: ExplorationStep[] = [];
    const filesToExamine: string[] = [];
    const queriesToRun: string[] = [];

    switch (strategy) {
      case 'understand_feature':
        steps.push(
          { step: 1, action: 'Find similar code', tool: 'findSimilar', expectedOutcome: 'List of related functions/classes' },
          { step: 2, action: 'Explore entry points', tool: 'getEntitySummary', expectedOutcome: 'Understanding of main interfaces' },
          { step: 3, action: 'Trace call graph', tool: 'getCallees', expectedOutcome: 'Flow of data through system' },
          { step: 4, action: 'Check dependencies', tool: 'getFileDependencies', expectedOutcome: 'External dependencies used' }
        );
        filesToExamine.push(...context.entryPoints, ...context.relatedFiles.slice(0, 5));
        queriesToRun.push(`Similar to: ${goal}`, 'Entry points', 'Call graph');
        break;

      case 'debug_issue':
        steps.push(
          { step: 1, action: 'Locate error source', tool: 'findSimilar', expectedOutcome: 'Files containing error-related code' },
          { step: 2, action: 'Find callers', tool: 'getCallers', expectedOutcome: 'Who calls the problematic code' },
          { step: 3, action: 'Check data flow', tool: 'getCallees', expectedOutcome: 'What the code calls' },
          { step: 4, action: 'Review test coverage', tool: 'findSimilar', expectedOutcome: 'Related test files' }
        );
        filesToExamine.push(...context.relatedFiles);
        queriesToRun.push(`Error: ${goal}`, 'Test files for related code');
        break;

      case 'assess_change_impact':
        steps.push(
          { step: 1, action: 'Find affected code', tool: 'findSimilar', expectedOutcome: 'Code that will be changed' },
          { step: 2, action: 'Get dependents', tool: 'getImpactAnalysis', expectedOutcome: 'Files that depend on changed code' },
          { step: 3, action: 'Check class hierarchy', tool: 'getClassHierarchy', expectedOutcome: 'Inheritance relationships' },
          { step: 4, action: 'Find test files', tool: 'findSimilar', expectedOutcome: 'Tests that need updating' }
        );
        filesToExamine.push(...context.relatedFiles);
        queriesToRun.push(`Impact of: ${goal}`, 'Test files');
        break;

      case 'onboard_codebase':
        steps.push(
          { step: 1, action: 'Find main entry points', tool: 'findSimilar', expectedOutcome: 'Main files and entry points' },
          { step: 2, action: 'Understand architecture', tool: 'getFileDependencies', expectedOutcome: 'Module structure' },
          { step: 3, action: 'Explore core classes', tool: 'getEntitySummary', expectedOutcome: 'Key class documentation' },
          { step: 4, action: 'Review API surface', tool: 'getFunctionsInFile', expectedOutcome: 'Public functions' }
        );
        filesToExamine.push(...context.entryPoints);
        queriesToRun.push('main entry point', 'core classes', 'public API');
        break;

      case 'find_similar_code':
        steps.push(
          { step: 1, action: 'Semantic search', tool: 'findSimilar', expectedOutcome: 'Semantically similar code' },
          { step: 2, action: 'Get context', tool: 'getEntitySummary', expectedOutcome: 'Details of similar entities' },
          { step: 3, action: 'Trace usage', tool: 'getCallers', expectedOutcome: 'How similar code is used' }
        );
        filesToExamine.push(...context.similarCode.slice(0, 5).map((c) => c.filePath));
        queriesToRun.push(goal);
        break;
    }

    return {
      strategy,
      goal,
      steps,
      filesToExamine,
      queriesToRun,
    };
  }

  /**
   * Get a quick overview of a file
   */
  async getFileOverview(filePath: string): Promise<{
    functions: number;
    classes: number;
    imports: number;
    dependents: number;
    topFunctions: EntitySummary[];
  }> {
    const functions = await this.queries.getFunctionsInFile(filePath);
    const classes = await this.queries.getClassesInFile(filePath);
    const imports = await this.queries.getImports(filePath);
    const dependents = await this.queries.getImporters(filePath);

    return {
      functions: functions.length,
      classes: classes.length,
      imports: imports.length,
      dependents: dependents.length,
      topFunctions: functions.slice(0, 5).map((fn) => ({
        qualifiedName: fn.qualifiedName,
        entityType: 'Function' as const,
        filePath: fn.filePath,
        lineStart: fn.lineStart,
        lineEnd: fn.lineEnd,
        docstring: fn.docstring,
      })),
    };
  }
}

/**
 * Create an agent API instance
 */
export function createAgentAPI(
  conn: KuzuConnection,
  embeddings?: EmbeddingService,
  logger?: Logger
): AgentAPI {
  return new AgentAPI(conn, embeddings, logger);
}
