import { vi } from "vitest";

export const TEST_USER_ID = "assistant-test-user";
export const TEST_CONVERSATION_ID = "assistant-test-conversation";

export function createApprovalStoreMock() {
  const records = new Map<string, {
    id: string;
    userId: string;
    conversationId: string;
    toolName: string;
    toolArguments: string;
    summary: string;
    status: "PENDING" | "APPROVED" | "CANCELLED" | "EXPIRED";
    approvedAt: Date | null;
    cancelledAt: Date | null;
    createdAt: Date;
    expiresAt: Date;
    updatedAt: Date;
  }>();

  return {
    assistantApproval: {
      findFirst: vi.fn(async ({
        where,
        orderBy,
      }: {
        where: {
          id?: string;
          userId?: string;
          conversationId?: string;
          status?: "PENDING" | "APPROVED" | "CANCELLED" | "EXPIRED";
        };
        orderBy?: { createdAt: "desc" | "asc" };
      }) => {
        const matches = [...records.values()]
          .filter((record) => (
            (!where.id || record.id === where.id)
            && (!where.userId || record.userId === where.userId)
            && (!where.conversationId || record.conversationId === where.conversationId)
            && (!where.status || record.status === where.status)
          ))
          .sort((a, b) => (
            orderBy?.createdAt === "asc"
              ? a.createdAt.getTime() - b.createdAt.getTime()
              : b.createdAt.getTime() - a.createdAt.getTime()
          ));
        return matches[0] ?? null;
      }),
      findMany: vi.fn(async ({
        where,
        orderBy,
      }: {
        where: {
          userId?: string;
          conversationId?: string;
          status?: "PENDING" | "APPROVED" | "CANCELLED" | "EXPIRED";
          expiresAt?: { lte?: Date; gt?: Date };
        };
        orderBy?: { createdAt: "desc" | "asc" };
      }) => (
        [...records.values()]
          .filter((record) => (
            (!where.userId || record.userId === where.userId)
            && (!where.conversationId || record.conversationId === where.conversationId)
            && (!where.status || record.status === where.status)
            && (!where.expiresAt?.lte || record.expiresAt <= where.expiresAt.lte)
            && (!where.expiresAt?.gt || record.expiresAt > where.expiresAt.gt)
          ))
          .sort((a, b) => (
            orderBy?.createdAt === "asc"
              ? a.createdAt.getTime() - b.createdAt.getTime()
              : b.createdAt.getTime() - a.createdAt.getTime()
          ))
      )),
      create: vi.fn(async ({
        data,
      }: {
        data: {
          userId: string;
          conversationId: string;
          toolName: string;
          toolArguments: string;
          summary: string;
          createdAt: Date;
          expiresAt: Date;
        };
      }) => {
        const record = {
          id: `approval-${records.size + 1}`,
          ...data,
          status: "PENDING" as const,
          approvedAt: null,
          cancelledAt: null,
          updatedAt: data.createdAt,
        };
        records.set(record.id, record);
        return record;
      }),
      updateMany: vi.fn(async ({
        where,
        data,
      }: {
        where: {
          id?: string;
          userId?: string;
          conversationId?: string;
          status?: "PENDING" | "APPROVED" | "CANCELLED" | "EXPIRED";
          expiresAt?: { lte?: Date; gt?: Date };
        };
        data: Partial<{
          status: "APPROVED" | "CANCELLED" | "EXPIRED";
          cancelledAt: Date;
          approvedAt: Date;
        }>;
      }) => {
        let count = 0;
        for (const [id, record] of records.entries()) {
          if (where.id && record.id !== where.id) continue;
          if (where.userId && record.userId !== where.userId) continue;
          if (where.conversationId && record.conversationId !== where.conversationId) continue;
          if (where.status && record.status !== where.status) continue;
          if (where.expiresAt?.lte && record.expiresAt > where.expiresAt.lte) continue;
          if (where.expiresAt?.gt && record.expiresAt <= where.expiresAt.gt) continue;
          records.set(id, {
            ...record,
            ...data,
            updatedAt: new Date(),
          });
          count += 1;
        }
        return { count };
      }),
      update: vi.fn(async ({
        where,
        data,
      }: {
        where: { id: string };
        data: {
          status: "APPROVED";
          approvedAt: Date;
        };
      }) => {
        const record = records.get(where.id);
        if (!record) throw new Error("Record not found");
        const next = {
          ...record,
          ...data,
          updatedAt: new Date(),
        };
        records.set(where.id, next);
        return next;
      }),
    },
  };
}

export function createMissingApprovalTableError() {
  return Object.assign(
    new Error("The table `public.assistant_approvals` does not exist in the current database."),
    {
      code: "P2021",
      meta: { table: "public.assistant_approvals" },
    },
  );
}
