import type { SidecarEvent } from '../protocol';
import type { Job, EnqueueParams } from '../../queue/types';
import { initEmailCron, stopEmailCron } from './email-cron';
import { initEmailIdle, stopEmailIdle } from './email-idle';
import { createSidecarConnector } from '../connect';

const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`;

// ── Queue access via WS ──

let reqCounter = 0;
const pendingQueue = new Map<string, { resolve: (v: unknown) => void; reject: (e: Error) => void; timer: Timer }>();

function nextQueueId(): string {
  return `eq_${Date.now()}_${++reqCounter}`;
}

function sendQueueCommand(cmd: Record<string, unknown>): Promise<unknown> {
  return new Promise((resolve, reject) => {
    const id = cmd.id as string;
    const timer = setTimeout(() => {
      pendingQueue.delete(id);
      reject(new Error(`Queue command ${cmd.type} timed out`));
    }, 30_000);
    pendingQueue.set(id, { resolve, reject, timer });
    connection.send(cmd as SidecarEvent);
  });
}

async function enqueueViaWs(params: EnqueueParams): Promise<Job> {
  const res = (await sendQueueCommand({ type: 'queue:enqueue', id: nextQueueId(), params })) as Record<string, unknown>;
  if (res.type === 'queue:enqueued') return res.job as Job;
  if (res.type === 'queue:error') throw new Error(res.error as string);
  throw new Error('Unexpected response');
}

async function listJobsViaWs(): Promise<Job[]> {
  const res = (await sendQueueCommand({ type: 'queue:list', id: nextQueueId() })) as Record<string, unknown>;
  if (res.type === 'queue:list') return res.jobs as Job[];
  throw new Error('Unexpected response');
}

// ── Command handlers ──

type ReplyFn = (msg: SidecarEvent) => void;

function handleCommand(cmd: Record<string, unknown>, reply: ReplyFn) {
  switch (cmd.type) {
    case 'ping':
      reply({ type: 'pong', id: cmd.id as string });
      break;

    default:
      // Check if this is a queue response (from API server responding to our queue commands)
      if (
        typeof cmd.type === 'string' &&
        cmd.type.startsWith('queue:') &&
        cmd.id &&
        pendingQueue.has(cmd.id as string)
      ) {
        const pending = pendingQueue.get(cmd.id as string)!;
        pendingQueue.delete(cmd.id as string);
        clearTimeout(pending.timer);
        pending.resolve(cmd);
        return;
      }

      reply({
        type: 'error',
        id: cmd.id as string,
        error: `Unknown command type: ${cmd.type}`,
      });
  }
}

// ── Connect to API server ──

const connection = createSidecarConnector({
  apiUrl: `${API_URL}/api/sidecar/register`,
  name: 'email',
  capabilities: ['email'],
  onCommand(cmd, reply) {
    handleCommand(cmd as Record<string, unknown>, reply as ReplyFn);
  },
  onConnected() {
    // Start email cron once connected (so queue commands can reach API server)
    initEmailCron();
    // Real-time push via IMAP IDLE; the cron above is the slow backstop. On new mail, tell the API
    // server so it can push an SSE event to that user's open /email page.
    initEmailIdle((userEmail) => connection.send({ type: 'email:new', userEmail }));
  },
});

// ── Graceful shutdown ──

function shutdown(signal: string) {
  console.log(`[email] ${signal} received, shutting down...`);
  stopEmailIdle();
  stopEmailCron();
  connection.destroy();
  process.exit(0);
}

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