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 - data must be inline to avoid hoisting issues
vi.mock("./db", () => {
  const submissions = [
    {
      id: 1,
      name: "John Doe",
      email: "john@example.com",
      company: "Acme Corp",
      industry: "Ceramics",
      message: "Need ceramic raw materials.",
      status: "new",
      createdAt: new Date("2026-02-01"),
      updatedAt: new Date("2026-02-01"),
    },
    {
      id: 2,
      name: "Jane Smith",
      email: "jane@factory.com",
      company: "Glass Factory LLC",
      industry: "Glass & Aluminum",
      message: "Interested in AZS refractories.",
      status: "read",
      createdAt: new Date("2026-02-02"),
      updatedAt: new Date("2026-02-02"),
    },
    {
      id: 3,
      name: "Ahmed Ali",
      email: "ahmed@cement.ae",
      company: null,
      industry: "Cement Industry",
      message: "Magnesia bricks inquiry.",
      status: "replied",
      createdAt: new Date("2026-02-03"),
      updatedAt: new Date("2026-02-03"),
    },
  ];

  return {
    createContactSubmission: vi.fn().mockResolvedValue(undefined),
    upsertUser: vi.fn(),
    getUserByOpenId: vi.fn(),
    getContactSubmissions: vi.fn().mockResolvedValue(submissions),
    getContactSubmissionById: vi.fn().mockImplementation((id: number) => {
      const found = submissions.find((s) => s.id === id);
      return Promise.resolve(found || null);
    }),
    updateContactSubmissionStatus: vi.fn().mockResolvedValue({ success: true }),
    getContactSubmissionStats: vi.fn().mockResolvedValue({
      total: 3,
      new: 1,
      read: 1,
      replied: 1,
      archived: 0,
    }),
  };
});

function createAdminContext(): TrpcContext {
  return {
    user: {
      id: 1,
      openId: "admin-open-id",
      name: "Admin User",
      email: "admin@alzarfze.com",
      loginMethod: "oauth",
      role: "admin",
      createdAt: new Date(),
      updatedAt: new Date(),
      lastSignedIn: new Date(),
    },
    req: {
      protocol: "https",
      headers: {},
    } as TrpcContext["req"],
    res: {
      clearCookie: vi.fn(),
    } as unknown as TrpcContext["res"],
  };
}

function createUserContext(): TrpcContext {
  return {
    user: {
      id: 2,
      openId: "user-open-id",
      name: "Regular User",
      email: "user@example.com",
      loginMethod: "oauth",
      role: "user",
      createdAt: new Date(),
      updatedAt: new Date(),
      lastSignedIn: new Date(),
    },
    req: {
      protocol: "https",
      headers: {},
    } as TrpcContext["req"],
    res: {
      clearCookie: vi.fn(),
    } as unknown as TrpcContext["res"],
  };
}

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

describe("admin.submissions.list", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("returns submissions for admin users", async () => {
    const ctx = createAdminContext();
    const caller = appRouter.createCaller(ctx);

    const result = await caller.admin.submissions.list({});

    expect(Array.isArray(result)).toBe(true);
    expect(result.length).toBe(3);
    expect(result[0]).toHaveProperty("name");
    expect(result[0]).toHaveProperty("email");
    expect(result[0]).toHaveProperty("status");
  });

  it("rejects non-admin users", async () => {
    const ctx = createUserContext();
    const caller = appRouter.createCaller(ctx);

    await expect(caller.admin.submissions.list({})).rejects.toThrow();
  });

  it("rejects unauthenticated users", async () => {
    const ctx = createPublicContext();
    const caller = appRouter.createCaller(ctx);

    await expect(caller.admin.submissions.list({})).rejects.toThrow();
  });
});

describe("admin.submissions.getById", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("returns a specific submission for admin", async () => {
    const ctx = createAdminContext();
    const caller = appRouter.createCaller(ctx);

    const result = await caller.admin.submissions.getById({ id: 1 });

    expect(result).toHaveProperty("name", "John Doe");
    expect(result).toHaveProperty("email", "john@example.com");
  });

  it("throws when submission not found", async () => {
    const ctx = createAdminContext();
    const caller = appRouter.createCaller(ctx);

    await expect(caller.admin.submissions.getById({ id: 999 })).rejects.toThrow("Submission not found");
  });

  it("rejects non-admin users", async () => {
    const ctx = createUserContext();
    const caller = appRouter.createCaller(ctx);

    await expect(caller.admin.submissions.getById({ id: 1 })).rejects.toThrow();
  });
});

describe("admin.submissions.updateStatus", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("updates status for admin users", async () => {
    const { updateContactSubmissionStatus } = await import("./db");
    const ctx = createAdminContext();
    const caller = appRouter.createCaller(ctx);

    const result = await caller.admin.submissions.updateStatus({ id: 1, status: "read" });

    expect(result).toEqual({ success: true });
    expect(updateContactSubmissionStatus).toHaveBeenCalledWith(1, "read");
  });

  it("accepts all valid status values", async () => {
    const ctx = createAdminContext();
    const caller = appRouter.createCaller(ctx);

    for (const status of ["new", "read", "replied", "archived"] as const) {
      const result = await caller.admin.submissions.updateStatus({ id: 1, status });
      expect(result).toEqual({ success: true });
    }
  });

  it("rejects non-admin users", async () => {
    const ctx = createUserContext();
    const caller = appRouter.createCaller(ctx);

    await expect(caller.admin.submissions.updateStatus({ id: 1, status: "read" })).rejects.toThrow();
  });
});

describe("admin.submissions.stats", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("returns stats for admin users", async () => {
    const ctx = createAdminContext();
    const caller = appRouter.createCaller(ctx);

    const result = await caller.admin.submissions.stats();

    expect(result).toHaveProperty("total", 3);
    expect(result).toHaveProperty("new", 1);
    expect(result).toHaveProperty("read", 1);
    expect(result).toHaveProperty("replied", 1);
    expect(result).toHaveProperty("archived", 0);
  });

  it("rejects non-admin users", async () => {
    const ctx = createUserContext();
    const caller = appRouter.createCaller(ctx);

    await expect(caller.admin.submissions.stats()).rejects.toThrow();
  });
});
