/**
 * diff-ticket-680282.ts
 *
 * One-shot investigation for the AI Ticket Analyzer fixture build.
 *
 * Pulls TicketNotes and TimeEntries for ticket 680282 (T20260424.0045) from
 * BOTH Autotask (live REST) and the local Postgres mirror, then prints a diff.
 *
 * Why: the spec for the analyzer feature asserts a "I'll take it from here"
 * ticket note from the requestor that is not present in our synced ticket_notes
 * table. This script answers the question "is the note missing from Autotask
 * too, or is our sync incomplete?".
 *
 * Output:
 *   - dev/analyzer-fixture/T20260424.0045.live.json   (live Autotask payload)
 *   - dev/analyzer-fixture/T20260424.0045.db.json     (local DB payload)
 *   - dev/analyzer-fixture/T20260424.0045.diff.json   (id-level diff summary)
 *
 * Usage:
 *   npx tsx scripts/diff-ticket-680282.ts
 */

import { config } from 'dotenv';
import { resolve } from 'path';
import { mkdirSync, writeFileSync } from 'fs';
import { Client } from 'pg';

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

const TICKET_ID = 680282;
const TICKET_NUMBER = 'T20260424.0045';

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 queryAll<T>(entity: string, filter: object[]): Promise<T[]> {
  const all: T[] = [];
  let nextUrl: string | null = null;
  const requestBody = JSON.stringify({ MaxRecords: 500, filter });

  while (true) {
    const url = nextUrl ?? `${API_BASE}/${entity}/query`;
    const res = await fetch(url, {
      method: 'POST',
      headers: authHeaders(),
      body: requestBody,
    });
    if (!res.ok) {
      throw new Error(`${entity} query failed: ${res.status} ${await res.text()}`);
    }
    const payload = (await res.json()) as {
      items: T[];
      pageDetails?: { nextPageUrl?: string };
    };
    all.push(...(payload.items || []));
    if (payload.pageDetails?.nextPageUrl) {
      nextUrl = payload.pageDetails.nextPageUrl;
    } else {
      break;
    }
  }
  return all;
}

interface ATTicketNote {
  id: number;
  ticketID: number;
  title?: string;
  description?: string;
  noteType?: number;
  publish?: number;
  creatorResourceID?: number | null;
  creatorType?: number | null;
  contactID?: number | null;
  lastActivityDate?: string;
  createDateTime?: string;
}

interface ATTimeEntry {
  id: number;
  ticketID: number;
  resourceID: number;
  hoursWorked: number;
  summaryNotes?: string;
  internalNotes?: string;
  dateWorked?: string;
  startDateTime?: string;
  endDateTime?: string;
  type?: number;
}

interface ATTicket {
  id: number;
  ticketNumber: string;
  title: string;
  status: number;
  priority: number;
  queueID: number | null;
  companyID: number;
  contactID: number | null;
  assignedResourceID: number | null;
  description?: string;
  createDate: string;
  lastActivityDate: string;
  resolvedDateTime?: string;
}

async function getDbConnection(): Promise<Client> {
  const client = new Client({
    host: process.env.POSTGRES_HOST === 'postgres' ? 'localhost' : (process.env.POSTGRES_HOST || 'localhost'),
    port: parseInt(process.env.POSTGRES_PORT || '5432'),
    database: process.env.POSTGRES_DB!,
    user: process.env.POSTGRES_USER!,
    password: process.env.POSTGRES_PASSWORD!,
  });
  await client.connect();
  return client;
}

