import pg from 'pg';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { logger } from '../utils/logger.js';
import partitionManager from './partitionManager.js';
import dbManager from './databaseManager.js';
import { config } from '../config/config.js';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

class DatabaseInitializer {
  async initialize() {
    logger.info('Starting database initialization check...');
    
    // 1. Check if database exists, create if not
    await this.ensureDatabaseExists();

    // 2. Initialize Schema and Parent Table (if not exists)
    // Note: We need to use dbManager because it connects to the target database
    await this.ensureSchemaAndTable();

    // 3. Ensure Partitions for the next month
    await partitionManager.ensurePartitions(30);
    
    logger.info('Database initialization completed successfully.');
  }

  async ensureDatabaseExists() {
    const { host, port, user, password, database, ssl } = config.db;

    // Connect to 'postgres' database to check/create target database
    const client = new pg.Client({
      host,
      port,
      user,
      password,
      database: 'postgres',
      ssl: ssl ? { rejectUnauthorized: false } : false
    });

    const maxRetries = 5;
    let retryCount = 0;

    while (retryCount < maxRetries) {
      try {
        await client.connect();
        break;
      } catch (err) {
        if (err.code === 'EADDRINUSE') {
          retryCount++;
          logger.warn(`Port conflict (EADDRINUSE) connecting to database, retrying (${retryCount}/${maxRetries})...`);
          await new Promise(resolve => setTimeout(resolve, 1000));
        } else {
          throw err;
        }
      }
    }

    try {
      const checkRes = await client.query(
        `SELECT 1 FROM pg_database WHERE datname = $1`,
        [database]
      );

      if (checkRes.rowCount === 0) {
        logger.info(`Database '${database}' does not exist. Creating...`);
        // CREATE DATABASE cannot run inside a transaction block
        await client.query(`CREATE DATABASE "${database}"`);
        logger.info(`Database '${database}' created.`);
      } else {
        logger.info(`Database '${database}' already exists.`);
      }
    } catch (err) {
      logger.error('Error ensuring database exists:', err);
      throw err;
    } finally {
      await client.end();
    }
  }

  async ensureSchemaAndTable() {
    // dbManager connects to the target database
    const client = await dbManager.pool.connect();
    try {
      const sqlPathCandidates = [
        path.resolve(process.cwd(), 'scripts/init_db.sql'),
        path.resolve(__dirname, '../scripts/init_db.sql'),
        path.resolve(__dirname, '../../scripts/init_db.sql')
      ];
      const sqlPath = sqlPathCandidates.find((candidate) => fs.existsSync(candidate));
      if (!sqlPath) {
        throw new Error(`init_db.sql not found. Candidates: ${sqlPathCandidates.join(' | ')}`);
      }
      const sql = fs.readFileSync(sqlPath, 'utf8');
      
      logger.info('Executing init_db.sql...');
      await client.query(sql);
      logger.info('Schema and parent table initialized.');
    } catch (err) {
      logger.error('Error initializing schema and table:', err);
      throw err;
    } finally {
      client.release();
    }
  }
}

export default new DatabaseInitializer();
