import { afterAll, beforeAll, describe, expect, it, vi } from "bun:test"

vi.mock("../convex/rbac", () => ({
  requireStaff: vi.fn(),
}))

import type { Doc, Id } from "../convex/_generated/dataModel"
import {
  csatOverviewHandler,
  hoursByClientHandler,
  openedResolvedByDayHandler,
} from "../convex/reports"
import { requireStaff } from "../convex/rbac"
import { createReportsCtx } from "./utils/report-test-helpers"

const TENANT_ID = "tenant-1"
const VIEWER_ID = "user-reports" as Id<"users">

function buildTicket(overrides: Partial<Doc<"tickets">>): Doc<"tickets"> {
  const base: Record<string, unknown> = {
    _id: "ticket_base" as Id<"tickets">,
    tenantId: TENANT_ID,
    reference: 50000,
    subject: "Chamado",
    summary: null,
    status: "PENDING",
    priority: "MEDIUM",
    channel: "EMAIL",
    queueId: undefined,
    requesterId: "user_req" as Id<"users">,
    requesterSnapshot: { name: "Alice", email: "alice@example.com", avatarUrl: undefined, teams: [] },
    assigneeId: "user_assignee" as Id<"users">,
    assigneeSnapshot: { name: "Bob", email: "bob@example.com", avatarUrl: undefined, teams: [] },
    companyId: undefined,
    companySnapshot: undefined,
    machineId: undefined,
    machineSnapshot: undefined,
    working: false,
    dueAt: undefined,
    firstResponseAt: undefined,
    resolvedAt: undefined,
    closedAt: undefined,
    updatedAt: Date.now(),
    createdAt: Date.now(),
    tags: [],
    customFields: [],
    totalWorkedMs: 0,
    internalWorkedMs: 0,
    externalWorkedMs: 0,
    activeSessionId: undefined,
  }
  return { ...(base as Doc<"tickets">), ...overrides }
}

function buildCompany(overrides: Partial<Doc<"companies">>): Doc<"companies"> {
  const base: Record<string, unknown> = {
    _id: "company_base" as Id<"companies">,
    tenantId: TENANT_ID,
    name: "Empresa",
    slug: "empresa",
    createdAt: Date.now(),
    updatedAt: Date.now(),
    isAvulso: false,
    contractedHoursPerMonth: 40,
  }
  return { ...(base as Doc<"companies">), ...overrides }
}

describe("convex.reports.openedResolvedByDay", () => {
  const requireStaffMock = vi.mocked(requireStaff)
  const FIXED_NOW = Date.UTC(2024, 4, 15, 12, 0, 0)

  beforeAll(() => {
    vi.useFakeTimers()
    vi.setSystemTime(FIXED_NOW)
  })

  afterAll(() => {
    vi.useRealTimers()
  })

  it("counts opened and resolved tickets per day within the range", async () => {
    requireStaffMock.mockResolvedValue({
      role: "ADMIN",
      user: { companyId: undefined },
    } as unknown as Awaited<ReturnType<typeof requireStaff>>)

    const tickets = [
      buildTicket({
        _id: "ticket_open" as Id<"tickets">,
        createdAt: Date.UTC(2024, 4, 13, 9, 0, 0),
        status: "PENDING",
      }),
      buildTicket({
        _id: "ticket_resolved" as Id<"tickets">,
        createdAt: Date.UTC(2024, 4, 12, 10, 0, 0),
        resolvedAt: Date.UTC(2024, 4, 14, 8, 0, 0),
        status: "RESOLVED",
      }),
      buildTicket({
        _id: "ticket_old" as Id<"tickets">,
        createdAt: Date.UTC(2024, 3, 30, 10, 0, 0),
        status: "PENDING",
      }),
    ]

    const ctx = createReportsCtx({ tickets }) as Parameters<typeof openedResolvedByDayHandler>[0]

    const result = await openedResolvedByDayHandler(ctx, {
      tenantId: TENANT_ID,
      viewerId: VIEWER_ID,
      range: "7d",
    })

    const opened13 = result.series.find((point) => point.date === "2024-05-13")
    const resolved14 = result.series.find((point) => point.date === "2024-05-14")

    expect(result.rangeDays).toBe(7)
    expect(opened13).toMatchObject({ opened: 1, resolved: 0 })
    expect(resolved14).toMatchObject({ opened: 0, resolved: 1 })
  })
})

