import 'dotenv/config';
import { PrismaClient } from '@prisma/client';

type CliOptions = {
  empresaId?: string;
};

type CatalogRefs = {
  sucursalId: string;
  departamentoId: string;
  cargoId: string;
  centroCostoId: string;
  tipoEmpleadoId: string;
};

type CatalogCreated = {
  tipoEmpleado: number;
  cargo: number;
  centroCosto: number;
  departamento: number;
};

const MIN_EMPLEADOS_ACTIVOS = 20;

const NOMBRES = [
  'Juan',
  'Mariana',
  'Carlos',
  'Lucia',
  'Miguel',
  'Ana',
  'Pedro',
  'Sofia',
  'Diego',
  'Camila',
  'Jorge',
  'Paola',
  'Ricardo',
  'Elena',
  'Andres',
  'Natalia',
  'Fernando',
  'Gabriela',
  'Oscar',
  'Valeria',
  'Roberto',
  'Patricia',
  'Daniel',
  'Monica',
];

const APELLIDOS = [
  'Gonzalez',
  'Benitez',
  'Lopez',
  'Martinez',
  'Rojas',
  'Fernandez',
  'Acosta',
  'Romero',
  'Sosa',
  'Ramirez',
  'Vera',
  'Caballero',
  'Mendoza',
  'Duarte',
  'Gimenez',
  'Ortiz',
  'Alvarez',
  'Pereira',
  'Nunez',
  'Riveros',
  'Insfran',
  'Valdez',
  'Medina',
  'Franco',
];

const ESTADOS_CIVILES = ['SOLTERO', 'CASADO', 'DIVORCIADO', 'UNION_LIBRE'];
const TIPOS_SALARIO = ['MENSUAL', 'JORNAL', 'COMISION'];

const prisma = new PrismaClient();

function parseArgs(argv: string[]): CliOptions {
  const options: CliOptions = {};

  for (let i = 0; i < argv.length; i++) {
    const raw = argv[i];
    if (!raw.startsWith('--')) continue;

    if (raw.startsWith('--empresa-id=')) {
      const value = raw.slice('--empresa-id='.length).trim();
      if (value) options.empresaId = value;
      continue;
    }

    if (raw === '--empresa-id') {
      const value = (argv[i + 1] || '').trim();
      if (value && !value.startsWith('--')) {
        options.empresaId = value;
        i += 1;
      }
    }
  }

  return options;
}

function requireEmpresaId(options: CliOptions): string {
  if (!options.empresaId) {
    throw new Error(
      'Parámetro obligatorio faltante: --empresa-id=<UUID>. Ejemplo: npm run seed:rrhh:empleados -- --empresa-id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
    );
  }
  const empresaId = options.empresaId.trim();
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
  if (!uuidRegex.test(empresaId)) {
    throw new Error(
      `El valor de --empresa-id no es un UUID válido: ${empresaId}`,
    );
  }
  return empresaId;
}

async function ensureEmpresa(empresaId: string) {
  const empresa = await prisma.empresas.findUnique({
    where: { id: empresaId },
    select: { id: true, razon_social: true, active: true },
  });

  if (!empresa) {
    throw new Error(`La empresa ${empresaId} no existe`);
  }

  return empresa;
}

async function getSucursalValida(empresaId: string) {
  const sucursal = await prisma.empresas_sucursales.findFirst({
    where: { empresa_id: empresaId },
    orderBy: [{ active: 'desc' }, { created_at: 'asc' }],
    select: { id: true, descripcion: true },
  });

  if (!sucursal) {
    throw new Error(
      `La empresa ${empresaId} no tiene sucursales. Cree al menos una sucursal antes de ejecutar este seed de RRHH.`,
    );
  }

  return sucursal;
}

