import { describe, it, expect, beforeEach, vi } from "vitest";
import { POST } from "@/app/api/stripe/connect/onboard/route";
import { db } from "@/lib/db";
import { auth } from "@/lib/auth";
import { stripe } from "@/lib/stripe";

vi.mock("@/lib/db", () => ({
  db: {
    user: {
      findUnique: vi.fn(),
      update: vi.fn(),
    },
  },
}));

vi.mock("@/lib/auth", () => ({
  auth: vi.fn(),
}));

vi.mock("@/lib/stripe", () => ({
  stripe: {
    accounts: {
      create: vi.fn(),
    },
    accountLinks: {
      create: vi.fn(),
    },
  },
}));

vi.mock("@/lib/logger", () => ({
  logger: { info: vi.fn(), error: vi.fn() },
}));

describe("POST /api/stripe/connect/onboard", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("returns 401 when not authenticated", async () => {
    vi.mocked(auth).mockResolvedValue(null as never);

    const res = await POST();
    const data = await res.json();

    expect(res.status).toBe(401);
    expect(data.error).toBe("Unauthorized");
    expect(stripe!.accounts.create).not.toHaveBeenCalled();
  });

  it("creates new Connect account and returns account link url when user has no account", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user_1" } } as never);
    vi.mocked(db.user.findUnique).mockResolvedValue({
      id: "user_1",
      email: "op@example.com",
      stripeConnectAccountId: null,
    } as never);
    vi.mocked(stripe!.accounts.create).mockResolvedValue({
      id: "acct_new123",
    } as never);
    vi.mocked(db.user.update).mockResolvedValue({} as never);
    vi.mocked(stripe!.accountLinks.create).mockResolvedValue({
      url: "https://connect.stripe.com/setup/e/acct_new123/xyz",
    } as never);

    const res = await POST();
    const data = await res.json();

    expect(res.status).toBe(200);
    expect(data.url).toBe("https://connect.stripe.com/setup/e/acct_new123/xyz");
    expect(stripe!.accounts.create).toHaveBeenCalledWith({
      type: "express",
      email: "op@example.com",
      metadata: { userId: "user_1" },
    });
    expect(db.user.update).toHaveBeenCalledWith({
      where: { id: "user_1" },
      data: { stripeConnectAccountId: "acct_new123" },
    });
    expect(stripe!.accountLinks.create).toHaveBeenCalledWith(
      expect.objectContaining({
        account: "acct_new123",
        type: "account_onboarding",
      }),
    );
  });

  it("returns account link url for existing Connect account", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user_1" } } as never);
    vi.mocked(db.user.findUnique).mockResolvedValue({
      id: "user_1",
      email: "op@example.com",
      stripeConnectAccountId: "acct_existing",
    } as never);
    vi.mocked(stripe!.accountLinks.create).mockResolvedValue({
      url: "https://connect.stripe.com/setup/e/acct_existing/abc",
    } as never);

    const res = await POST();
    const data = await res.json();

    expect(res.status).toBe(200);
    expect(data.url).toBe("https://connect.stripe.com/setup/e/acct_existing/abc");
    expect(stripe!.accounts.create).not.toHaveBeenCalled();
    expect(stripe!.accountLinks.create).toHaveBeenCalledWith(
      expect.objectContaining({
        account: "acct_existing",
        type: "account_onboarding",
      }),
    );
  });

  it("returns 404 when user has no email", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user_1" } } as never);
    vi.mocked(db.user.findUnique).mockResolvedValue({
      id: "user_1",
      email: null,
      stripeConnectAccountId: null,
    } as never);

    const res = await POST();
    const data = await res.json();

    expect(res.status).toBe(404);
    expect(data.error).toContain("email");
    expect(stripe!.accounts.create).not.toHaveBeenCalled();
  });
});
