import { beforeEach, describe, expect, it, vi } from 'vitest'

vi.mock('../../lib/prisma', () => ({
  prisma: {
    reservation: {
      count: vi.fn(),
      aggregate: vi.fn(),
      findMany: vi.fn(),
      groupBy: vi.fn(),
    },
    vehicle: {
      count: vi.fn(),
    },
    customer: {
      count: vi.fn(),
    },
    subscription: {
      findUnique: vi.fn(),
    },
    accountingSettings: {
      findUnique: vi.fn(),
    },
  },
}))

vi.mock('../../services/financialReportService', () => ({
  generateFinancialReport: vi.fn(),
  toCsv: vi.fn(),
}))

import { prisma } from '../../lib/prisma'
import { generateFinancialReport, toCsv } from '../../services/financialReportService'
import { getDashboard, getReport, getSources, getSummary } from './analytics.service'

describe('analytics.service', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.setSystemTime(new Date('2026-06-08T12:00:00.000Z'))
  })

  it('summarizes reservations, available vehicles, revenue, and customers for a day window', async () => {
    vi.mocked(prisma.reservation.count).mockResolvedValue(7 as never)
    vi.mocked(prisma.vehicle.count).mockResolvedValue(12 as never)
    vi.mocked(prisma.reservation.aggregate).mockResolvedValue({ _sum: { totalAmount: 4800 } } as never)
    vi.mocked(prisma.customer.count).mockResolvedValue(42 as never)

    const result = await getSummary('company_1', '14d')

    expect(prisma.reservation.count).toHaveBeenCalledWith({
      where: { companyId: 'company_1', createdAt: { gte: new Date('2026-05-25T12:00:00.000Z') } },
    })
    expect(prisma.vehicle.count).toHaveBeenCalledWith({ where: { companyId: 'company_1', status: 'AVAILABLE' } })
    expect(result).toEqual({
      totalReservations: 7,
      activeVehicles: 12,
      totalRevenue: 4800,
      totalCustomers: 42,
      period: '14d',
    })
  })

  it('builds dashboard KPIs, percentage deltas, recent reservation cards, source breakdown, and subscription summary', async () => {
    vi.mocked(prisma.reservation.count)
      .mockResolvedValueOnce(10 as never)
      .mockResolvedValueOnce(5 as never)
    vi.mocked(prisma.vehicle.count)
      .mockResolvedValueOnce(4 as never)
      .mockResolvedValueOnce(0 as never)
    vi.mocked(prisma.customer.count)
      .mockResolvedValueOnce(20 as never)
      .mockResolvedValueOnce(25 as never)
    vi.mocked(prisma.reservation.aggregate)
      .mockResolvedValueOnce({ _sum: { totalAmount: 9000 } } as never)
      .mockResolvedValueOnce({ _sum: { totalAmount: 3000 } } as never)
    vi.mocked(prisma.reservation.findMany).mockResolvedValue([
      {
        id: 'reservation_abc12345',
        contractNumber: null,
        customer: { firstName: 'Nora', lastName: 'Driver' },
        vehicle: { make: 'Dacia', model: 'Duster' },
        startDate: new Date('2026-06-10T00:00:00.000Z'),
        endDate: new Date('2026-06-12T00:00:00.000Z'),
        status: 'CONFIRMED',
        totalAmount: 1200,
      },
    ] as never)
    vi.mocked(prisma.reservation.groupBy).mockResolvedValue([
      { source: 'CARPLACE', _count: { id: 3 }, _sum: { totalAmount: 3600 } },
      { source: 'DIRECT', _count: { id: 2 }, _sum: { totalAmount: null } },
    ] as never)
    vi.mocked(prisma.subscription.findUnique).mockResolvedValue({
      status: 'ACTIVE',
      plan: 'PRO',
      trialEndAt: new Date('2026-06-30T00:00:00.000Z'),
    } as never)

    const result = await getDashboard('company_1')

    expect(result.kpis).toEqual({
      totalBookings: 10,
      activeVehicles: 4,
      monthlyRevenue: 9000,
      totalCustomers: 20,
      bookingsChange: 100,
      vehiclesChange: 100,
      revenueChange: 200,
      customersChange: -20,
    })
    expect(result.recentReservations).toEqual([expect.objectContaining({
      id: 'reservation_abc12345',
      bookingRef: 'ABC12345',
      customerName: 'Nora Driver',
      vehicleName: 'Dacia Duster',
      status: 'CONFIRMED',
      totalAmount: 1200,
    })])
    expect(result.sourceBreakdown).toEqual([
      { source: 'CARPLACE', count: 3, revenue: 3600 },
      { source: 'DIRECT', count: 2, revenue: 0 },
    ])
    expect(result.subscription).toEqual({
      status: 'ACTIVE',
      planName: 'PRO',
      trialEndsAt: new Date('2026-06-30T00:00:00.000Z'),
    })
  })

  it('returns source groups straight from reservation grouping', async () => {
    vi.mocked(prisma.reservation.groupBy).mockResolvedValue([{ source: 'DIRECT', _count: { id: 2 } }] as never)

    await expect(getSources('company_1')).resolves.toEqual([{ source: 'DIRECT', _count: { id: 2 } }])
    expect(prisma.reservation.groupBy).toHaveBeenCalledWith({ by: ['source'], where: { companyId: 'company_1' }, _count: { id: true } })
  })

  it('uses explicit report dates and emits CSV only when requested', async () => {
    vi.mocked(prisma.accountingSettings.findUnique).mockResolvedValue({ reportingPeriod: 'ANNUAL' } as never)
    vi.mocked(generateFinancialReport).mockResolvedValue({ rows: [{ label: 'Revenue', amount: 1000 }] } as never)
    vi.mocked(toCsv).mockReturnValue('label,amount\nRevenue,1000')

    const result = await getReport('company_1', {
      from: '2026-05-01',
      to: '2026-05-31',
      format: 'CSV',
    })

    expect(generateFinancialReport).toHaveBeenCalledWith('company_1', new Date('2026-05-01'), new Date('2026-05-31'))
    expect(toCsv).toHaveBeenCalledWith([{ label: 'Revenue', amount: 1000 }])
    expect(result.csv).toBe('label,amount\nRevenue,1000')
  })
})
