#!/usr/bin/env tsx
/**
 * SAR — Provisionamento de cliente
 *
 * Aplica o schema SAR (views + tabelas + triggers) no banco ERP do cliente.
 * Não requer psql — usa a lib pg que já é dependência do projeto.
 *
 * Uso:
 *   pnpm client:provision --host <host> --db <banco> [opções]
 *
 * Opções:
 *   --host      Host do PostgreSQL (obrigatório)
 *   --db        Nome do banco ERP  (obrigatório)
 *   --port      Porta (default: 5432)
 *   --user      Usuário (default: postgres)
 *   --password  Senha (default: lê PG_PASSWORD do env)
 *   --role      Role do app SAR — aplica GRANTs se informado (ex: sar_app)
 *
 * O que faz:
 *   1. Executa scripts/sar-erp-schema.sql  (schema sar + views + tabelas SAR)
 *   2. Executa scripts/sar-triggers.sql    (triggers de sync ERP + staging tables)
 *   3. Aplica GRANTs se --role foi informado
 *   4. Executa `prisma migrate deploy`     (aplica migrations Prisma pendentes)
 */

import { readFileSync, readdirSync } from 'fs';
import { resolve, dirname } from 'path';
import { fileURLToPath } from 'url';
import { execSync } from 'child_process';
import pg from 'pg';

// ─── CLI args ─────────────────────────────────────────────────────────────────

function arg(flag: string): string | undefined {
  const idx = process.argv.indexOf(flag);
  return idx !== -1 ? process.argv[idx + 1] : undefined;
}

const host = arg('--host');
const db = arg('--db');
const port = parseInt(arg('--port') ?? '5432', 10);
const user = arg('--user') ?? 'postgres';
const password = arg('--password') ?? process.env['PG_PASSWORD'] ?? '';
const role = arg('--role');

if (!host || !db) {
  console.error(
    'Uso: pnpm client:provision --host <host> --db <banco> [--port 5432] [--user postgres] [--password ...] [--role <pg-role>]',
  );
  console.error(
    'Exemplo: pnpm client:provision --host 192.168.0.43 --db libreplast --role sar_app',
  );
  process.exit(1);
}

// ─── Paths ────────────────────────────────────────────────────────────────────

const SCRIPTS_DIR = dirname(fileURLToPath(import.meta.url));
const MONOREPO_ROOT = resolve(SCRIPTS_DIR, '..');
const API_DIR = resolve(MONOREPO_ROOT, 'apps/api');

// Banco ERP usa LATIN1 — strip chars fora do range 0x00-0xFF (decorações nos comentários SQL)
function toLatinSafe(sql: string): string {
  // O range LATIN1 começa em \x00 por definição — control chars intencionais.
  // eslint-disable-next-line no-control-regex
  return sql.replace(/[^\x00-\xFF]/g, (c) => {
    if (c === '—' || c === '–') return '--'; // em dash / en dash
    return '-'; // box-drawing chars e outros
  });
}

const SQL_SCHEMA = toLatinSafe(readFileSync(resolve(SCRIPTS_DIR, 'sar-erp-schema.sql'), 'utf8'));
const SQL_TRIGGERS = toLatinSafe(readFileSync(resolve(SCRIPTS_DIR, 'sar-triggers.sql'), 'utf8'));

const DATABASE_URL = `postgresql://${user}:${encodeURIComponent(password)}@${host}:${port}/${db}`;

// ─── Helpers ──────────────────────────────────────────────────────────────────

async function runSql(client: pg.Client, label: string, sql: string): Promise<void> {
  console.log(`  Executando ${label}...`);
  await client.query(sql);
  console.log(`  ✓ ${label} aplicado`);
}

async function applyGrants(client: pg.Client, appRole: string): Promise<void> {
  console.log(`  Aplicando GRANTs para role "${appRole}"...`);
  await client.query(`
    GRANT USAGE ON SCHEMA sar TO ${appRole};
    GRANT SELECT ON ALL TABLES IN SCHEMA sar TO ${appRole};
    GRANT INSERT, UPDATE, DELETE ON
        sar.pedidos,
        sar.pedido_itens,
        sar.historico_pedido,
        sar.alcada_desconto,
        sar.meta_representante,
        sar.push_subscription,
        sar.promocoes,
        sar.regras_desconto,
        sar.config_empresa,
        sar.clientes_novos,
        sar.contatos_novos
    TO ${appRole};
    GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA sar TO ${appRole};
  `);
  console.log(`  ✓ GRANTs aplicados`);
}

// ─── Main ─────────────────────────────────────────────────────────────────────

async function main(): Promise<void> {
  console.log('\nSAR — Provisionamento de Cliente');
  console.log('──────────────────────────────────');
  console.log(`Host:  ${host}:${port}`);
  console.log(`Banco: ${db}`);
  console.log(`User:  ${user}`);
  if (role) console.log(`Role:  ${role} (GRANTs serão aplicados)`);
  console.log('');

  const client = new pg.Client({ host, port, database: db, user, password });
  await client.connect();

  let needsBaseline = false;

  try {
    console.log('[1/3] Schema SAR...');
    await runSql(client, 'sar-erp-schema.sql', SQL_SCHEMA);

    console.log('[2/3] Triggers e tabelas staging...');
    await runSql(client, 'sar-triggers.sql', SQL_TRIGGERS);

    if (role) {
      console.log('[+]   GRANTs...');
      await applyGrants(client, role);
    }

    // O schema SQL acima já criou as tabelas, então o schema `sar` nunca está
    // vazio quando o Prisma roda. Sem histórico de migrations o `migrate deploy`
    // aborta com P3005 — é preciso marcar a migration baseline como aplicada.
    const { rows } = await client.query(
      `SELECT to_regclass('sar._prisma_migrations') IS NOT NULL AS existe`,
    );
    needsBaseline = !rows[0]?.existe;
  } finally {
    await client.end();
  }

  // _prisma_migrations precisa morar no schema sar, não no public do ERP.
  const MIGRATE_URL = `${DATABASE_URL}?schema=sar`;
  const prismaEnv = { ...process.env, DATABASE_URL: MIGRATE_URL };

  console.log('[3/3] Migrations Prisma...');

  if (needsBaseline) {
    const baseline = readdirSync(resolve(API_DIR, 'prisma/migrations'), { withFileTypes: true })
      .filter((e) => e.isDirectory())
      .map((e) => e.name)
      .sort()[0];
    if (!baseline) throw new Error('nenhuma migration encontrada para baseline');
    console.log(`  Banco pré-existente: marcando "${baseline}" como aplicada (baseline)...`);
    execSync(`pnpm exec prisma migrate resolve --applied ${baseline}`, {
      cwd: API_DIR,
      env: prismaEnv,
      stdio: 'inherit',
    });
  }

  execSync('pnpm exec prisma migrate deploy', {
    cwd: API_DIR,
    env: prismaEnv,
    stdio: 'inherit',
  });
  console.log('  ✓ Migrations aplicadas');

  console.log('');
  console.log('════════════════════════════════════');
  console.log('✓ Cliente provisionado com sucesso');
  console.log('════════════════════════════════════');
  console.log('');
  console.log('Próximos passos:');
  console.log(`  1. Configure DATABASE_URL=${DATABASE_URL}`);
  console.log(`     no ambiente de produção (Vault ou .env do cliente)`);
  if (!role) {
    console.log('  2. Execute os GRANTs manualmente ou rode novamente com --role <pg-role>');
  }
  console.log('');
}

main().catch((e: unknown) => {
  console.error('\nErro durante provisionamento:', e);
  process.exit(1);
});
