/**
 * backfill-ticket-notes-gap.ts
 *
 * Reconciles the ticket_notes table for the 2026-04-24 → 2026-04-26 webhook
 * outage. Pulls every Autotask TicketNote whose lastActivityDate falls inside
 * the window (with a small overlap on each side) and upserts via
 * postgresClient.bulkUpsert. Idempotent — rows already in the DB are refreshed,
 * not duplicated.
 *
 * Usage:
 *   npx tsx scripts/backfill-ticket-notes-gap.ts                # default window
 *   npx tsx scripts/backfill-ticket-notes-gap.ts 2026-04-23 2026-04-28
 */

import { config } from 'dotenv';
import { resolve } from 'path';

config({ path: resolve(__dirname, '../.env.local') });

// Force the singleton postgres-client to use localhost rather than the docker
// hostname `postgres`, which only resolves inside the compose network.
if (process.env.POSTGRES_HOST === 'postgres') {
  process.env.POSTGRES_HOST = 'localhost';
}

import postgresClient from '../lib/services/postgres-client';
import { mapAutotaskBatch } from '../lib/utils/entity-mapper';
import { EntityType } from '../lib/types/sync';

const API_BASE = process.env.AUTOTASK_API_URL!;
const USERNAME = process.env.AUTOTASK_USERNAME!;
const SECRET = process.env.AUTOTASK_SECRET!;
const INT_CODE = process.env.AUTOTASK_API_INTEGRATION_CODE!;

if (!API_BASE || !USERNAME || !SECRET || !INT_CODE) {
  console.error('Missing Autotask credentials in env');
  process.exit(1);
}

function authHeaders(): Record<string, string> {
  return {
    Username: USERNAME,
    Secret: SECRET,
    APIIntegrationcode: INT_CODE,
    'Content-Type': 'application/json',
    Accept: 'application/json',
  };
}

async function fetchAllNotesInWindow(startIso: string, endIso: string) {
  const all: any[] = [];
  const filter = [
    { field: 'lastActivityDate', op: 'gte', value: startIso },
    { field: 'lastActivityDate', op: 'lte', value: endIso },
  ];

  let nextUrl: string | null = null;
  let page = 0;
  while (true) {
    const url = nextUrl ?? `${API_BASE}/TicketNotes/query`;
    const res = await fetch(url, {
      method: 'POST',
      headers: authHeaders(),
      body: JSON.stringify({ MaxRecords: 500, filter }),
    });
    if (!res.ok) {
      throw new Error(`TicketNotes query failed: ${res.status} ${await res.text()}`);
    }
    const payload = (await res.json()) as {
      items?: any[];
      pageDetails?: { nextPageUrl?: string };
    };
    const items = payload.items ?? [];
    all.push(...items);
    page++;
    console.log(`  page ${page}: +${items.length} (running total ${all.length})`);
    if (payload.pageDetails?.nextPageUrl) {
      nextUrl = payload.pageDetails.nextPageUrl;
    } else {
      break;
    }
  }
  return all;
}

async function main() {
  const [, , startArg, endArg] = process.argv;
  const startIso = startArg
    ? new Date(startArg).toISOString()
    : '2026-04-23T00:00:00Z';
  const endIso = endArg
    ? new Date(endArg).toISOString()
    : '2026-04-27T13:00:00Z';

  console.log(`[backfill] window: ${startIso} -> ${endIso}`);
  const before = await postgresClient.query<{ count: string }>(
    `SELECT COUNT(*) AS count FROM ticket_notes WHERE last_activity_date >= $1 AND last_activity_date <= $2`,
    [startIso, endIso]
  );
  console.log(`[backfill] rows in DB before: ${before.rows[0].count}`);

  console.log('[backfill] fetching from Autotask...');
  const liveNotes = await fetchAllNotesInWindow(startIso, endIso);
  console.log(`[backfill] fetched ${liveNotes.length} notes from Autotask`);

  if (liveNotes.length === 0) {
    console.log('[backfill] nothing to upsert');
    process.exit(0);
  }

  // Map via the project's entity-mapper so the row shape matches what the
  // webhook handler / sync would produce.
  const mapped = mapAutotaskBatch(EntityType.TICKET_NOTES, liveNotes);
  console.log(`[backfill] mapped ${mapped.length} records`);

  // Sample one record so the sanity-check is visible in the script log.
  console.log('[backfill] sample mapped row:', JSON.stringify(mapped[0], null, 2));

  // Bulk upsert in chunks of 200 to stay well below the parameter limit
  // (~10 columns × 200 = 2,000 params per statement).
  const CHUNK = 200;
  let totalUpserted = 0;
  for (let i = 0; i < mapped.length; i += CHUNK) {
    const chunk = mapped.slice(i, i + CHUNK);
    const n = await postgresClient.bulkUpsert('ticket_notes', chunk, ['id']);
    totalUpserted += n;
    console.log(`[backfill] upserted ${i + chunk.length}/${mapped.length}`);
  }

  const after = await postgresClient.query<{ count: string }>(
    `SELECT COUNT(*) AS count FROM ticket_notes WHERE last_activity_date >= $1 AND last_activity_date <= $2`,
    [startIso, endIso]
  );
  console.log(`[backfill] rows in DB after:  ${after.rows[0].count}`);
  console.log(`[backfill] upsert ops:        ${totalUpserted}`);
  console.log(
    `[backfill] net new rows:      ${parseInt(after.rows[0].count) - parseInt(before.rows[0].count)}`
  );

  process.exit(0);
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});
