import { describe, expect, it, vi, beforeEach } from "vitest";
import { appRouter } from "./routers";
import type { TrpcContext } from "./_core/context";

// Mock the notification module
vi.mock("./_core/notification", () => ({
  notifyOwner: vi.fn().mockResolvedValue(true),
}));

// Mock the db module
vi.mock("./db", () => ({
  createContactSubmission: vi.fn().mockResolvedValue(undefined),
  upsertUser: vi.fn(),
  getUserByOpenId: vi.fn(),
}));

function createPublicContext(): TrpcContext {
  return {
    user: null,
    req: {
      protocol: "https",
      headers: {},
    } as TrpcContext["req"],
    res: {
      clearCookie: vi.fn(),
    } as unknown as TrpcContext["res"],
  };
}

// Helper to get a valid CAPTCHA from the server
async function getValidCaptcha(caller: ReturnType<typeof appRouter.createCaller>) {
  const captcha = await caller.captcha.generate();
  // We need to solve the captcha - parse the question
  const match = captcha.question.match(/^(\d+)\s*([+-])\s*(\d+)\s*=\s*\?$/);
  if (!match) throw new Error("Could not parse captcha question: " + captcha.question);
  const a = parseInt(match[1], 10);
  const op = match[2];
  const b = parseInt(match[3], 10);
  const answer = op === "+" ? a + b : a - b;
  return { captchaId: captcha.id, captchaAnswer: answer };
}

describe("captcha.generate", () => {
  it("returns a captcha with id and question", async () => {
    const ctx = createPublicContext();
    const caller = appRouter.createCaller(ctx);

    const captcha = await caller.captcha.generate();

    expect(captcha).toHaveProperty("id");
    expect(captcha).toHaveProperty("question");
    expect(typeof captcha.id).toBe("string");
    expect(captcha.id.length).toBeGreaterThan(0);
    expect(captcha.question).toMatch(/^\d+\s*[+-]\s*\d+\s*=\s*\?$/);
  });

  it("generates unique captcha IDs", async () => {
    const ctx = createPublicContext();
    const caller = appRouter.createCaller(ctx);

    const captcha1 = await caller.captcha.generate();
    const captcha2 = await caller.captcha.generate();

    expect(captcha1.id).not.toBe(captcha2.id);
  });
});