async function ensureCatalogosMinimos(empresaId: string, sucursalIdFallback: string) {
  const created: CatalogCreated = {
    tipoEmpleado: 0,
    cargo: 0,
    centroCosto: 0,
    departamento: 0,
  };

  let tipoEmpleado = await prisma.rrhh_tipos_empleado.findFirst({
    where: { empresa_id: empresaId, activo: true },
    orderBy: { created_at: 'asc' },
    select: { id: true },
  });

  if (!tipoEmpleado) {
    tipoEmpleado = await prisma.rrhh_tipos_empleado.create({
      data: {
        empresa_id: empresaId,
        codigo: 'DEMO',
        nombre: 'Empleado demo',
        recibe_aguinaldo: true,
        activo: true,
      },
      select: { id: true },
    });
    created.tipoEmpleado = 1;
  }

  let cargo = await prisma.rrhh_cargos.findFirst({
    where: { empresa_id: empresaId, activo: true },
    orderBy: { created_at: 'asc' },
    select: { id: true },
  });

  if (!cargo) {
    cargo = await prisma.rrhh_cargos.create({
      data: {
        empresa_id: empresaId,
        codigo: 'DEMO',
        nombre: 'Empleado general demo',
        activo: true,
      },
      select: { id: true },
    });
    created.cargo = 1;
  }

  // Centro de costo unificado con Contabilidad (cont_centros_costo).
  let centroCosto = await prisma.cont_centros_costo.findFirst({
    where: { empresa_id: empresaId, activo: true },
    orderBy: { created_at: 'asc' },
    select: { id: true },
  });

  if (!centroCosto) {
    centroCosto = await prisma.cont_centros_costo.create({
      data: {
        empresa_id: empresaId,
        codigo: 'DEMO',
        descripcion: 'Centro de costo demo RRHH',
        activo: true,
      },
      select: { id: true },
    });
    created.centroCosto = 1;
  }

  let departamento = await prisma.rrhh_departamentos.findFirst({
    where: { empresa_id: empresaId, activo: true },
    orderBy: { created_at: 'asc' },
    select: { id: true, sucursal_id: true },
  });

  if (!departamento) {
    departamento = await prisma.rrhh_departamentos.create({
      data: {
        empresa_id: empresaId,
        sucursal_id: sucursalIdFallback,
        codigo: 'DEMO',
        nombre: 'Departamento demo RRHH',
        activo: true,
      },
      select: { id: true, sucursal_id: true },
    });
    created.departamento = 1;
  }

  const refs: CatalogRefs = {
    sucursalId: departamento.sucursal_id,
    departamentoId: departamento.id,
    cargoId: cargo.id,
    centroCostoId: centroCosto.id,
    tipoEmpleadoId: tipoEmpleado.id,
  };

  return { refs, created };
}

function normalizeToken(value: string) {
  return value
    .normalize('NFD')
    .replace(/[\u0300-\u036f]/g, '')
    .replace(/[^a-zA-Z0-9]+/g, '')
    .toLowerCase();
}

async function buildUniqueSets(empresaId: string) {
  const existentes = await prisma.rrhh_empleados.findMany({
    where: { empresa_id: empresaId },
    select: { numero_empleado: true, cedula_identidad: true },
  });

  const numeros = new Set(existentes.map((e) => e.numero_empleado));
  const cedulas = new Set(existentes.map((e) => e.cedula_identidad));

  return { numeros, cedulas };
}

function nextNumeroEmpleado(startAt: number, usados: Set<string>) {
  let current = startAt;
  while (true) {
    const candidate = `DEMO-${String(current).padStart(4, '0')}`;
    if (!usados.has(candidate)) return { value: candidate, next: current + 1 };
    current += 1;
  }
}

function nextCedula(startAt: number, usadas: Set<string>) {
  let current = startAt;
  while (true) {
    const candidate = String(current);
    if (!usadas.has(candidate)) return { value: candidate, next: current + 1 };
    current += 1;
  }
}

