import 'dotenv/config';
import { PrismaClient } from '@prisma/client';
import { MAPEO_DEFAULT, PLAN_CUENTAS_PARAGUAY, type CuentaSeed } from '../src/contabilidad/seeds/plan-cuentas-paraguay.seed';

type CliOptions = {
  empresaId?: string;
  allActive: boolean;
  fixTesoreria: boolean;
  fixMapeos: boolean;
  modoTesoreria: 'AUTOMATICO' | 'CONFIRMACION' | 'MANUAL';
};

type CheckItem = {
  key: string;
  status: 'OK' | 'FAIL' | 'WARN' | 'SKIP';
  detail: string;
};

const REQUIRED_MAPEOS_COMPRAS = [
  'PROVEEDORES',
  'CAJA_GENERAL',
  'INVENTARIO',
  'IVA_CREDITO_10',
  'IVA_CREDITO_5',
  'GASTOS_GENERALES',
  'BANCOS_DEFAULT',
] as const;

const prisma = new PrismaClient();

function parseArgs(argv: string[]): CliOptions {
  const options: CliOptions = {
    allActive: false,
    fixTesoreria: false,
    fixMapeos: false,
    modoTesoreria: 'AUTOMATICO',
  };

  for (const raw of argv) {
    if (!raw.startsWith('--')) continue;
    const [key, value = ''] = raw.slice(2).split('=');

    if (key === 'empresa' && value) options.empresaId = value.trim();
    if (key === 'all-active') options.allActive = true;
    if (key === 'fix-tesoreria') options.fixTesoreria = true;
    if (key === 'fix-mapeos') options.fixMapeos = true;
    if (key === 'fix-all') {
      options.fixTesoreria = true;
      options.fixMapeos = true;
    }
    if (key === 'modo' && value) {
      const normalized = value.trim().toUpperCase();
      if (normalized === 'AUTOMATICO' || normalized === 'CONFIRMACION' || normalized === 'MANUAL') {
        options.modoTesoreria = normalized;
      }
    }
  }

  return options;
}

async function resolveCompanyIds(options: CliOptions): Promise<string[]> {
  if (options.allActive) {
    const empresas = await prisma.empresas.findMany({
      where: { active: true },
      select: { id: true },
      orderBy: { created_at: 'asc' },
    });
    return empresas.map((e) => e.id);
  }

  const empresaId = options.empresaId || process.env.HOLDING_EMPRESA_ID;
  if (empresaId) return [empresaId];

  const empresa = await prisma.empresas.findFirst({
    where: { active: true },
    select: { id: true },
    orderBy: { created_at: 'asc' },
  });

  if (!empresa) throw new Error('No se encontró empresa activa para validar');
  return [empresa.id];
}

function toDateYMD(date: Date): string {
  return date.toISOString().substring(0, 10);
}

async function ensureTesoreriaRule(empresaId: string, modo: CliOptions['modoTesoreria']): Promise<string> {
  const cuentaActiva = await prisma.tes_cuentas.findFirst({
    where: { empresa_id: empresaId, activo: true },
    orderBy: [{ tipo: 'asc' }, { created_at: 'asc' }],
    select: { id: true, nombre: true },
  });

  const cuentaId = cuentaActiva?.id
    ? cuentaActiva.id
    : (
        await prisma.tes_cuentas.create({
          data: {
            empresa_id: empresaId,
            nombre: 'Tesorería Pagos Proveedor',
            tipo: 'CAJA_CHICA',
            moneda: 'PYG',
            saldo_inicial: 0,
            saldo_actual: 0,
            activo: true,
          },
          select: { id: true },
        })
      ).id;

  const existente = await prisma.tes_reglas.findFirst({
    where: { empresa_id: empresaId, tipo_operacion: 'PAGO_PROVEEDOR', medio_pago_id: null },
    select: { id: true },
  });
  if (existente) {
    await prisma.tes_reglas.update({
      where: { id: existente.id },
      data: { cuenta_id: cuentaId, modo, activo: true, updated_at: new Date() },
    });
  } else {
    await prisma.tes_reglas.create({
      data: { empresa_id: empresaId, tipo_operacion: 'PAGO_PROVEEDOR', cuenta_id: cuentaId, modo, activo: true },
    });
  }

  return cuentaId;
}

