import { vi } from "bun:test"

import type { Doc, Id } from "../../convex/_generated/dataModel"

type ReportsCtxOptions = {
  tickets?: Doc<"tickets">[]
  createdRangeTickets?: Doc<"tickets">[]
  queues?: Doc<"queues">[]
  categories?: Doc<"ticketCategories">[]
  companies?: Map<string, Doc<"companies">>
  users?: Map<string, Doc<"users">>
  ticketEventsByTicket?: Map<string, Array<{ type: string; payload?: unknown; createdAt: number }>>
  ticketWorkSessionsByAgent?: Map<string, Array<{ agentId: Id<"users">; startedAt: number; stoppedAt?: number; durationMs?: number }>>
}

const noopFilterBuilder = {
  lt: () => noopFilterBuilder,
  field: () => "createdAt",
}

const noopIndexBuilder = {
  eq: () => noopIndexBuilder,
  gte: () => noopIndexBuilder,
}

function ticketsChain(collection: Doc<"tickets">[]) {
  const chain = {
    filter: vi.fn((cb?: (builder: typeof noopFilterBuilder) => unknown) => {
      cb?.(noopFilterBuilder)
      return chain
    }),
    order: vi.fn(() => chain),
    collect: vi.fn(async () => collection),
    take: vi.fn(async (limit: number) => collection.slice(0, limit)),
  }
  return chain
}

export function createReportsCtx({
  tickets = [],
  createdRangeTickets = tickets,
  queues = [],
  categories = [],
  companies = new Map<string, Doc<"companies">>(),
  users = new Map<string, Doc<"users">>(),
  ticketEventsByTicket = new Map<string, Array<{ type: string; payload?: unknown; createdAt: number }>>(),
  ticketWorkSessionsByAgent = new Map<string, Array<{ agentId: Id<"users">; startedAt: number; stoppedAt?: number; durationMs?: number }>>(),
}: ReportsCtxOptions = {}) {
  const db = {
    get: vi.fn(async (id: Id<"companies"> | Id<"users">) => {
      const company = companies.get(String(id))
      if (company) return company
      const user = users.get(String(id))
      if (user) return user
      return null
    }),
    query: vi.fn((table: string) => {
      if (table === "tickets") {
        return {
          withIndex: vi.fn((indexName: string, cb?: (builder: typeof noopIndexBuilder) => unknown) => {
            cb?.(noopIndexBuilder)
            const collection =
              indexName.includes("created") || indexName.includes("tenant_company_created")
                ? createdRangeTickets
                : tickets
            return ticketsChain(collection)
          }),
          collect: vi.fn(async () => tickets),
        }
      }

      if (table === "queues") {
        return {
          withIndex: vi.fn((_indexName: string, cb?: (builder: typeof noopIndexBuilder) => unknown) => {
            cb?.(noopIndexBuilder)
            return {
              collect: vi.fn(async () => queues),
              take: vi.fn(async (limit: number) => queues.slice(0, limit)),
            }
          }),
          collect: vi.fn(async () => queues),
          take: vi.fn(async (limit: number) => queues.slice(0, limit)),
        }
      }

      if (table === "ticketCategories") {
        return {
          withIndex: vi.fn((_indexName: string, cb?: (builder: typeof noopIndexBuilder) => unknown) => {
            cb?.(noopIndexBuilder)
            return {
              collect: vi.fn(async () => categories),
              take: vi.fn(async (limit: number) => categories.slice(0, limit)),
            }
          }),
          collect: vi.fn(async () => categories),
          take: vi.fn(async (limit: number) => categories.slice(0, limit)),
        }
      }

      if (table === "ticketEvents") {
        return {
          withIndex: vi.fn((_indexName: string, cb?: (builder: { eq: (field: unknown, value: unknown) => unknown }) => unknown) => {
            let ticketId: string | null = null
            const builder = {
              eq: (_field: unknown, value: unknown) => {
                ticketId = String(value)
                return builder
              },
            }
            cb?.(builder as { eq: (field: unknown, value: unknown) => unknown })
            const events = ticketId ? ticketEventsByTicket.get(ticketId) ?? [] : []
            return {
              collect: vi.fn(async () => events),
              take: vi.fn(async (limit: number) => events.slice(0, limit)),
            }
          }),
        }
      }

      if (table === "ticketWorkSessions") {
        return {
          withIndex: vi.fn((_indexName: string, cb?: (builder: { eq: (field: unknown, value: unknown) => unknown }) => unknown) => {
            let agentId: string | null = null
            const builder = {
              eq: (_field: unknown, value: unknown) => {
                agentId = String(value)
                return builder
              },
            }
            cb?.(builder as { eq: (field: unknown, value: unknown) => unknown })
            const sessions = agentId ? ticketWorkSessionsByAgent.get(agentId) ?? [] : []
            return {
              collect: vi.fn(async () => sessions),
              take: vi.fn(async (limit: number) => sessions.slice(0, limit)),
            }
          }),
        }
      }

      return {
        withIndex: vi.fn(() => ({
          collect: vi.fn(async () => []),
          take: vi.fn(async () => []),
        })),
        collect: vi.fn(async () => []),
        take: vi.fn(async () => []),
      }
    }),
  }

  return { db } as unknown
}