async function crearEmpleadosFaltantes(empresaId: string, refs: CatalogRefs, faltantes: number) {
  const { numeros, cedulas } = await buildUniqueSets(empresaId);
  let numeroSeq = 1;
  let cedulaSeq = 9000000;
  let created = 0;

  for (let i = 0; i < faltantes; i++) {
    const numero = nextNumeroEmpleado(numeroSeq, numeros);
    numeroSeq = numero.next;

    const cedula = nextCedula(cedulaSeq, cedulas);
    cedulaSeq = cedula.next;

    const firstName = NOMBRES[(i + created) % NOMBRES.length];
    const lastName = APELLIDOS[(i * 2 + created) % APELLIDOS.length];
    const sexo = (i + created) % 2 === 0 ? 'M' : 'F';
    const estadoCivil = ESTADOS_CIVILES[(i + created) % ESTADOS_CIVILES.length];
    const tipoSalario = TIPOS_SALARIO[(i + created) % TIPOS_SALARIO.length];
    const salarioBase = 2800000 + (i + created) * 175000;

    const nacimientoAnio = 1987 + ((i + created) % 10);
    const nacimientoMes = (i + created) % 12;
    const nacimientoDia = ((i + created) % 27) + 1;

    const ingresoAnio = 2018 + ((i + created) % 8);
    const ingresoMes = (i + created + 3) % 12;
    const ingresoDia = ((i * 2 + created) % 27) + 1;

    const email = `${normalizeToken(firstName)}.${normalizeToken(lastName)}.${numero.value.toLowerCase().replace(/[^a-z0-9]/g, '')}@demo.local`;

    await prisma.rrhh_empleados.create({
      data: {
        empresa_id: empresaId,
        numero_empleado: numero.value,
        nombres: firstName,
        apellidos: lastName,
        cedula_identidad: cedula.value,
        fecha_nacimiento: new Date(Date.UTC(nacimientoAnio, nacimientoMes, nacimientoDia)),
        sexo,
        estado_civil: estadoCivil,
        cantidad_hijos: (i + created) % 4,
        email,
        telefono: `09${String(6000000 + i + created).slice(-7)}`,
        direccion: `Calle Demo ${i + created + 1}`,
        sucursal_id: refs.sucursalId,
        departamento_id: refs.departamentoId,
        cargo_id: refs.cargoId,
        centro_costo_id: refs.centroCostoId,
        tipo_empleado_id: refs.tipoEmpleadoId,
        fecha_ingreso: new Date(Date.UTC(ingresoAnio, ingresoMes, ingresoDia)),
        salario_base: salarioBase,
        tipo_salario: tipoSalario,
        aporta_ips: true,
        regimen_ips: 'GENERAL',
        estado: 'ACTIVO',
        observaciones: 'Empleado de prueba generado automáticamente por seed RRHH',
      },
    });

    numeros.add(numero.value);
    cedulas.add(cedula.value);
    created += 1;
  }

  return created;
}

async function main() {
  const options = parseArgs(process.argv.slice(2));
  const empresaId = requireEmpresaId(options);

  const empresa = await ensureEmpresa(empresaId);
  const sucursal = await getSucursalValida(empresaId);

  const { refs, created: catalogsCreated } = await ensureCatalogosMinimos(empresaId, sucursal.id);

  const activosIniciales = await prisma.rrhh_empleados.count({
    where: { empresa_id: empresaId, estado: 'ACTIVO' },
  });

  const faltantes = Math.max(0, MIN_EMPLEADOS_ACTIVOS - activosIniciales);
  const empleadosCreados =
    faltantes > 0 ? await crearEmpleadosFaltantes(empresaId, refs, faltantes) : 0;

  const activosFinales = await prisma.rrhh_empleados.count({
    where: { empresa_id: empresaId, estado: 'ACTIVO' },
  });

  console.log('Seed RRHH completado');
  console.log(
    JSON.stringify(
      {
        empresa: {
          id: empresa.id,
          razon_social: empresa.razon_social,
          active: empresa.active,
        },
        sucursal_usada: sucursal,
        catalogos_creados: catalogsCreated,
        empleados_activos_iniciales: activosIniciales,
        empleados_creados: empleadosCreados,
        empleados_activos_finales: activosFinales,
        objetivo_minimo_activos: MIN_EMPLEADOS_ACTIVOS,
      },
      null,
      2,
    ),
  );
}

main()
  .catch((error) => {
    console.error('[seed-rrhh-empleados] Error:', error.message || error);
    process.exitCode = 1;
  })
  .finally(async () => {
    await prisma.$disconnect();
  });
