import fs from 'fs';
import path from 'path';
import pg from 'pg';
import { fileURLToPath } from 'url';

const { Client } = pg;

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

const parseNumber = (value, defaultValue) => {
  const parsed = Number(value);
  return Number.isFinite(parsed) ? parsed : defaultValue;
};

const dbConfig = {
  host: process.env.DB_HOST || process.env.POSTGRES_HOST || 'localhost',
  port: parseNumber(process.env.DB_PORT || process.env.POSTGRES_PORT, 5432),
  user: process.env.DB_USER || process.env.POSTGRES_USER || 'postgres',
  password: process.env.DB_PASSWORD || process.env.POSTGRES_PASSWORD || '',
  database: process.env.DB_DATABASE || process.env.POSTGRES_DATABASE || 'bls_rcu_action',
  ssl: process.env.DB_SSL === 'true' ? { rejectUnauthorized: false } : undefined
};

const withClient = async (runner) => {
  const client = new Client(dbConfig);
  await client.connect();
  try {
    await runner(client);
  } finally {
    await client.end();
  }
};

const executeSqlFile = async (client, fileName) => {
  const filePath = path.join(scriptDir, fileName);
  const sql = fs.readFileSync(filePath, 'utf8');
  await client.query(sql);
};

export const ensureDatabase = async () => {
  const adminClient = new Client({
    ...dbConfig,
    database: process.env.DB_ADMIN_DATABASE || 'postgres'
  });
  await adminClient.connect();
  try {
    const targetDb = dbConfig.database;
    const check = await adminClient.query('SELECT 1 FROM pg_database WHERE datname = $1', [targetDb]);
    if (check.rowCount === 0) {
      await adminClient.query(`CREATE DATABASE "${targetDb}"`);
      console.log(`[SQL_Script] created database: ${targetDb}`);
    } else {
      console.log(`[SQL_Script] database exists: ${targetDb}`);
    }
  } finally {
    await adminClient.end();
  }
};

const toPartitionSuffix = (date) => {
  const yyyy = date.getFullYear();
  const mm = String(date.getMonth() + 1).padStart(2, '0');
  const dd = String(date.getDate()).padStart(2, '0');
  return `${yyyy}${mm}${dd}`;
};

const getDayRange = (date) => {
  const start = new Date(date);
  start.setHours(0, 0, 0, 0);
  const end = new Date(start);
  end.setDate(end.getDate() + 1);
  return { startMs: start.getTime(), endMs: end.getTime() };
};

export const ensureRcuPartitions = async (daysAhead = 30) => {
  const tpl = fs.readFileSync(path.join(scriptDir, 'partition_rcu_action.sql'), 'utf8');
  await withClient(async (client) => {
    const now = new Date();
    for (let i = 0; i < daysAhead; i++) {
      const d = new Date(now);
      d.setDate(now.getDate() + i);
      const suffix = toPartitionSuffix(d);
      const partitionName = `rcu_action.rcu_action_events_${suffix}`;
      const { startMs, endMs } = getDayRange(d);

      const sql = tpl
        .replaceAll('{partition_name}', partitionName)
        .replaceAll('{start_ms}', String(startMs))
        .replaceAll('{end_ms}', String(endMs));

      await client.query(sql);
    }
  });
  console.log(`[SQL_Script] ensured rcu_action partitions for ${daysAhead} days`);
};

export const ensureRoomStatusPartition = async (hotelId) => {
  if (!Number.isFinite(Number(hotelId))) {
    throw new Error('hotelId is required and must be a number');
  }
  const tpl = fs.readFileSync(path.join(scriptDir, 'partition_room_status.sql'), 'utf8');
  const sql = tpl.replaceAll('{hotel_id}', String(hotelId));

  await withClient(async (client) => {
    await client.query(sql);
  });

  console.log(`[SQL_Script] ensured room_status partition for hotel_id=${hotelId}`);
};

export const initAll = async () => {
  await ensureDatabase();
  await withClient(async (client) => {
    await executeSqlFile(client, 'init_rcu_action.sql');
    await executeSqlFile(client, 'init_room_status.sql');
  });
  console.log('[SQL_Script] initialized schemas and tables');
};

const run = async () => {
  const cmd = process.argv[2];

  if (!cmd) {
    throw new Error('missing command: init-all | init-rcu | init-room-status | partition-rcu [days] | partition-room-status <hotelId>');
  }

  switch (cmd) {
    case 'init-all':
      await initAll();
      break;
    case 'init-rcu':
      await withClient((client) => executeSqlFile(client, 'init_rcu_action.sql'));
      console.log('[SQL_Script] initialized rcu_action schema/table');
      break;
    case 'init-room-status':
      await withClient((client) => executeSqlFile(client, 'init_room_status.sql'));
      console.log('[SQL_Script] initialized room_status schema/table');
      break;
    case 'partition-rcu': {
      const days = parseNumber(process.argv[3], 30);
      await ensureRcuPartitions(days);
      break;
    }
    case 'partition-room-status': {
      const hotelId = process.argv[3];
      await ensureRoomStatusPartition(hotelId);
      break;
    }
    default:
      throw new Error(`unsupported command: ${cmd}`);
  }
};

run().catch((err) => {
  console.error('[SQL_Script] failed:', err?.message || err);
  process.exit(1);
});