describe("convex.reports.csatOverview", () => {
  const requireStaffMock = vi.mocked(requireStaff)
  const FIXED_NOW = Date.UTC(2024, 4, 20, 12, 0, 0)

  beforeAll(() => {
    vi.useFakeTimers()
    vi.setSystemTime(FIXED_NOW)
  })

  afterAll(() => {
    vi.useRealTimers()
  })

  it("summarizes survey averages and distribution", async () => {
    requireStaffMock.mockResolvedValue({
      role: "ADMIN",
      user: { companyId: undefined },
    } as unknown as Awaited<ReturnType<typeof requireStaff>>)

    const ticketA = buildTicket({
      _id: "ticket_a" as Id<"tickets">,
      createdAt: Date.UTC(2024, 4, 18, 9, 0, 0),
    })
    const ticketB = buildTicket({
      _id: "ticket_b" as Id<"tickets">,
      createdAt: Date.UTC(2024, 4, 17, 15, 0, 0),
    })

    const eventsByTicket = new Map<string, Array<{ type: string; payload?: unknown; createdAt: number }>>([
      ["ticket_a", [{ type: "CSAT_RECEIVED", payload: { score: 5 }, createdAt: Date.UTC(2024, 4, 19, 8, 0, 0) }]],
      [
        "ticket_b",
        [
          { type: "CSAT_RECEIVED", payload: { score: 3 }, createdAt: Date.UTC(2024, 4, 18, 14, 0, 0) },
          { type: "COMMENT", payload: {}, createdAt: Date.UTC(2024, 4, 18, 15, 0, 0) },
        ],
      ],
    ])

    const ctx = createReportsCtx({ tickets: [ticketA, ticketB], ticketEventsByTicket: eventsByTicket }) as Parameters<typeof csatOverviewHandler>[0]

    const result = await csatOverviewHandler(ctx, {
      tenantId: TENANT_ID,
      viewerId: VIEWER_ID,
      range: "7d",
    })

    expect(result.rangeDays).toBe(7)
    expect(result.totalSurveys).toBe(2)
    expect(result.averageScore).toBe(4)
    expect(result.distribution.find((entry) => entry.score === 5)?.total).toBe(1)
    expect(result.distribution.find((entry) => entry.score === 3)?.total).toBe(1)
    expect(result.recent[0]?.ticketId).toBe("ticket_a" as Id<"tickets">)
  })
})

describe("convex.reports.hoursByClient", () => {
  const requireStaffMock = vi.mocked(requireStaff)
  const FIXED_NOW = Date.UTC(2024, 5, 5, 12, 0, 0)

  beforeAll(() => {
    vi.useFakeTimers()
    vi.setSystemTime(FIXED_NOW)
  })

  afterAll(() => {
    vi.useRealTimers()
  })

  it("aggregates worked hours by company", async () => {
    requireStaffMock.mockResolvedValue({
      role: "ADMIN",
      user: { companyId: undefined },
    } as unknown as Awaited<ReturnType<typeof requireStaff>>)

    const companyA = buildCompany({ _id: "company_a" as Id<"companies">, name: "Empresa A", contractedHoursPerMonth: 60 })
    const companyB = buildCompany({ _id: "company_b" as Id<"companies">, name: "Empresa B", isAvulso: true })

    const tickets = [
      buildTicket({
        _id: "ticket_a" as Id<"tickets">,
        companyId: companyA._id,
        updatedAt: Date.UTC(2024, 5, 4, 10, 0, 0),
        internalWorkedMs: 3 * 3600000,
        externalWorkedMs: 1 * 3600000,
      }),
      buildTicket({
        _id: "ticket_b" as Id<"tickets">,
        companyId: companyB._id,
        updatedAt: Date.UTC(2024, 5, 3, 15, 0, 0),
        internalWorkedMs: 3600000,
        externalWorkedMs: 2 * 3600000,
      }),
      buildTicket({
        _id: "ticket_old" as Id<"tickets">,
        companyId: companyA._id,
        updatedAt: Date.UTC(2024, 4, 20, 12, 0, 0),
        internalWorkedMs: 5 * 3600000,
        externalWorkedMs: 0,
      }),
    ]

    const companies = new Map<string, Doc<"companies">>([
      [String(companyA._id), companyA],
      [String(companyB._id), companyB],
    ])

    const ctx = createReportsCtx({ tickets, companies }) as Parameters<typeof hoursByClientHandler>[0]

    const result = await hoursByClientHandler(ctx, {
      tenantId: TENANT_ID,
      viewerId: VIEWER_ID,
      range: "7d",
    })

    expect(result.rangeDays).toBe(7)
    expect(result.items).toHaveLength(2)
    expect(result.items).toEqual(
      expect.arrayContaining([
        expect.objectContaining({
          companyId: companyA._id,
          internalMs: 3 * 3600000,
          externalMs: 3600000,
          totalMs: 4 * 3600000,
          contractedHoursPerMonth: 60,
        }),
        expect.objectContaining({
          companyId: companyB._id,
          internalMs: 3600000,
          externalMs: 2 * 3600000,
          totalMs: 3 * 3600000,
          isAvulso: true,
        }),
      ])
    )
  })
})