async function main() {
  const outDir = resolve(__dirname, '../dev/analyzer-fixture');
  mkdirSync(outDir, { recursive: true });

  console.log(`\n== Ticket ${TICKET_NUMBER} (id=${TICKET_ID}) ==`);

  // ── Live Autotask fetch ────────────────────────────────────────────────────
  console.log('\n[live] fetching Autotask Tickets/TicketNotes/TimeEntries...');

  const liveTickets = await queryAll<ATTicket>('Tickets', [
    { op: 'eq', field: 'id', value: TICKET_ID },
  ]);
  const liveNotes = await queryAll<ATTicketNote>('TicketNotes', [
    { op: 'eq', field: 'ticketID', value: TICKET_ID },
  ]);
  const liveEntries = await queryAll<ATTimeEntry>('TimeEntries', [
    { op: 'eq', field: 'ticketID', value: TICKET_ID },
  ]);

  console.log(`  live: ticket=${liveTickets.length} notes=${liveNotes.length} time_entries=${liveEntries.length}`);

  // ── DB fetch ───────────────────────────────────────────────────────────────
  console.log('\n[db] querying local Postgres...');
  const db = await getDbConnection();
  try {
    const dbTicket = await db.query(
      `SELECT id, ticket_number, title, status, priority, queue_id, company_id,
              contact_id, assigned_resource_id, description, create_date,
              last_activity_date, resolved_date_time
         FROM tickets WHERE id = $1`,
      [TICKET_ID]
    );
    const dbNotes = await db.query(
      `SELECT id, ticket_id, title, description, note_type, publish,
              creator_resource_id, creator_type, last_activity_date,
              create_date_time, is_deleted
         FROM ticket_notes WHERE ticket_id = $1 ORDER BY create_date_time`,
      [TICKET_ID]
    );
    const dbEntries = await db.query(
      `SELECT id, ticket_id, resource_id, hours_worked, notes, internal_notes,
              entry_date, start_date_time, end_date_time, type, is_deleted
         FROM time_entries WHERE ticket_id = $1 ORDER BY entry_date, id`,
      [TICKET_ID]
    );

    console.log(`  db: ticket=${dbTicket.rowCount} notes=${dbNotes.rowCount} time_entries=${dbEntries.rowCount}`);

    // ── Write raw payloads ────────────────────────────────────────────────────
    writeFileSync(
      `${outDir}/T20260424.0045.live.json`,
      JSON.stringify(
        { ticket: liveTickets[0] ?? null, notes: liveNotes, time_entries: liveEntries },
        null,
        2
      )
    );
    writeFileSync(
      `${outDir}/T20260424.0045.db.json`,
      JSON.stringify(
        {
          ticket: dbTicket.rows[0] ?? null,
          notes: dbNotes.rows,
          time_entries: dbEntries.rows,
        },
        null,
        2
      )
    );

    // ── Diff ──────────────────────────────────────────────────────────────────
    const liveNoteIds = new Set(liveNotes.map((n) => n.id));
    const dbNoteIds = new Set(dbNotes.rows.map((r) => Number(r.id)));
    const liveEntryIds = new Set(liveEntries.map((e) => e.id));
    const dbEntryIds = new Set(dbEntries.rows.map((r) => Number(r.id)));

    const notesOnlyInLive = [...liveNoteIds].filter((id) => !dbNoteIds.has(id));
    const notesOnlyInDb = [...dbNoteIds].filter((id) => !liveNoteIds.has(id));
    const entriesOnlyInLive = [...liveEntryIds].filter((id) => !dbEntryIds.has(id));
    const entriesOnlyInDb = [...dbEntryIds].filter((id) => !liveEntryIds.has(id));

    const diff = {
      ticket_id: TICKET_ID,
      ticket_number: TICKET_NUMBER,
      counts: {
        live_notes: liveNotes.length,
        db_notes: dbNotes.rowCount,
        live_time_entries: liveEntries.length,
        db_time_entries: dbEntries.rowCount,
      },
      notes_only_in_live: notesOnlyInLive.map((id) => {
        const n = liveNotes.find((x) => x.id === id)!;
        return {
          id: n.id,
          title: n.title,
          noteType: n.noteType,
          publish: n.publish,
          creatorResourceID: n.creatorResourceID,
          creatorType: n.creatorType,
          createDateTime: n.createDateTime,
          description_preview: (n.description || '').slice(0, 240),
        };
      }),
      notes_only_in_db: notesOnlyInDb,
      entries_only_in_live: entriesOnlyInLive.map((id) => {
        const e = liveEntries.find((x) => x.id === id)!;
        return {
          id: e.id,
          dateWorked: e.dateWorked,
          hoursWorked: e.hoursWorked,
          summary_preview: (e.summaryNotes || '').slice(0, 240),
        };
      }),
      entries_only_in_db: entriesOnlyInDb,
    };

    writeFileSync(`${outDir}/T20260424.0045.diff.json`, JSON.stringify(diff, null, 2));

    console.log('\n== DIFF ==');
    console.log(JSON.stringify(diff, null, 2));
    console.log(`\nWrote: ${outDir}/T20260424.0045.{live,db,diff}.json`);
  } finally {
    await db.end();
  }
}

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