import { describe, it, expect, vi, beforeEach } from "vitest";
import { TRPCError } from "@trpc/server";

const mockSelectFromWhereLimit = vi.fn();
const mockSelectFromWhereOrderByLimitOffset = vi.fn();
const mockInsertValuesReturning = vi.fn();
const mockUpdateSetWhereReturning = vi.fn();
const mockSubFindFirst = vi.fn();
const mockWhereLimit = vi.fn();
const mockCountSelectFromWhere = vi.fn();

vi.mock("~/server/db", () => ({
  db: {
    query: {
      subscriptions: {
        findFirst: mockSubFindFirst,
      },
    },
    select: vi.fn((...args: unknown[]) => {
      const firstArg = args[0] as { count?: unknown } | undefined;
      const isCount = args.length > 0 && firstArg?.count;

      if (isCount) {
        return {
          from: vi.fn(() => ({
            where: vi.fn(() => mockCountSelectFromWhere()),
          })),
        };
      }

      return {
        from: vi.fn(() => ({
          where: vi.fn(() => ({
            limit: mockSelectFromWhereLimit,
            orderBy: vi.fn(() => ({
              limit: vi.fn(() => ({
                offset: mockSelectFromWhereOrderByLimitOffset,
              })),
            })),
          })),
          innerJoin: vi.fn(() => ({
            where: vi.fn(() => ({
              limit: vi.fn().mockResolvedValue([]),
            })),
          })),
        })),
      };
    }),
    insert: vi.fn(() => ({
      values: vi.fn(() => ({
        returning: mockInsertValuesReturning,
      })),
    })),
    update: vi.fn(() => ({
      set: vi.fn(() => ({
        where: vi.fn(() => ({
          returning: mockUpdateSetWhereReturning,
        })),
      })),
    })),
  },
}));

const mockSub = {
  id: "sub-1",
  userId: "user-1",
  tier: "premium" as const,
  status: "active" as const,
  stripeId: null as string | null,
  familyGroupId: null as string | null,
  currentPeriodStart: new Date(),
  currentPeriodEnd: new Date(Date.now() + 86400000),
  cancelAtPeriodEnd: false,
  createdAt: new Date(),
  updatedAt: new Date(),
};

beforeEach(() => {
  vi.clearAllMocks();
  mockSelectFromWhereLimit.mockResolvedValue([mockSub]);
});

describe("checkTierLimits", () => {
  it("allows premium tier always", async () => {
    mockSubFindFirst.mockResolvedValue({ ...mockSub, tier: "premium" as const });
    const { checkTierLimits } = await import("./darkwatch.service");
    const result = await checkTierLimits("user-1");
    expect(result.allowed).toBe(true);
  });

  it("allows basic tier if under limit", async () => {
    mockSubFindFirst.mockResolvedValue({ ...mockSub, tier: "basic" as const });
    mockCountSelectFromWhere.mockResolvedValue([{ count: 0 }]);
    const { checkTierLimits } = await import("./darkwatch.service");
    const result = await checkTierLimits("user-1");
    expect(result.allowed).toBe(true);
  });
});

describe("addWatchlistItem", () => {
  it("hashes and deduplicates values", async () => {
    mockSubFindFirst.mockResolvedValue(mockSub);
    mockSelectFromWhereLimit
      .mockResolvedValueOnce([mockSub])
      .mockResolvedValueOnce([]);
    mockInsertValuesReturning.mockResolvedValue([{
      id: "w1", subscriptionId: "sub-1", type: "email", value: "test@example.com",
      hash: "some-hash", isActive: true,
      createdAt: new Date(), updatedAt: new Date(),
    }]);
    const { addWatchlistItem } = await import("./darkwatch.service");
    const result = await addWatchlistItem("user-1", "email", "test@example.com");
    expect(result.type).toBe("email");
    expect(result.value).toBe("test@example.com");
  });
});

describe("removeWatchlistItem", () => {
  it("throws not found if item does not belong to user", async () => {
    mockSubFindFirst.mockResolvedValue(mockSub);
    mockSelectFromWhereLimit.mockResolvedValue([]);
    const { removeWatchlistItem } = await import("./darkwatch.service");
    await expect(removeWatchlistItem("user-1", "nonexistent")).rejects.toThrow(TRPCError);
  });
});

describe("getExposures", () => {
  it("returns paginated exposures", async () => {
    mockSubFindFirst.mockResolvedValue(mockSub);
    mockCountSelectFromWhere.mockResolvedValue([{ count: 0 }]);
    mockSelectFromWhereOrderByLimitOffset.mockResolvedValue([]);
    const { getExposures } = await import("./darkwatch.service");
    const result = await getExposures("user-1", { page: 1, limit: 10 });
    expect(result.items).toEqual([]);
    expect(result.total).toBe(0);
    expect(result.page).toBe(1);
  });
});
