import 'dotenv/config';
import * as joi from 'joi';

const ESFUERZOS = ['low', 'medium', 'high', 'xhigh', 'max'] as const;
export type Esfuerzo = (typeof ESFUERZOS)[number];

interface EnvVars {
  NODE_ENV: string;
  PORT: number;
  CORS_ORIGINS: string;
  ERP_DATABASE_URL: string;
  ERP_DB_POOL_MAX: number;
  SOPORTE_DATABASE_URL: string;
  JWT_SECRET: string;
  SOPORTE_ALCANCE_SECRETO: string;
  HOLDING_EMPRESA_ID: string;
  SOPORTE_TELEFONO_CONTACTO: string;
  HORAS_PARA_TRABADO: number;
  ANTHROPIC_API_KEY?: string;
  EXTRACTOR_MODEL: string;
  EXTRACTOR_EFFORT: Esfuerzo;
  EXTRACTOR_MAX_TOKENS: number;
  REDACTOR_MODEL: string;
  REDACTOR_EFFORT: Esfuerzo;
  REDACTOR_MAX_TOKENS: number;
  CONSULTOR_MODEL: string;
  CONSULTOR_EFFORT: Esfuerzo;
  CONSULTOR_MAX_TOKENS: number;
  MANUAL_DIR: string;
  MANUAL_MODEL: string;
  CAPTURAS_MODEL: string;
  YOUTRACK_URL: string | undefined;
  YOUTRACK_TOKEN: string | undefined;
  YOUTRACK_PROYECTO: string;
}

const envsSchema = joi
  .object({
    NODE_ENV: joi.string().valid('development', 'production', 'test').default('development'),
    PORT: joi.number().default(3020),
    // Orígenes del frontend del ERP, separados por coma.
    CORS_ORIGINS: joi.string().default('http://localhost:5173'),
    // Rol soporte_ia: solo lectura, solo esquema soporte_ro (db/roles.sql).
    ERP_DATABASE_URL: joi.string().required(),
    ERP_DB_POOL_MAX: joi.number().integer().min(1).max(10).default(5),
    // Base propia del agente (tickets, mensajes, trazas): rol soporte_app.
    SOPORTE_DATABASE_URL: joi.string().required(),
    // El MISMO valor que JWT_SECRET del backend del ERP (decisión D6 del plan).
    JWT_SECRET: joi.string().min(16).required(),
    // Clave de la firma del alcance: el mismo valor que guarda db/roles.sql en soporte_ro_privado.clave.
    SOPORTE_ALCANCE_SECRETO: joi.string().min(32).required(),
    // El mismo valor que HOLDING_EMPRESA_ID del ERP: identifica a Novasis.
    HOLDING_EMPRESA_ID: joi.string().guid().required(),
    SOPORTE_TELEFONO_CONTACTO: joi.string().default('+595 971 202885'),
    // A partir de cuántas horas un documento pendiente en SIFEN se considera trabado.
    HORAS_PARA_TRABADO: joi.number().min(0.5).default(2),
    // Sin key el servicio arranca igual, en modo degradado: el chat muestra el contacto.
    ANTHROPIC_API_KEY: joi.string().allow('').optional(),
    // Modelo por etapa (§6.5). Se baja de nivel solo pasando el golden set.
    // Extractor en Haiku 5.5 desde el 24/10/2026: 40/40 igual que Opus 5.5 en
    // evals/extractor-casos.json (3 corridas), p50 1,3 s contra 2,0 s y ~40 veces más barato.
    EXTRACTOR_MODEL: joi.string().default('claude-haiku-5-5'),
    EXTRACTOR_EFFORT: joi
      .string()
      .valid(...ESFUERZOS)
      .default('low'),
    EXTRACTOR_MAX_TOKENS: joi.number().integer().min(256).default(1024),
    REDACTOR_MODEL: joi.string().default('claude-opus-5-5'),
    // low: con medium el primer texto tardaba 4–9 s y el modelo repetía consultas que ya
    // tenía; con low, 1,3–3,9 s y la misma calidad en las pruebas del 24/10/2026.
    REDACTOR_EFFORT: joi
      .string()
      .valid(...ESFUERZOS)
      .default('low'),
    // Techo anti-runaway, no dial de ahorro.
    REDACTOR_MAX_TOKENS: joi.number().integer().min(1024).default(8000),
    // Consultor (§6.7): escribe el SQL de las preguntas de negocio sobre las vistas.
    // Haiku 5.5 desde el 24/10/2026: en 24 preguntas (scripts/probar-consultor.js, dos empresas)
    // dio las mismas cifras que Opus 5.5 y Sonnet 5.5, en ~2,2 s contra ~4 s y ~25 veces más barato.
    // La seguridad no depende del modelo: la base filtra por el alcance firmado.
    CONSULTOR_MODEL: joi.string().default('claude-haiku-5-5'),
    CONSULTOR_EFFORT: joi
      .string()
      .valid(...ESFUERZOS)
      .default('low'),
    CONSULTOR_MAX_TOKENS: joi.number().integer().min(1024).default(4096),
    // Manual del sistema (pnpm manual:construir): índice, foto del código y capturas.
    MANUAL_DIR: joi.string().default('manual'),
    // Enrutador del manual: elige las pantallas para una pregunta de uso (índice cacheado).
    MANUAL_MODEL: joi.string().default('claude-haiku-5-5'),
    // Lectura de las capturas que adjunta el cliente: las transcribe a texto (§6.10).
    CAPTURAS_MODEL: joi.string().default('claude-haiku-5-5'),
    // Incidencias en YouTrack (§6.8): sin URL o token, la bandeja espera (modo degradado).
    YOUTRACK_URL: joi.string().uri().allow('').optional(),
    YOUTRACK_TOKEN: joi.string().allow('').optional(),
    YOUTRACK_PROYECTO: joi.string().default('NE'),
  })
  .unknown(true);