async function findAccountForConcept(empresaId: string, concept: string): Promise<{ id: string; source: string } | null> {
  const code = MAPEO_DEFAULT[concept];
  if (!code) return null;

  const companyAccount = await prisma.cont_plan_cuentas.findFirst({
    where: {
      empresa_id: empresaId,
      codigo: code,
      active: true,
    },
    select: { id: true },
  });
  if (companyAccount) return { id: companyAccount.id, source: `empresa:${code}` };

  const systemAccount = await prisma.cont_plan_cuentas.findFirst({
    where: {
      empresa_id: null,
      codigo: code,
      active: true,
    },
    select: { id: true },
  });
  if (systemAccount) return { id: systemAccount.id, source: `sistema:${code}` };

  return null;
}

async function ensureAccountSeeded(empresaId: string, code: string): Promise<string | null> {
  const existing = await prisma.cont_plan_cuentas.findFirst({
    where: { empresa_id: empresaId, codigo: code, active: true },
    select: { id: true },
  });
  if (existing) return existing.id;

  const seedByCode = new Map<string, CuentaSeed>(PLAN_CUENTAS_PARAGUAY.map((c) => [c.codigo, c]));
  const seed = seedByCode.get(code);
  if (!seed) return null;

  const parentCode = code.includes('.') ? code.split('.').slice(0, -1).join('.') : null;
  let parentId: string | null = null;

  if (parentCode) {
    parentId = await ensureAccountSeeded(empresaId, parentCode);
    if (!parentId) {
      const parentExisting = await prisma.cont_plan_cuentas.findFirst({
        where: { empresa_id: empresaId, codigo: parentCode },
        select: { id: true },
      });
      parentId = parentExisting?.id ?? null;
    }
  }

  const created = await prisma.cont_plan_cuentas.create({
    data: {
      empresa_id: empresaId,
      codigo: seed.codigo,
      descripcion: seed.descripcion,
      tipo: seed.tipo,
      naturaleza: seed.naturaleza,
      nivel: seed.nivel,
      cuenta_padre_id: parentId,
      acepta_movimientos: seed.acepta_movimientos,
      acepta_cc: seed.acepta_cc ?? false,
      moneda_fija: seed.moneda_fija ?? null,
      is_sistema: true,
      active: true,
    },
    select: { id: true },
  });

  return created.id;
}

async function resolveFallbackAccountForConcept(empresaId: string, concept: string): Promise<{ id: string; source: string } | null> {
  if (concept === 'GASTOS_GENERALES') {
    const gastos = await prisma.cont_plan_cuentas.findFirst({
      where: {
        empresa_id: empresaId,
        tipo: 'GASTO',
        acepta_movimientos: true,
        active: true,
      },
      orderBy: { codigo: 'asc' },
      select: { id: true, codigo: true },
    });
    if (gastos) return { id: gastos.id, source: `fallback:${gastos.codigo}` };
  }

  if (concept === 'BANCOS_DEFAULT') {
    const bancos = await prisma.cont_plan_cuentas.findFirst({
      where: {
        empresa_id: empresaId,
        codigo: { startsWith: '1.1.1.' },
        acepta_movimientos: true,
        active: true,
      },
      orderBy: { codigo: 'asc' },
      select: { id: true, codigo: true },
    });
    if (bancos) return { id: bancos.id, source: `fallback:${bancos.codigo}` };
  }

  return null;
}

