/**
 * Database Manager for Integration Tests
 * Handles database connections, migrations, seeding, and cleanup
 */

import { Pool, PoolClient, QueryResult } from 'pg';
import { setTimeout } from 'timers/promises';

export interface DatabaseConfig {
  host: string;
  port: number;
  database: string;
  user: string;
  password: string;
}

export class DatabaseManager {
  private pool: Pool;
  private client: PoolClient | null = null;

  constructor(config: DatabaseConfig) {
    this.pool = new Pool({
      host: config.host,
      port: config.port,
      database: config.database,
      user: config.user,
      password: config.password,
      max: 1,
      idleTimeoutMillis: 30000,
      connectionTimeoutMillis: 10000,
    });
  }

  /**
   * Wait for database to be ready
   */
  async waitForReady(timeout: number = 30000): Promise<void> {
    const startTime = Date.now();
    
    while (Date.now() - startTime < timeout) {
      try {
        const client = await this.pool.connect();
        await client.query('SELECT 1');
        client.release();
        console.log('[DatabaseManager] ✓ Database is ready');
        return;
      } catch (error) {
        await setTimeout(1000);
      }
    }
    
    throw new Error('Database failed to become ready');
  }

  /**
   * Get a client for transactions
   */
  async getClient(): Promise<PoolClient> {
    if (!this.client) {
      this.client = await this.pool.connect();
    }
    return this.client;
  }

  /**
   * Execute query with automatic client management
   */
  async query(text: string, params?: unknown[]): Promise<QueryResult> {
    const client = await this.getClient();
    return client.query(text, params);
  }

  /**
   * Begin transaction
   */
  async begin(): Promise<void> {
    const client = await this.getClient();
    await client.query('BEGIN');
  }

  /**
   * Commit transaction
   */
  async commit(): Promise<void> {
    if (this.client) {
      await this.client.query('COMMIT');
    }
  }

  /**
   * Rollback transaction
   */
  async rollback(): Promise<void> {
    if (this.client) {
      await this.client.query('ROLLBACK');
    }
  }

  /**
   * Truncate all tables (for cleanup between tests)
   */
  async truncateAllTables(): Promise<void> {
    const client = await this.getClient();
    
    // Get all table names
    const result = await client.query(`
      SELECT tablename 
      FROM pg_tables 
      WHERE schemaname = 'public' 
      AND tablename NOT LIKE 'pg_%' 
      AND tablename NOT LIKE 'sql_%'
    `);

    if (result.rows.length === 0) return;

    // Disable triggers temporarily to allow truncation
    await client.query('SET session_replication_role = replica');

    const tableNames = result.rows.map(r => r.tablename).join(', ');
    try {
      await client.query(`TRUNCATE TABLE ${tableNames} CASCADE`);
      console.log(`[DatabaseManager] ✓ Truncated tables: ${tableNames}`);
    } finally {
      await client.query('SET session_replication_role = DEFAULT');
    }
  }

  /**
   * Run database migrations
   */
  async runMigrations(): Promise<void> {
    // This would typically run TypeORM migrations
    // For now, we'll assume the API handles this on startup
    console.log('[DatabaseManager] Migrations handled by API startup');
  }

  /**
   * Seed minimal test data
   */
  async seedMinimalData(): Promise<void> {
    // Insert minimal required data for tests
    // This will be extended based on test requirements
    
    console.log('[DatabaseManager] ✓ Minimal test data seeded');
  }

  /**
   * Check for constraint violations in recent operations
   */
  async getRecentConstraintErrors(since: Date): Promise<string[]> {
    const client = await this.getClient();
    
    const result = await client.query(`
      SELECT 
        sqlstate,
        message,
        detail,
        constraint_name
      FROM pg_last_error_log() 
      WHERE sqlstate IN ('23505', '23503', '23514')
      AND log_time > $1
      ORDER BY log_time DESC
    `, [since]);

    return (result.rows as { message: string }[]).map(r => r.message);
  }

  /**
   * Get table constraints
   */
  async getTableConstraints(tableName: string): Promise<unknown[]> {
    const client = await this.getClient();
    
    const result = await client.query(`
      SELECT 
        conname as constraint_name,
        contype as constraint_type,
        pg_get_constraintdef(oid) as definition
      FROM pg_constraint
      WHERE conrelid = $1::regclass
      ORDER BY contype
    `, [tableName]);

    return result.rows;
  }

  /**
   * Close connection pool
   */
  async close(): Promise<void> {
    if (this.client) {
      this.client.release();
      this.client = null;
    }
    await this.pool.end();
  }
}