const { error, value } = envsSchema.validate(process.env);
if (error) {
  throw new Error(`Configuración inválida: ${error.message}`);
}
const envVars = value as EnvVars;

export const envs = {
  nodeEnv: envVars.NODE_ENV,
  port: envVars.PORT,
  corsOrigins: envVars.CORS_ORIGINS.split(',')
    .map((o) => o.trim())
    .filter(Boolean),
  erpDatabaseUrl: envVars.ERP_DATABASE_URL,
  erpDbPoolMax: envVars.ERP_DB_POOL_MAX,
  soporteDatabaseUrl: envVars.SOPORTE_DATABASE_URL,
  jwtSecret: envVars.JWT_SECRET,
  alcanceSecreto: envVars.SOPORTE_ALCANCE_SECRETO,
  holdingEmpresaId: envVars.HOLDING_EMPRESA_ID,
  telefonoContacto: envVars.SOPORTE_TELEFONO_CONTACTO,
  horasParaTrabado: envVars.HORAS_PARA_TRABADO,
  anthropicApiKey: envVars.ANTHROPIC_API_KEY || null,
  extractor: {
    model: envVars.EXTRACTOR_MODEL,
    effort: envVars.EXTRACTOR_EFFORT,
    maxTokens: envVars.EXTRACTOR_MAX_TOKENS,
  },
  redactor: {
    model: envVars.REDACTOR_MODEL,
    effort: envVars.REDACTOR_EFFORT,
    maxTokens: envVars.REDACTOR_MAX_TOKENS,
  },
  manualDir: envVars.MANUAL_DIR,
  manual: { model: envVars.MANUAL_MODEL },
  capturas: { model: envVars.CAPTURAS_MODEL },
  youtrack: {
    url: (envVars.YOUTRACK_URL || '').replace(/\/$/, '') || null,
    token: envVars.YOUTRACK_TOKEN || null,
    proyecto: envVars.YOUTRACK_PROYECTO,
  },
  consultor: {
    model: envVars.CONSULTOR_MODEL,
    effort: envVars.CONSULTOR_EFFORT,
    maxTokens: envVars.CONSULTOR_MAX_TOKENS,
  },
};