async function ensureRequiredMapeos(empresaId: string): Promise<Array<{ concept: string; accountId: string; source: string }>> {
  const applied: Array<{ concept: string; accountId: string; source: string }> = [];

  for (const concept of REQUIRED_MAPEOS_COMPRAS) {
    const code = MAPEO_DEFAULT[concept];

    let account = await findAccountForConcept(empresaId, concept);

    if (!account && code) {
      const seededId = await ensureAccountSeeded(empresaId, code);
      if (seededId) {
        account = { id: seededId, source: `seed:${code}` };
      }
    }

    if (!account) {
      account = await resolveFallbackAccountForConcept(empresaId, concept);
    }

    if (!account) {
      throw new Error(`No se pudo resolver cuenta para concepto ${concept}`);
    }

    await prisma.cont_mapeo_cuentas.upsert({
      where: {
        empresa_id_concepto: {
          empresa_id: empresaId,
          concepto: concept,
        },
      },
      create: {
        empresa_id: empresaId,
        concepto: concept,
        cuenta_id: account.id,
      },
      update: {
        cuenta_id: account.id,
        updated_at: new Date(),
      },
    });

    applied.push({ concept, accountId: account.id, source: account.source });
  }

  return applied;
}

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

  if (!empresa) {
    return {
      empresaId,
      empresaNombre: empresaId,
      checks: [{ key: 'empresa', status: 'FAIL', detail: 'Empresa no encontrada' } as CheckItem],
      ok: false,
    };
  }

  const checks: CheckItem[] = [];

  if (!empresa.active) {
    checks.push({ key: 'empresa_activa', status: 'WARN', detail: 'Empresa inactiva' });
  } else {
    checks.push({ key: 'empresa_activa', status: 'OK', detail: 'Empresa activa' });
  }

  // Tesorería: regla PAGO_PROVEEDOR
  let tesRule = await prisma.tes_reglas.findFirst({
    where: { empresa_id: empresaId, tipo_operacion: 'PAGO_PROVEEDOR', medio_pago_id: null },
    select: { id: true, activo: true, cuenta_id: true, modo: true },
  });

  let tesCuentaOk = false;
  if (tesRule?.cuenta_id) {
    const cuenta = await prisma.tes_cuentas.findUnique({
      where: { id: tesRule.cuenta_id },
      select: { id: true, empresa_id: true, activo: true },
    });
    tesCuentaOk = !!cuenta && cuenta.activo && cuenta.empresa_id === empresaId;
  }

  if ((!tesRule || !tesRule.activo || !tesCuentaOk) && options.fixTesoreria) {
    await ensureTesoreriaRule(empresaId, options.modoTesoreria);
    tesRule = await prisma.tes_reglas.findFirst({
      where: { empresa_id: empresaId, tipo_operacion: 'PAGO_PROVEEDOR', medio_pago_id: null },
      select: { id: true, activo: true, cuenta_id: true, modo: true },
    });
    if (tesRule?.cuenta_id) {
      const cuenta = await prisma.tes_cuentas.findUnique({
        where: { id: tesRule.cuenta_id },
        select: { empresa_id: true, activo: true },
      });
      tesCuentaOk = !!cuenta && cuenta.activo && cuenta.empresa_id === empresaId;
    }
  }

  if (tesRule && tesRule.activo && tesCuentaOk) {
    checks.push({
      key: 'tesoreria_regla_pago_proveedor',
      status: 'OK',
      detail: `Regla activa (modo=${tesRule.modo})`,
    });
  } else {
    checks.push({
      key: 'tesoreria_regla_pago_proveedor',
      status: 'FAIL',
      detail: 'Falta regla PAGO_PROVEEDOR activa o cuenta inválida',
    });
  }

  // Contabilidad habilitada
  const contabilidadActiva = await prisma.suscripcion_modulos.findFirst({
    where: {
      activo: true,
      modulos: { codigo: 'CONTABILIDAD' },
      suscripciones: { empresa_id: empresaId },
    },
    select: { id: true },
  });

  if (!contabilidadActiva) {
    checks.push({
      key: 'contabilidad_modulo_activo',
      status: 'WARN',
      detail: 'Módulo CONTABILIDAD no activo en suscripción (validación contable omitida)',
    });
    checks.push({ key: 'contabilidad_periodo_abierto', status: 'SKIP', detail: 'No aplica sin módulo contable' });
    checks.push({ key: 'contabilidad_mapeos_compras', status: 'SKIP', detail: 'No aplica sin módulo contable' });
  } else {
    checks.push({ key: 'contabilidad_modulo_activo', status: 'OK', detail: 'Módulo CONTABILIDAD activo' });

    const hoy = new Date();
    const periodo = await prisma.cont_periodos.findFirst({
      where: {
        empresa_id: empresaId,
        estado: { in: ['ABIERTO', 'AJUSTE'] },
        fecha_inicio: { lte: hoy },
        fecha_fin: { gte: hoy },
      },
      select: { id: true, anio: true, mes: true, estado: true },
    });

    if (periodo) {
      checks.push({
        key: 'contabilidad_periodo_abierto',
        status: 'OK',
        detail: `Período ${periodo.anio}-${String(periodo.mes).padStart(2, '0')} (${periodo.estado})`,
      });
    } else {
      checks.push({
        key: 'contabilidad_periodo_abierto',
        status: 'FAIL',
        detail: `No existe período ABIERTO/AJUSTE para fecha ${toDateYMD(hoy)}`,
      });
    }

    const existingMapeos = await prisma.cont_mapeo_cuentas.findMany({
      where: {
        empresa_id: empresaId,
        concepto: { in: [...REQUIRED_MAPEOS_COMPRAS] },
      },
      select: { concepto: true },
    });
    const existingSet = new Set(existingMapeos.map((m) => m.concepto));
    const missingBefore = REQUIRED_MAPEOS_COMPRAS.filter((c) => !existingSet.has(c));

    if (missingBefore.length > 0 && options.fixMapeos) {
      await ensureRequiredMapeos(empresaId);
    }

    const finalMapeos = await prisma.cont_mapeo_cuentas.findMany({
      where: {
        empresa_id: empresaId,
        concepto: { in: [...REQUIRED_MAPEOS_COMPRAS] },
      },
      select: { concepto: true },
    });
    const finalSet = new Set(finalMapeos.map((m) => m.concepto));
    const missingAfter = REQUIRED_MAPEOS_COMPRAS.filter((c) => !finalSet.has(c));

    if (missingAfter.length === 0) {
      checks.push({
        key: 'contabilidad_mapeos_compras',
        status: 'OK',
        detail: 'Mapeos mínimos de Compras/Gastos/OP completos',
      });
    } else {
      checks.push({
        key: 'contabilidad_mapeos_compras',
        status: 'FAIL',
        detail: `Faltan mapeos: ${missingAfter.join(', ')}`,
      });
    }
  }

  const ok = checks.every((item) => item.status === 'OK' || item.status === 'SKIP');

  return {
    empresaId: empresa.id,
    empresaNombre: empresa.razon_social ?? empresa.ruc ?? empresa.id,
    checks,
    ok,
  };
}

