/**
 * Polls the stats of every live service over HTTP and prints one line per service.
 *
 * Use polling when you only need a refresh every few seconds, when you cannot keep a websocket open, or when a
 * back end collects the metrics for other systems. For a wall board that must react to every call use the
 * websocket (liveStats.ts): polling cannot see a call that starts and ends between two requests, and the stats are
 * refreshed by the platform at most every 5 seconds anyway.
 *
 *   npm install @wildix/wda-stream-client
 *   WILDIX_TOKEN=<access token> npx tsx pollStats.ts
 *
 * Optional environment: WILDIX_ENV (default: production), WILDIX_COMPANY (only for service tokens that act for a
 * company), PERIOD_MINUTES (15, 30, 45 or 60; default 15) or PERIOD_START=today (the day so far), SERVICE_LEVEL_SECONDS (5, 10, 15, 20,
 * 25 or 30; default 20), POLL_SECONDS (default 10).
 */
import {GetServiceStatsCommand, ListServicesCommand, WdaStreamClient} from '@wildix/wda-stream-client';

import {
  abandonRate,
  averageTalkMs,
  averageWaitMs,
  formatDuration,
  formatPercent,
  longestWaitMs,
  maxWaitIncludingLiveMs,
  serviceLevel,
} from './deriveMetrics';

const company = process.env.WILDIX_COMPANY;
const periodMinutes = Number(process.env.PERIOD_MINUTES ?? 15);
// PERIOD_START=today asks for the stats since midnight of this machine's time zone instead of a rolling lookback.
// The day start must be on a UTC hour, so a time zone with a half-hour offset falls back to UTC.
const localOffsetMinutes = -new Date().getTimezoneOffset();
const periodWindow =
  process.env.PERIOD_START === 'today'
    ? {periodStart: 'today' as const, utcOffsetMinutes: localOffsetMinutes % 60 === 0 ? localOffsetMinutes : 0}
    : {periodMinutes};
const serviceLevelSeconds = Number(process.env.SERVICE_LEVEL_SECONDS ?? 20);
const pollSeconds = Number(process.env.POLL_SECONDS ?? 10);
const MAX_IDS_PER_REQUEST = 20;

const client = new WdaStreamClient({
  env: process.env.WILDIX_ENV,
  token: () => Promise.resolve({token: process.env.WILDIX_TOKEN!}),
});

async function pollOnce(): Promise<void> {
  // The services that are live right now. A service that has ended no longer appears here.
  const {services} = await client.send(new ListServicesCommand({company}));
  const names = new Map(services.map((service) => [service.id, service.name]));
  const ids = services.map((service) => service.id);

  for (let from = 0; from < ids.length; from += MAX_IDS_PER_REQUEST) {
    // Sequential on purpose: one request at a time keeps the load on the API low.
    // eslint-disable-next-line no-await-in-loop
    const {services: withStats} = await client.send(
      new GetServiceStatsCommand({
        company,
        ids: ids.slice(from, from + MAX_IDS_PER_REQUEST),
        ...periodWindow,
        serviceLevelSeconds,
      }),
    );
    const now = Date.now();

    for (const {id, stats} of withStats) {
      console.log(
        [
          `${names.get(id) ?? id}`.padEnd(24),
          `SL ${formatPercent(serviceLevel(stats))}`.padEnd(10),
          `abandon ${formatPercent(abandonRate(stats))}`.padEnd(15),
          `avg wait ${formatDuration(averageWaitMs(stats))}`.padEnd(16),
          `avg talk ${formatDuration(averageTalkMs(stats))}`.padEnd(16),
          `max wait ${formatDuration(maxWaitIncludingLiveMs(stats, now))}`.padEnd(16),
          `waiting ${stats.callsWaiting ?? '-'} (longest ${formatDuration(longestWaitMs(stats, now))})`.padEnd(30),
          `talking ${stats.callsActive ?? '-'}`,
        ].join(' '),
      );
    }
  }
}

async function main(): Promise<void> {
  for (;;) {
    try {
      // One poll at a time, then wait: the loop is meant to be sequential.
      // eslint-disable-next-line no-await-in-loop
      await pollOnce();
    } catch (error) {
      // Keep polling: a failed request is usually temporary (network, token about to expire).
      console.error('poll failed:', error instanceof Error ? error.message : error);
    }

    console.log('');
    // eslint-disable-next-line no-await-in-loop
    await new Promise((resolve) => setTimeout(resolve, pollSeconds * 1000));
  }
}

void main();