describe("contact.submit", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("accepts a valid contact form submission with correct CAPTCHA", async () => {
    const ctx = createPublicContext();
    const caller = appRouter.createCaller(ctx);
    const { captchaId, captchaAnswer } = await getValidCaptcha(caller);

    const result = await caller.contact.submit({
      name: "Test User",
      email: "test@example.com",
      company: "Test Company",
      industry: "Glass & Aluminum",
      message: "I need glass manufacturing equipment for my factory.",
      captchaId,
      captchaAnswer,
    });

    expect(result).toEqual({ success: true });
  });

  it("accepts submission without optional fields", async () => {
    const ctx = createPublicContext();
    const caller = appRouter.createCaller(ctx);
    const { captchaId, captchaAnswer } = await getValidCaptcha(caller);

    const result = await caller.contact.submit({
      name: "Test User",
      email: "test@example.com",
      message: "General inquiry about your services.",
      captchaId,
      captchaAnswer,
    });

    expect(result).toEqual({ success: true });
  });

  it("rejects submission with invalid email", async () => {
    const ctx = createPublicContext();
    const caller = appRouter.createCaller(ctx);
    const { captchaId, captchaAnswer } = await getValidCaptcha(caller);

    await expect(
      caller.contact.submit({
        name: "Test User",
        email: "not-an-email",
        message: "Test message",
        captchaId,
        captchaAnswer,
      })
    ).rejects.toThrow();
  });

  it("rejects submission with empty name", async () => {
    const ctx = createPublicContext();
    const caller = appRouter.createCaller(ctx);
    const { captchaId, captchaAnswer } = await getValidCaptcha(caller);

    await expect(
      caller.contact.submit({
        name: "",
        email: "test@example.com",
        message: "Test message",
        captchaId,
        captchaAnswer,
      })
    ).rejects.toThrow();
  });

  it("rejects submission with empty message", async () => {
    const ctx = createPublicContext();
    const caller = appRouter.createCaller(ctx);
    const { captchaId, captchaAnswer } = await getValidCaptcha(caller);

    await expect(
      caller.contact.submit({
        name: "Test User",
        email: "test@example.com",
        message: "",
        captchaId,
        captchaAnswer,
      })
    ).rejects.toThrow();
  });

  it("rejects submission with wrong CAPTCHA answer", async () => {
    const ctx = createPublicContext();
    const caller = appRouter.createCaller(ctx);
    const captcha = await caller.captcha.generate();

    await expect(
      caller.contact.submit({
        name: "Test User",
        email: "test@example.com",
        message: "Test message",
        captchaId: captcha.id,
        captchaAnswer: -99999, // Deliberately wrong
      })
    ).rejects.toThrow(/CAPTCHA/i);
  });

  it("rejects submission with invalid CAPTCHA ID", async () => {
    const ctx = createPublicContext();
    const caller = appRouter.createCaller(ctx);

    await expect(
      caller.contact.submit({
        name: "Test User",
        email: "test@example.com",
        message: "Test message",
        captchaId: "non-existent-id",
        captchaAnswer: 42,
      })
    ).rejects.toThrow(/CAPTCHA/i);
  });

  it("prevents CAPTCHA reuse (one-time use)", async () => {
    const ctx = createPublicContext();
    const caller = appRouter.createCaller(ctx);
    const { captchaId, captchaAnswer } = await getValidCaptcha(caller);

    // First submission should succeed
    const result = await caller.contact.submit({
      name: "Test User",
      email: "test@example.com",
      message: "First submission",
      captchaId,
      captchaAnswer,
    });
    expect(result).toEqual({ success: true });

    // Second submission with same CAPTCHA should fail
    await expect(
      caller.contact.submit({
        name: "Test User",
        email: "test@example.com",
        message: "Second submission with reused CAPTCHA",
        captchaId,
        captchaAnswer,
      })
    ).rejects.toThrow(/CAPTCHA/i);
  });

  it("calls createContactSubmission with correct data", async () => {
    const { createContactSubmission } = await import("./db");
    const ctx = createPublicContext();
    const caller = appRouter.createCaller(ctx);
    const { captchaId, captchaAnswer } = await getValidCaptcha(caller);

    await caller.contact.submit({
      name: "John Doe",
      email: "john@company.com",
      company: "Acme Corp",
      industry: "Cement Industry",
      message: "Need magnesia bricks for our cement plant.",
      captchaId,
      captchaAnswer,
    });

    expect(createContactSubmission).toHaveBeenCalledWith({
      name: "John Doe",
      email: "john@company.com",
      company: "Acme Corp",
      industry: "Cement Industry",
      message: "Need magnesia bricks for our cement plant.",
    });
  });

  it("calls notifyOwner after successful submission", async () => {
    const { notifyOwner } = await import("./_core/notification");
    const ctx = createPublicContext();
    const caller = appRouter.createCaller(ctx);
    const { captchaId, captchaAnswer } = await getValidCaptcha(caller);

    await caller.contact.submit({
      name: "Jane Smith",
      email: "jane@factory.com",
      company: "Glass Factory LLC",
      industry: "Glass & Aluminum",
      message: "Interested in AZS refractories.",
      captchaId,
      captchaAnswer,
    });

    expect(notifyOwner).toHaveBeenCalledWith(
      expect.objectContaining({
        title: expect.stringContaining("Jane Smith"),
        content: expect.stringContaining("jane@factory.com"),
      })
    );
  });
});