function printReport(report: Array<{ empresaId: string; empresaNombre: string; checks: CheckItem[]; ok: boolean }>) {
  for (const r of report) {
    console.log('\n====================================================');
    console.log(`Empresa: ${r.empresaNombre}`);
    console.log(`ID:      ${r.empresaId}`);
    console.log('----------------------------------------------------');
    for (const c of r.checks) {
      const icon = c.status === 'OK' ? 'OK ' : c.status === 'FAIL' ? 'FAIL' : c.status === 'WARN' ? 'WARN' : 'SKIP';
      console.log(`${icon.padEnd(4)} ${c.key}: ${c.detail}`);
    }
    console.log(`Resultado empresa: ${r.ok ? 'GO' : 'NO-GO'}`);
  }

  const total = report.length;
  const okCount = report.filter((r) => r.ok).length;
  const noGoCount = total - okCount;

  console.log('\n====================================================');
  console.log(`Resumen global: ${okCount}/${total} GO, ${noGoCount}/${total} NO-GO`);
  console.log('====================================================');
}

async function main() {
  const options = parseArgs(process.argv.slice(2));
  const companyIds = await resolveCompanyIds(options);

  const results = [];
  for (const companyId of companyIds) {
    const result = await evaluateCompany(companyId, options);
    results.push(result);
  }

  printReport(results);

  const hasNoGo = results.some((r) => !r.ok);
  if (hasNoGo) process.exitCode = 2;
}

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