import { createDatabase } from '@pig/db';
import { piggyModelCatalogue } from './agent/models';
import { loadPiggyConfig } from './config';
import { PrimeOpenAIProvider } from './provider';
import { AgentTaskQueue } from './queue';
import { PiggyWorker } from './worker';
import { startPiggyChatServer } from './chat-server';

/**
 * Configuration faults are printed, not thrown.
 *
 * A missing PRIME_API_KEY is by far the most likely reason this process fails
 * to start, and a stack trace buries the one line that says so under twenty
 * frames of zod. The message from loadPiggyConfig already names every offending
 * variable, so print it and stop.
 */
function loadConfigOrExit(): ReturnType<typeof loadPiggyConfig> {
  try {
    return loadPiggyConfig();
  } catch (error) {
    console.error(error instanceof Error ? error.message : String(error));
    process.exit(1);
  }
}

const config = loadConfigOrExit();
const db = createDatabase({ url: config.DATABASE_URL, max: 4 });
const provider = new PrimeOpenAIProvider({
  apiKey: config.PIGGY_INFERENCE_API_KEY,
  baseUrl: config.PIGGY_INFERENCE_BASE,
  model: config.PIGGY_MODEL,
  maxTokens: config.PIGGY_MAX_TOKENS,
  // Retries are the operator's only warning that the endpoint is unwell; a
  // silent one makes a slow extraction look like a slow model.
  onRetry: ({ attempt, delayMs, reason }) =>
    console.warn(`[piggy] worker retry ${attempt} in ${delayMs}ms: ${reason}`),
});
// The chat server builds its own sessions, tools and model catalogue: every
// remaining option here has a working default, and passing one from this file
// would give a deployment two places to disagree about the same thing. What is
// left is the socket and who may talk to it.
const chatServer = startPiggyChatServer(db, {
  host: config.PIGGY_CHAT_HOST,
  port: config.PIGGY_CHAT_PORT,
  internalToken: config.PIGGY_INTERNAL_TOKEN,
  allowNonLoopback: config.PIGGY_CHAT_ALLOW_NON_LOOPBACK,
});
const queue = new AgentTaskQueue(db, config.workerId, config.PIGGY_LEASE_SECONDS);
const worker = new PiggyWorker(db, queue, provider, {
  pollIntervalMs: config.PIGGY_POLL_INTERVAL_MS,
  leaseSeconds: config.PIGGY_LEASE_SECONDS,
});
const shutdown = new AbortController();

process.on('SIGTERM', () => shutdown.abort());
process.on('SIGINT', () => shutdown.abort());

console.log(`[piggy] worker ${config.workerId} using ${provider.model}`);
// The agent line is separate from the worker line because they are separate
// budgets and separate models, and a deploy reading one and assuming the other
// is how a picker change gets blamed on the extraction queue.
console.log(
  `[piggy] agent mode ${config.PIGGY_AGENT_MODE}, default model ${config.PIGGY_AGENT_MODEL}, ` +
    `${piggyModelCatalogue().length} models in the picker, agent dir ${config.PIGGY_AGENT_DIR}`,
);
try {
  await worker.run(shutdown.signal);
} finally {
  chatServer.close();
}
console.log('[piggy] stopped');
