import { beforeEach, describe, expect, it, vi } from "vitest";
import { events, orders, paymentSubmissions, ticketTypes } from "../drizzle/schema";

const { sendTicketSms } = vi.hoisted(() => ({ sendTicketSms: vi.fn() }));
vi.mock("./mnotify", () => ({ sendTicketSms }));
vi.mock("./ticket-email-flow", () => ({
  getTicketEmailTrigger: (input: { totalMinor: number; orderStatus: string }) => input.totalMinor === 0 && input.orderStatus === "paid" ? "free_registration" : "none",
  deliverIssuedTicketEmail: vi.fn(async () => true),
}));

import { approveMtnPayment, createOrder, setDbForTests } from "./db";

function createFakeDb(overrides: { payment?: unknown; order?: unknown; event?: unknown; ticket?: unknown } = {}) {
  const orderUpdates: Array<Record<string, unknown>> = [];
  const db = {
    insert: () => ({ values: async () => [{ insertId: 501 }] }),
    select: () => ({
      from: (table: unknown) => ({
        where: () => ({
          limit: async () => table === paymentSubmissions ? [overrides.payment] : table === orders ? [overrides.order] : table === events ? [overrides.event] : table === ticketTypes ? [overrides.ticket] : [],
        }),
      }),
    }),
    update: (table: unknown) => ({
      set: (values: Record<string, unknown>) => ({
        where: async () => {
          if (table === orders) orderUpdates.push(values);
        },
      }),
    }),
    orderUpdates,
  };
  return db;
}

describe("end-to-end ticket SMS triggers", () => {
  beforeEach(() => {
    sendTicketSms.mockReset();
    setDbForTests(null);
  });

  it("sends SMS after free-ticket issuance and persists sent status", async () => {
    sendTicketSms.mockResolvedValue(true);
    const db = createFakeDb({
      event: { id: 7, title: "Free Night", startsAt: new Date(), location: "Accra" },
      ticket: { id: 8, name: "Free admission", quantitySold: 0 },
    });
    setDbForTests(db as never);

    const orderId = await createOrder({ eventId: 7, ticketTypeId: 8, quantity: 1, buyerName: "Ama", buyerEmail: "ama@example.com", buyerPhone: "0552250838", totalMinor: 0, status: "paid", buyerId: null });
    await new Promise((resolve) => setTimeout(resolve, 0));

    expect(orderId).toBe(501);
    expect(sendTicketSms).toHaveBeenCalledOnce();
    expect(db.orderUpdates).toContainEqual({ smsStatus: "sent" });
  });

  it("sends SMS after approved MTN release and persists failed status", async () => {
    sendTicketSms.mockResolvedValue(false);
    const db = createFakeDb({
      payment: { id: 12, orderId: 501, status: "pending" },
      order: { id: 501, eventId: 7, ticketTypeId: 8, quantity: 1, buyerName: "Kojo", buyerEmail: "kojo@example.com", buyerPhone: "0552250838", totalMinor: 1000 },
      event: { id: 7, organizerId: 9, title: "Paid Night", startsAt: new Date(), location: "Accra" },
      ticket: { id: 8, name: "General admission", quantitySold: 0 },
    });
    setDbForTests(db as never);

    await approveMtnPayment(12, 9, "user", "approved");
    await new Promise((resolve) => setTimeout(resolve, 0));

    expect(sendTicketSms).toHaveBeenCalledOnce();
    expect(db.orderUpdates).toContainEqual({ smsStatus: "failed" });
  });
});

