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

const mockInsertValuesReturning = vi.fn();
const mockSelectFromWhereLimit = vi.fn();
const mockSelectFromWhereOrderByLimitOffset = vi.fn();
const mockSelectFromWhereOrderBy = vi.fn();
const mockCountSelectFromWhere = vi.fn();
const mockSubFindFirst = vi.fn();
const mockUpdateSetWhereReturning = 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([]),
              })),
            })),
          })),
          orderBy: mockSelectFromWhereOrderBy,
        })),
      };
    }),
    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("getBrokerRegistry", () => {
  it("returns active brokers with metadata", async () => {
    const { getBrokerRegistry } = await import("./removebrokers.service");
    const brokers = await getBrokerRegistry();
    expect(brokers.length).toBeGreaterThan(20);
    expect(brokers[0]).toHaveProperty("name");
    expect(brokers[0]).toHaveProperty("domain");
    expect(brokers[0]).toHaveProperty("removalMethod");
    expect(brokers[0]).toHaveProperty("removalUrl");
  });
});

describe("createRemovalRequest", () => {
  it("creates a removal request with PENDING status", async () => {
    mockSubFindFirst.mockResolvedValue(mockSub);
    mockSelectFromWhereLimit
      .mockResolvedValueOnce([mockSub])
      .mockResolvedValueOnce([{ id: "broker-1", name: "Spokeo", domain: "spokeo.com", removalMethod: "MANUAL_FORM", category: "PEOPLE_SEARCH", removalUrl: "https://spokeo.com/optout", requiresAccount: false, requiresVerification: true, estimatedDays: 7, isActive: true }])
      .mockResolvedValueOnce([])
      .mockResolvedValueOnce([{
        id: "req-1", subscriptionId: "sub-1", brokerId: "broker-1",
        status: "PENDING", personalInfo: {}, method: "MANUAL_FORM",
        attempts: 0, createdAt: new Date(), updatedAt: new Date(),
      }]);
    mockInsertValuesReturning.mockResolvedValue([{
      id: "req-1", subscriptionId: "sub-1", brokerId: "broker-1",
      status: "PENDING", personalInfo: {}, method: "MANUAL_FORM",
      attempts: 0, createdAt: new Date(), updatedAt: new Date(),
    }]);

    const { createRemovalRequest } = await import("./removebrokers.service");
    const result = await createRemovalRequest("user-1", "broker-1", { fullName: "John Doe" });
    expect(result.status).toBe("PENDING");
  });

  it("throws conflict if active request exists", async () => {
    mockSubFindFirst.mockResolvedValue(mockSub);
    mockSelectFromWhereLimit
      .mockResolvedValueOnce([mockSub])
      .mockResolvedValueOnce([{ id: "broker-1", name: "Spokeo", domain: "spokeo.com", removalMethod: "MANUAL_FORM", category: "PEOPLE_SEARCH", removalUrl: "https://spokeo.com/optout", requiresAccount: false, requiresVerification: true, estimatedDays: 7, isActive: true }])
      .mockResolvedValueOnce([{ id: "existing-req", status: "PENDING" }]);

    const { createRemovalRequest } = await import("./removebrokers.service");
    await expect(
      createRemovalRequest("user-1", "broker-1", { fullName: "John Doe" }),
    ).rejects.toThrow(TRPCError);
  });

  it("throws not found for inactive broker", async () => {
    mockSubFindFirst.mockResolvedValue(mockSub);
    mockSelectFromWhereLimit
      .mockResolvedValueOnce([mockSub])
      .mockResolvedValueOnce([]);

    const { createRemovalRequest } = await import("./removebrokers.service");
    await expect(
      createRemovalRequest("user-1", "broker-1", { fullName: "John Doe" }),
    ).rejects.toThrow(TRPCError);
  });
});

describe("getRemovalRequests", () => {
  it("returns paginated removal requests", async () => {
    mockSubFindFirst.mockResolvedValue(mockSub);
    mockSelectFromWhereLimit.mockResolvedValue([mockSub]);
    mockCountSelectFromWhere.mockResolvedValue([{ count: 1 }]);
    mockSelectFromWhereOrderByLimitOffset.mockResolvedValue([{
      id: "req-1", status: "PENDING",
      createdAt: new Date(), updatedAt: new Date(),
    }]);

    const { getRemovalRequests } = await import("./removebrokers.service");
    const result = await getRemovalRequests("user-1", { page: 1, limit: 20 });
    expect(result.total).toBe(1);
    expect(result.items).toHaveLength(1);
  });
});

