/**
 * Data-access layer for the AI Ticket Analyzer.
 *
 * Loads a ticket from the local Postgres mirror in the exact shape the
 * pre-processor consumes (header + ticket_notes + time_entries with creator
 * names/emails resolved by JOIN).
 *
 * After the ticket-notes sync fix landed (lib/services/entity-sync syncTicketNotes
 * + scheduled incremental sync), the local DB is the canonical source for an
 * analyzer run.
 */

import postgresClient from '@/lib/services/postgres-client';
import type { RawTicketBundle } from './preprocessor';

export class TicketNotFoundError extends Error {
  constructor(public ticketNumber: string) {
    super(`Ticket ${ticketNumber} not found in local mirror`);
    this.name = 'TicketNotFoundError';
  }
}

interface TicketRow {
  id: string;
  ticket_number: string;
  title: string;
  description: string | null;
  status: number;
  status_label: string | null;
  priority: number;
  priority_label: string | null;
  queue_id: number | null;
  queue_label: string | null;
  company_id: string;
  company_name: string | null;
  contact_id: string | null;
  contact_name: string | null;
  contact_email: string | null;
  assigned_resource_id: string | null;
  assignee_name: string | null;
  assignee_email: string | null;
  create_date: Date | string;
  last_activity_date: Date | string;
  resolved_date_time: Date | string | null;
  problem_ticket_id: string | null;
}

interface NoteRow {
  id: string;
  title: string | null;
  description: string;
  note_type: number | null;
  publish: number | null;
  creator_resource_id: string | null;
  creator_name: string | null;
  creator_email: string | null;
  creator_type: number | null;
  create_date_time: Date | string | null;
}

interface TimeEntryRow {
  id: string;
  resource_id: string;
  resource_name: string | null;
  resource_email: string | null;
  hours_worked: string;
  notes: string | null;
  internal_notes: string | null;
  entry_date: Date | string | null;
  start_date_time: Date | string | null;
  end_date_time: Date | string | null;
  type: number | null;
}

function toIso(d: Date | string | null): string | null {
  if (d === null || d === undefined) return null;
  if (d instanceof Date) return d.toISOString();
  return new Date(d).toISOString();
}

function toIsoRequired(d: Date | string): string {
  if (d instanceof Date) return d.toISOString();
  return new Date(d).toISOString();
}

