// Mock all external dependencies before any imports
jest.mock("@/lib/services/quotation-notifier.service", () => ({
  notifyQuotationCustomer: jest
    .fn()
    .mockResolvedValue({ emailQueued: true, whatsappSent: true }),
}));
jest.mock("@/lib/db");
jest.mock("@/lib/services/quotation-portal.service", () => ({
  createPortalCheckoutSession: jest.fn(),
}));
jest.mock("@paralleldrive/cuid2", () => ({
  createId: jest.fn().mockReturnValue("token-abc"),
}));

import { prisma } from "@/lib/db";
import * as portalService from "@/lib/services/quotation-portal.service";
import * as notifierService from "@/lib/services/quotation-notifier.service";
import { dispatchQuotation } from "@/lib/services/quotations.service";

const mockQuotation = {
  id: "q1",
  userId: "u1",
  quotationNumber: "Q-20260226-0001",
  title: "Test Quote",
  total: 500,
  currency: "QAR",
  portalToken: null,
  dueDate: null,
  customer: {
    name: "Ahmed",
    email: "ahmed@example.com",
    phone: "+97450000001",
  },
  items: [
    {
      description: "Service",
      quantity: 1,
      unitPrice: 500,
      total: 500,
      sortOrder: 0,
    },
  ],
};

const mockUpdated = {
  ...mockQuotation,
  portalToken: "token-abc",
  status: "SENT",
  sentAt: new Date(),
};

beforeEach(() => {
  jest.clearAllMocks();
  (prisma.quotations.findFirst as jest.Mock).mockResolvedValue(mockQuotation);
  (prisma.quotations.update as jest.Mock).mockResolvedValue(mockUpdated);
  (portalService.createPortalCheckoutSession as jest.Mock).mockResolvedValue({
    success: true,
    checkoutUrl: "https://checkout.stripe.com/pay/cs_test_abc",
  });
});

describe("dispatchQuotation", () => {
  it("returns null when quotation not found", async () => {
    (prisma.quotations.findFirst as jest.Mock).mockResolvedValue(null);
    const result = await dispatchQuotation("u1", "q-missing");
    expect(result).toBeNull();
  });

  it("saves portal token and marks status SENT", async () => {
    await dispatchQuotation("u1", "q1");
    expect(prisma.quotations.update).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { id: "q1" },
        data: expect.objectContaining({
          portalToken: "token-abc",
          status: "SENT",
        }),
      }),
    );
  });

  it("reuses existing portal token if already set", async () => {
    (prisma.quotations.findFirst as jest.Mock).mockResolvedValue({
      ...mockQuotation,
      portalToken: "existing-token",
    });
    await dispatchQuotation("u1", "q1");
    expect(prisma.quotations.update).toHaveBeenCalledWith(
      expect.objectContaining({
        data: expect.objectContaining({ portalToken: "existing-token" }),
      }),
    );
  });

  it("calls createPortalCheckoutSession with the portal token", async () => {
    await dispatchQuotation("u1", "q1");
    expect(portalService.createPortalCheckoutSession).toHaveBeenCalledWith(
      "token-abc",
      expect.stringContaining("/quotation/token-abc"),
      expect.any(String),
    );
  });

  it("calls notifyQuotationCustomer with dispatch type", async () => {
    await dispatchQuotation("u1", "q1");
    expect(notifierService.notifyQuotationCustomer).toHaveBeenCalledWith(
      expect.objectContaining({ id: "q1" }),
      "dispatch",
    );
  });

  it("returns phoneStatus: sent always", async () => {
    const result = await dispatchQuotation("u1", "q1");
    expect(result?.phoneStatus).toBe("sent");
  });

  it("returns whatsappSent from notifier result", async () => {
    const result = await dispatchQuotation("u1", "q1");
    expect(result?.whatsappSent).toBe(true);
  });
});
