/**
 * Graph Builder for Layer A - Code Structure
 *
 * Orchestrates building the code graph from parsed entities.
 */

import type { KuzuConnection } from './connection.js';
import { GraphWriter } from './writer.js';
import type {
  CodebaseEntities,
  FileEntities,
  FileEntity,
  ClassEntity,
  FunctionEntity,
  ModuleEntity,
  ImportStatement,
  GraphDatabaseInfo,
} from './types.js';
import { createLogger, type Logger } from '../utils/logger.js';

/**
 * Build options
 */
export interface BuildOptions {
  /** Batch size for writes */
  batchSize?: number;
  /** Whether to clear existing data first */
  clearFirst?: boolean;
  /** Logger instance */
  logger?: Logger;
}

/**
 * Build statistics
 */
export interface BuildStats {
  filesWritten: number;
  classesWritten: number;
  functionsWritten: number;
  modulesWritten: number;
  importsWritten: number;
  inheritanceWritten: number;
  callsWritten: number;
  durationMs: number;
}

/**
 * Graph Builder for Layer A
 *
 * Provides high-level API for building the code structure graph:
 * - Build from aggregated entities
 * - Incremental updates
 * - Full rebuilds
 */
export class GraphBuilder {
  private conn: KuzuConnection;
  private writer: GraphWriter;
  private logger: Logger;

  constructor(conn: KuzuConnection, options?: { batchSize?: number; logger?: Logger }) {
    this.conn = conn;
    this.logger = options?.logger ?? createLogger({ name: 'graph-builder' });
    this.writer = new GraphWriter(conn, {
      batchSize: options?.batchSize,
      logger: this.logger,
    });
  }

  /**
   * Build graph from complete codebase entities
   */
  async build(entities: CodebaseEntities, options?: BuildOptions): Promise<BuildStats> {
    const startTime = Date.now();
    this.logger.info({ options }, 'Building code graph');

    // Initialize schema
    await this.conn.initializeSchema();

    // Clear if requested
    if (options?.clearFirst) {
      await this.writer.clearAll();
    }

    // Write entities in dependency order
    const filesWritten = await this.writer.writeFiles(entities.files);
    const modulesWritten = await this.writer.writeModules(entities.modules);
    const classesWritten = await this.writer.writeClasses(entities.classes);
    const functionsWritten = await this.writer.writeFunctions(entities.functions);
    const importsWritten = await this.writer.writeImports(entities.imports);

    // Write relationship edges
    const inheritanceWritten = await this.writer.writeInheritance(entities.classes);
    const callsWritten = await this.writer.writeCalls(entities.functions);

    const durationMs = Date.now() - startTime;
    const stats: BuildStats = {
      filesWritten,
      classesWritten,
      functionsWritten,
      modulesWritten,
      importsWritten,
      inheritanceWritten,
      callsWritten,
      durationMs,
    };

    this.logger.info({ stats }, 'Code graph built');
    return stats;
  }

  /**
   * Update graph with entities from a single file
   */
  async updateFile(fileEntities: FileEntities): Promise<void> {
    this.logger.debug({ path: fileEntities.file.path }, 'Updating file in graph');

    // Delete existing file and its entities
    await this.writer.deleteFiles([fileEntities.file.path]);

    // Write new entities
    await this.writer.writeFiles([fileEntities.file]);
    await this.writer.writeModules(fileEntities.modules);
    await this.writer.writeClasses(fileEntities.classes);
    await this.writer.writeFunctions(fileEntities.functions);
    await this.writer.writeImports(fileEntities.imports);

    // Write relationships
    await this.writer.writeInheritance(fileEntities.classes);
    await this.writer.writeCalls(fileEntities.functions);

    this.logger.debug({ path: fileEntities.file.path }, 'File updated in graph');
  }

  /**
   * Update graph with multiple files
   */
  async updateFiles(filesEntities: FileEntities[]): Promise<void> {
    this.logger.info({ count: filesEntities.length }, 'Updating files in graph');

    // Delete existing files
    const filePaths = filesEntities.map((fe) => fe.file.path);
    await this.writer.deleteFiles(filePaths);

    // Aggregate entities
    const files: FileEntity[] = [];
    const classes: ClassEntity[] = [];
    const functions: FunctionEntity[] = [];
    const modules: ModuleEntity[] = [];
    const imports: ImportStatement[] = [];

    for (const fe of filesEntities) {
      files.push(fe.file);
      classes.push(...fe.classes);
      functions.push(...fe.functions);
      modules.push(...fe.modules);
      imports.push(...fe.imports);
    }

    // Write entities
    await this.writer.writeFiles(files);
    await this.writer.writeModules(modules);
    await this.writer.writeClasses(classes);
    await this.writer.writeFunctions(functions);
    await this.writer.writeImports(imports);

    // Write relationships
    await this.writer.writeInheritance(classes);
    await this.writer.writeCalls(functions);

    this.logger.info({ count: filesEntities.length }, 'Files updated in graph');
  }

  /**
   * Remove files from graph
   */
  async removeFiles(filePaths: string[]): Promise<number> {
    this.logger.info({ count: filePaths.length }, 'Removing files from graph');
    return this.writer.deleteFiles(filePaths);
  }

  /**
   * Clear all graph data
   */
  async clear(): Promise<void> {
    await this.writer.clearAll();
  }

  /**
   * Get graph database info
   */
  async getInfo(): Promise<GraphDatabaseInfo> {
    const dbInfo = await this.conn.getDatabaseInfo();

    return {
      path: '', // Will be set by caller if needed
      nodeCount: {
        files: dbInfo.nodeCount.File ?? 0,
        classes: dbInfo.nodeCount.Class ?? 0,
        functions: dbInfo.nodeCount.Function ?? 0,
        modules: dbInfo.nodeCount.Module ?? 0,
      },
      relationshipCount: {
        containsClass: dbInfo.relCount.CONTAINS_CLASS ?? 0,
        containsFunction: dbInfo.relCount.CONTAINS_FUNCTION ?? 0,
        hasMethod: dbInfo.relCount.HAS_METHOD ?? 0,
        inheritsFrom: dbInfo.relCount.INHERITS_FROM ?? 0,
        calls: dbInfo.relCount.CALLS ?? 0,
        imports: dbInfo.relCount.IMPORTS ?? 0,
      },
    };
  }

  /**
   * Rebuild entire graph
   */
  async rebuild(entities: CodebaseEntities): Promise<BuildStats> {
    return this.build(entities, { clearFirst: true });
  }
}
