/**
 * Context Builder
 *
 * Builds comprehensive planning context for feature implementation.
 */

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 type {
  PlanningContext,
  DomainExpert,
  SimilarPR,
  ContextBuilderOptions,
} from './types.js';
import { createLogger, type Logger } from '../utils/logger.js';

/**
 * Default context builder options
 */
const DEFAULT_OPTIONS: Required<ContextBuilderOptions> = {
  maxSimilarCode: 10,
  maxSimilarPRs: 5,
  maxExperts: 5,
  expandCallGraph: true,
  maxHops: 2,
};

/**
 * Context Builder
 *
 * Aggregates multiple data sources to build planning context:
 * - Similar code entities
 * - Similar PRs
 * - Related files
 * - Domain experts
 * - Entry points
 */
export class ContextBuilder {
  private queries: GraphQueries;
  private similaritySearch: SimilaritySearch;
  private logger: Logger;

  constructor(
    conn: KuzuConnection,
    embeddings?: EmbeddingService,
    logger?: Logger
  ) {
    this.queries = new GraphQueries(conn);
    this.similaritySearch = new SimilaritySearch(conn, embeddings, logger);
    this.logger = logger ?? createLogger({ name: 'context-builder' });
  }

  /**
   * Build comprehensive planning context
   */
  async buildContext(
    query: string,
    options?: ContextBuilderOptions
  ): Promise<PlanningContext> {
    const opts = { ...DEFAULT_OPTIONS, ...options };
    this.logger.info({ query }, 'Building planning context');

    // Find similar code
    const similarCode = await this.similaritySearch.findSimilarCode(query, {
      limit: opts.maxSimilarCode,
    });

    // Collect related files from similar code
    const relatedFilesSet = new Set<string>();
    for (const entity of similarCode) {
      relatedFilesSet.add(entity.filePath);

      // If expanding call graph, get related files
      if (opts.expandCallGraph && entity.entityType === 'Function') {
        const callees = await this.queries.getCallGraph(
          entity.qualifiedName,
          opts.maxHops
        );
        for (const callee of callees) {
          // Get the file for each callee
          const fn = await this.queries.getFunction(callee.qualifiedName);
          if (fn) {
            relatedFilesSet.add(fn.filePath);
          }
        }
      }
    }

    // Find entry points (main files, index files)
    const entryPoints = await this.findEntryPoints(Array.from(relatedFilesSet));

    // Generate suggested tasks based on similar code
    const suggestedTasks = this.generateSuggestedTasks(query, similarCode);

    // Domain experts (placeholder - would need git history)
    const experts = await this.findDomainExperts(Array.from(relatedFilesSet), opts.maxExperts);

    // Similar PRs (placeholder - would need PR data)
    const similarPRs: SimilarPR[] = [];

    const context: PlanningContext = {
      query,
      similarCode,
      similarPRs,
      relatedFiles: Array.from(relatedFilesSet),
      experts,
      entryPoints,
      suggestedTasks,
    };

    this.logger.info(
      {
        similarCodeCount: similarCode.length,
        relatedFilesCount: relatedFilesSet.size,
        entryPointsCount: entryPoints.length,
      },
      'Built planning context'
    );

    return context;
  }

  /**
   * Find entry points from a set of files
   */
  private async findEntryPoints(files: string[]): Promise<string[]> {
    const entryPoints: string[] = [];

    // Entry point patterns (priority order)
    const entryPatterns = [
      /\/index\.[tj]sx?$/,
      /\/main\.[tj]sx?$/,
      /\/app\.[tj]sx?$/,
      /\/server\.[tj]sx?$/,
      /\/cli\.[tj]sx?$/,
    ];

    for (const pattern of entryPatterns) {
      for (const file of files) {
        if (pattern.test(file) && !entryPoints.includes(file)) {
          entryPoints.push(file);
        }
      }
    }

    // If no specific entry points, use the most imported files
    if (entryPoints.length === 0 && files.length > 0) {
      // Just return the first few files as entry points
      return files.slice(0, 3);
    }

    return entryPoints.slice(0, 5);
  }

  /**
   * Find domain experts for the given files
   */
  private async findDomainExperts(
    _files: string[],
    _maxExperts: number
  ): Promise<DomainExpert[]> {
    // Placeholder - would need git history integration
    // For now, return empty array
    // In a real implementation, this would:
    // 1. Get git log for each file
    // 2. Count commits per author
    // 3. Weight by recency and file importance
    return [];
  }

  /**
   * Generate suggested tasks based on query and similar code
   */
  private generateSuggestedTasks(
    query: string,
    similarCode: Array<{ qualifiedName: string; entityType: string; filePath: string }>
  ): string[] {
    const tasks: string[] = [];

    // Always suggest exploring similar code first
    const firstSimilar = similarCode[0];
    if (firstSimilar) {
      tasks.push(`Review similar code in ${firstSimilar.filePath}`);
    }

    // Suggest understanding the area
    const directories = new Set(
      similarCode.map((e) => {
        const parts = e.filePath.split('/');
        return parts.slice(0, -1).join('/');
      })
    );

    if (directories.size > 0) {
      const firstDir = Array.from(directories)[0] ?? '';
      tasks.push(`Understand the ${firstDir} module structure`);
    }

    // Generic tasks based on query keywords
    const queryLower = query.toLowerCase();

    if (queryLower.includes('add') || queryLower.includes('create') || queryLower.includes('new')) {
      tasks.push('Identify where the new feature should be placed');
      tasks.push('Design the API/interface for the new feature');
      tasks.push('Plan integration points with existing code');
    }

    if (queryLower.includes('fix') || queryLower.includes('bug') || queryLower.includes('issue')) {
      tasks.push('Reproduce the issue');
      tasks.push('Trace the code path');
      tasks.push('Identify the root cause');
    }

    if (queryLower.includes('refactor') || queryLower.includes('improve')) {
      tasks.push('Document current behavior');
      tasks.push('Identify dependencies');
      tasks.push('Plan incremental changes');
    }

    // Always suggest testing
    tasks.push('Identify test files and coverage');

    return tasks.slice(0, 8); // Limit to 8 tasks
  }
}

/**
 * Create a context builder instance
 */
export function createContextBuilder(
  conn: KuzuConnection,
  embeddings?: EmbeddingService,
  logger?: Logger
): ContextBuilder {
  return new ContextBuilder(conn, embeddings, logger);
}