export async function loadTicketBundle(
  ticketNumber: string
): Promise<RawTicketBundle> {
  const ticketRes = await postgresClient.query<TicketRow>(
    `
    SELECT
      t.id::text                          AS id,
      t.ticket_number                     AS ticket_number,
      t.title                             AS title,
      t.description                       AS description,
      t.status                            AS status,
      (SELECT label FROM statuses   WHERE value = t.status)   AS status_label,
      t.priority                          AS priority,
      (SELECT label FROM priorities WHERE value = t.priority) AS priority_label,
      t.queue_id                          AS queue_id,
      (SELECT label FROM queues     WHERE value = t.queue_id) AS queue_label,
      t.company_id::text                  AS company_id,
      (SELECT company_name FROM companies WHERE id = t.company_id) AS company_name,
      t.contact_id::text                  AS contact_id,
      (SELECT TRIM(first_name || ' ' || last_name) FROM contacts WHERE id = t.contact_id)
                                          AS contact_name,
      (SELECT email_address FROM contacts WHERE id = t.contact_id)
                                          AS contact_email,
      t.assigned_resource_id::text        AS assigned_resource_id,
      (SELECT TRIM(first_name || ' ' || last_name) FROM resources WHERE id = t.assigned_resource_id)
                                          AS assignee_name,
      (SELECT email FROM resources WHERE id = t.assigned_resource_id)
                                          AS assignee_email,
      t.create_date                       AS create_date,
      t.last_activity_date                AS last_activity_date,
      t.resolved_date_time                AS resolved_date_time,
      t.problem_ticket_id::text           AS problem_ticket_id
    FROM tickets t
    WHERE t.ticket_number = $1
      AND COALESCE(t.is_deleted, false) = false
    LIMIT 1
    `,
    [ticketNumber]
  );

  if (ticketRes.rowCount === 0) {
    throw new TicketNotFoundError(ticketNumber);
  }
  const t = ticketRes.rows[0];
  const ticketIdNum = Number(t.id);

  const notesRes = await postgresClient.query<NoteRow>(
    `
    SELECT
      n.id::text                          AS id,
      n.title                             AS title,
      n.description                       AS description,
      n.note_type                         AS note_type,
      n.publish                           AS publish,
      n.creator_resource_id::text         AS creator_resource_id,
      (SELECT TRIM(first_name || ' ' || last_name) FROM resources WHERE id = n.creator_resource_id)
                                          AS creator_name,
      (SELECT email FROM resources WHERE id = n.creator_resource_id)
                                          AS creator_email,
      n.creator_type                      AS creator_type,
      n.create_date_time                  AS create_date_time
    FROM ticket_notes n
    WHERE n.ticket_id = $1
      AND COALESCE(n.is_deleted, false) = false
    ORDER BY n.create_date_time
    `,
    [ticketIdNum]
  );

  const entriesRes = await postgresClient.query<TimeEntryRow>(
    `
    SELECT
      te.id::text                         AS id,
      te.resource_id::text                AS resource_id,
      (SELECT TRIM(first_name || ' ' || last_name) FROM resources WHERE id = te.resource_id)
                                          AS resource_name,
      (SELECT email FROM resources WHERE id = te.resource_id)
                                          AS resource_email,
      te.hours_worked::text               AS hours_worked,
      te.notes                            AS notes,
      te.internal_notes                   AS internal_notes,
      te.entry_date                       AS entry_date,
      te.start_date_time                  AS start_date_time,
      te.end_date_time                    AS end_date_time,
      te.type                             AS type
    FROM time_entries te
    WHERE te.ticket_id = $1
      AND COALESCE(te.is_deleted, false) = false
    ORDER BY te.entry_date, te.id
    `,
    [ticketIdNum]
  );

  return {
    ticket: {
      id: ticketIdNum,
      ticket_number: t.ticket_number,
      title: t.title,
      description: t.description,
      status: t.status,
      status_label: t.status_label,
      priority: t.priority,
      priority_label: t.priority_label,
      queue_id: t.queue_id,
      queue_label: t.queue_label,
      company_id: Number(t.company_id),
      company_name: t.company_name,
      contact_id: t.contact_id ? Number(t.contact_id) : null,
      contact_name: t.contact_name,
      contact_email: t.contact_email,
      assigned_resource_id: t.assigned_resource_id ? Number(t.assigned_resource_id) : null,
      assignee_name: t.assignee_name,
      assignee_email: t.assignee_email,
      create_date: toIsoRequired(t.create_date),
      last_activity_date: toIsoRequired(t.last_activity_date),
      resolved_date_time: toIso(t.resolved_date_time),
      problem_ticket_id: t.problem_ticket_id ? Number(t.problem_ticket_id) : null,
    },
    notes: notesRes.rows.map((n) => ({
      id: Number(n.id),
      title: n.title,
      description: n.description ?? '',
      note_type: n.note_type,
      publish: n.publish,
      creator_resource_id: n.creator_resource_id ? Number(n.creator_resource_id) : null,
      creator_name: n.creator_name,
      creator_email: n.creator_email,
      creator_type: n.creator_type,
      create_date_time: toIso(n.create_date_time),
    })),
    time_entries: entriesRes.rows.map((e) => ({
      id: Number(e.id),
      resource_id: Number(e.resource_id),
      resource_name: e.resource_name,
      resource_email: e.resource_email,
      hours_worked: Number(e.hours_worked),
      notes: e.notes,
      internal_notes: e.internal_notes,
      entry_date: toIso(e.entry_date),
      start_date_time: toIso(e.start_date_time),
      end_date_time: toIso(e.end_date_time),
      type: e.type,
    })),
  };
}
