import { isLsn, subtractLsn } from "./parseLsn";
import { sql } from "./quote";
import { runSql } from "./runSql";

export interface PhysicalReplica {
  pid: string;
  clientAddr: string;
  userName: string;
  applicationName: string;
  lagSec: number | null;
  feedbackAgoSec: number | null;
  masterLsn: string | null;
  replicaLsn: string | null;
  gap: number | null;
}

/**
 * Returns detailed information about physical replicas of a DSN. Works for
 * vanilla PostgreSQL only: in Aurora, it returns an empty array.
 */
export async function getPhysicalReplicas({
  dsn,
  comment,
}: {
  dsn: string;
  comment?: string;
}): Promise<PhysicalReplica[]> {
  const replicas = await runSql(
    dsn,
    sql`
      SELECT
        pid,
        client_addr,
        usename,
        application_name,
        EXTRACT(EPOCH FROM replay_lag),
        EXTRACT(EPOCH FROM (now() - reply_time)),
        sent_lsn,
        replay_lsn
      FROM pg_stat_replication
      WHERE
        application_name = 'walreceiver'
        OR pid IN (SELECT active_pid FROM pg_replication_slots WHERE slot_type = 'physical')
    `,
    comment,
  );
  return replicas.map(
    ([
      pid,
      clientAddr,
      userName,
      applicationName,
      lagSec,
      feedbackAgoSec,
      masterLsn,
      replicaLsn,
    ]) => ({
      pid,
      clientAddr,
      userName,
      applicationName,
      // "If the standby server has entirely caught up with the sending server
      // and there is no more WAL activity, the most recently measured lag times
      // will continue to be displayed for a short time and then show NULL."
      lagSec: lagSec ? Math.round(parseFloat(lagSec)) : 0,
      feedbackAgoSec:
        feedbackAgoSec !== "" ? Math.round(parseFloat(feedbackAgoSec)) : null,
      masterLsn: isLsn(masterLsn) ? masterLsn : null,
      replicaLsn: isLsn(replicaLsn) ? replicaLsn : null,
      gap: subtractLsn(masterLsn, replicaLsn),
    }),
  );
}