describe("scanForListings", () => {
  it("scans specified broker and creates listing", async () => {
    mockSubFindFirst.mockResolvedValue(mockSub);
    mockSelectFromWhereLimit
      .mockResolvedValueOnce([mockSub])
      .mockResolvedValueOnce([{ id: "broker-1", name: "Spokeo", domain: "spokeo.com", removalMethod: "MANUAL_FORM", category: "PEOPLE_SEARCH", removalUrl: "https://spokeo.com/optout", requiresAccount: false, requiresVerification: true, estimatedDays: 7, isActive: true }])
      .mockResolvedValueOnce([]);
    mockInsertValuesReturning.mockResolvedValue([{
      id: "listing-1", brokerId: "broker-1", url: "https://spokeo.com/profile/sub-1",
      dataFound: {}, isRemoved: false,
    }]);

    const { scanForListings } = await import("./removebrokers.service");
    const result = await scanForListings("user-1", "broker-1");
    expect(result.scanned).toBe(1);
    expect(result.listingsFound).toBe(1);
  });

  it("scans all active brokers when no brokerId", async () => {
    mockSubFindFirst.mockResolvedValue(mockSub);
    mockSelectFromWhereLimit
      .mockResolvedValueOnce([mockSub])
      .mockResolvedValueOnce([{ id: "broker-1", name: "Spokeo", domain: "spokeo.com", removalMethod: "MANUAL_FORM", category: "PEOPLE_SEARCH", removalUrl: "https://spokeo.com/optout", requiresAccount: false, requiresVerification: true, estimatedDays: 7, isActive: true }]);
    mockInsertValuesReturning.mockResolvedValue([{
      id: "listing-1", brokerId: "broker-1", url: "https://spokeo.com/profile/sub-1",
      dataFound: {}, isRemoved: false,
    }]);

    const { scanForListings } = await import("./removebrokers.service");
    const result = await scanForListings("user-1");
    expect(result.scanned).toBe(1);
  });
});

describe("processRemovals", () => {
  it("processes pending removal requests", async () => {
    mockSelectFromWhereLimit
      .mockResolvedValueOnce([{
        id: "req-1", subscriptionId: "sub-1", brokerId: "broker-1",
        status: "PENDING", personalInfo: { fullName: "John" },
        method: "MANUAL_FORM", attempts: 0,
        createdAt: new Date(), updatedAt: new Date(),
      }]);
    mockUpdateSetWhereReturning
      .mockResolvedValueOnce([{ id: "req-1", status: "SUBMITTED" }]);

    const { processRemovals } = await import("./removebrokers.service");
    const result = await processRemovals();
    expect(result.processed).toBe(1);
  });
});

describe("getStats", () => {
  it("returns removal statistics", async () => {
    mockSubFindFirst.mockResolvedValue(mockSub);
    mockSelectFromWhereLimit.mockResolvedValue([mockSub]);
    mockCountSelectFromWhere
      .mockResolvedValueOnce([{ count: 5 }])
      .mockResolvedValueOnce([{ count: 2 }])
      .mockResolvedValueOnce([{ count: 0 }])
      .mockResolvedValueOnce([{ count: 0 }])
      .mockResolvedValueOnce([{ count: 2 }])
      .mockResolvedValueOnce([{ count: 0 }])
      .mockResolvedValueOnce([{ count: 0 }])
      .mockResolvedValueOnce([{ count: 1 }])
      .mockResolvedValueOnce([{ count: 10 }])
      .mockResolvedValueOnce([{ count: 2 }]);

    const { getStats } = await import("./removebrokers.service");
    const result = await getStats("user-1");
    expect(result.total).toBe(5);
    expect(result.completionRate).toBe(40);
    expect(result.totalListings).toBe(10);
    expect(result.listingsRemoved).toBe(2);
  });
});
