/**
 * Graph schema definitions for Layer A - Code Structure
 *
 * Defines node tables, relationship tables, and indexes for the code graph.
 */

import type { Connection } from 'kuzu';
import type { Logger } from '../utils/logger.js';
import { createLogger } from '../utils/logger.js';

/**
 * Node table DDL statements
 */
const NODE_TABLES = {
  File: `
    CREATE NODE TABLE IF NOT EXISTS File (
      path STRING,
      name STRING,
      extension STRING,
      size_bytes INT64,
      lines_of_code INT64,
      hash STRING,
      last_modified TIMESTAMP,
      PRIMARY KEY (path)
    )
  `,

  Class: `
    CREATE NODE TABLE IF NOT EXISTS Class (
      qualified_name STRING,
      name STRING,
      file_path STRING,
      line_start INT64,
      line_end INT64,
      docstring STRING,
      is_abstract BOOL,
      decorators STRING[],
      base_classes STRING[],
      embedding DOUBLE[],
      PRIMARY KEY (qualified_name)
    )
  `,

  Function: `
    CREATE NODE TABLE IF NOT EXISTS Function (
      qualified_name STRING,
      name STRING,
      file_path STRING,
      line_start INT64,
      line_end INT64,
      docstring STRING,
      parameters STRING[],
      return_annotation STRING,
      is_async BOOL,
      is_method BOOL,
      is_static BOOL,
      decorators STRING[],
      calls STRING[],
      parent_class STRING,
      cyclomatic_complexity INT64,
      embedding DOUBLE[],
      PRIMARY KEY (qualified_name)
    )
  `,

  Module: `
    CREATE NODE TABLE IF NOT EXISTS Module (
      name STRING,
      import_path STRING,
      is_external BOOL,
      package STRING,
      PRIMARY KEY (name)
    )
  `,
};

/**
 * Relationship table DDL statements
 */
const REL_TABLES = {
  CONTAINS_CLASS: `
    CREATE REL TABLE IF NOT EXISTS CONTAINS_CLASS (
      FROM File TO Class,
      line_start INT64
    )
  `,

  CONTAINS_FUNCTION: `
    CREATE REL TABLE IF NOT EXISTS CONTAINS_FUNCTION (
      FROM File TO Function,
      line_start INT64
    )
  `,

  HAS_METHOD: `
    CREATE REL TABLE IF NOT EXISTS HAS_METHOD (
      FROM Class TO Function
    )
  `,

  INHERITS_FROM: `
    CREATE REL TABLE IF NOT EXISTS INHERITS_FROM (
      FROM Class TO Class
    )
  `,

  CALLS: `
    CREATE REL TABLE IF NOT EXISTS CALLS (
      FROM Function TO Function
    )
  `,

  IMPORTS: `
    CREATE REL TABLE IF NOT EXISTS IMPORTS (
      FROM File TO Module,
      import_type STRING,
      line_number INT64,
      alias STRING
    )
  `,
};

/**
 * Index creation statements
 */
const INDEXES = [
  // File indexes
  'CREATE INDEX IF NOT EXISTS file_name_idx ON File(name)',
  'CREATE INDEX IF NOT EXISTS file_extension_idx ON File(extension)',

  // Class indexes
  'CREATE INDEX IF NOT EXISTS class_name_idx ON Class(name)',
  'CREATE INDEX IF NOT EXISTS class_file_path_idx ON Class(file_path)',

  // Function indexes
  'CREATE INDEX IF NOT EXISTS function_name_idx ON Function(name)',
  'CREATE INDEX IF NOT EXISTS function_file_path_idx ON Function(file_path)',
  'CREATE INDEX IF NOT EXISTS function_is_method_idx ON Function(is_method)',

  // Module indexes
  'CREATE INDEX IF NOT EXISTS module_import_path_idx ON Module(import_path)',
  'CREATE INDEX IF NOT EXISTS module_is_external_idx ON Module(is_external)',
];

/**
 * Schema Manager for Layer A - Code Structure
 *
 * Handles creation and management of graph schema.
 */
export class SchemaManager {
  private conn: Connection;
  private logger: Logger;

  constructor(conn: Connection, logger?: Logger) {
    this.conn = conn;
    this.logger = logger ?? createLogger({ name: 'schema-manager' });
  }

  /**
   * Initialize the complete schema
   */
  async initialize(): Promise<void> {
    this.logger.info('Initializing graph schema');

    await this.createNodeTables();
    await this.createRelTables();
    await this.createIndexes();

    this.logger.info('Graph schema initialized');
  }

  /**
   * Create all node tables
   */
  async createNodeTables(): Promise<void> {
    this.logger.debug('Creating node tables');

    for (const [name, ddl] of Object.entries(NODE_TABLES)) {
      try {
        await this.conn.query(ddl.trim());
        this.logger.debug({ table: name }, 'Created node table');
      } catch (err) {
        // Ignore "already exists" errors
        if (!String(err).includes('already exists')) {
          this.logger.error({ table: name, error: err }, 'Failed to create node table');
          throw err;
        }
      }
    }
  }

  /**
   * Create all relationship tables
   */
  async createRelTables(): Promise<void> {
    this.logger.debug('Creating relationship tables');

    for (const [name, ddl] of Object.entries(REL_TABLES)) {
      try {
        await this.conn.query(ddl.trim());
        this.logger.debug({ table: name }, 'Created relationship table');
      } catch (err) {
        if (!String(err).includes('already exists')) {
          this.logger.error({ table: name, error: err }, 'Failed to create relationship table');
          throw err;
        }
      }
    }
  }

  /**
   * Create all indexes
   */
  async createIndexes(): Promise<void> {
    this.logger.debug('Creating indexes');

    for (const ddl of INDEXES) {
      try {
        await this.conn.query(ddl);
      } catch (err) {
        // Ignore "already exists" errors
        if (!String(err).includes('already exists')) {
          this.logger.warn({ ddl, error: err }, 'Failed to create index');
        }
      }
    }
  }

  /**
   * Drop all tables (dangerous!)
   */
  async dropAllTables(): Promise<void> {
    this.logger.warn('Dropping all graph tables');

    // Drop relationships first (they depend on nodes)
    for (const name of Object.keys(REL_TABLES)) {
      try {
        await this.conn.query(`DROP TABLE IF EXISTS ${name}`);
      } catch {
        // Ignore errors
      }
    }

    // Then drop nodes
    for (const name of Object.keys(NODE_TABLES)) {
      try {
        await this.conn.query(`DROP TABLE IF EXISTS ${name}`);
      } catch {
        // Ignore errors
      }
    }

    this.logger.info('All tables dropped');
  }

  /**
   * Reset schema (drop and recreate)
   */
  async reset(): Promise<void> {
    await this.dropAllTables();
    await this.initialize();
  }

  /**
   * Get schema information
   */
  async getSchemaInfo(): Promise<{
    nodeTables: string[];
    relTables: string[];
  }> {
    return {
      nodeTables: Object.keys(NODE_TABLES),
      relTables: Object.keys(REL_TABLES),
    };
  }
}

// Export table names for reference
export const NODE_TABLE_NAMES = Object.keys(NODE_TABLES);
export const REL_TABLE_NAMES = Object.keys(REL_TABLES);
