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

vi.mock('../../lib/prisma', () => ({
  prisma: {
    damageInspection: { upsert: vi.fn() },
    damageReport: { upsert: vi.fn() },
    reservation: { update: vi.fn() },
  },
}))

vi.mock('./reservation.repo', () => ({
  findInspections: vi.fn(),
  findForInspection: vi.fn(),
}))

import { prisma } from '../../lib/prisma'
import * as repo from './reservation.repo'
import { getInspections, upsertInspection } from './reservation.inspection.service'

const editableReservation = {
  id: 'reservation_1',
  status: 'CONFIRMED',
  contractNumber: null,
  invoiceNumber: null,
  extras: {},
  customer: { firstName: 'Mina', lastName: 'Alami' },
}

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

  it('delegates inspection lookup to the repository with tenant scoping', async () => {
    vi.mocked(repo.findInspections).mockResolvedValue([{ id: 'inspection_1' }] as never)

    await expect(getInspections('reservation_1', 'company_1')).resolves.toEqual([{ id: 'inspection_1' }])
    expect(repo.findInspections).toHaveBeenCalledWith('reservation_1', 'company_1')
  })

  it('upserts check-in inspection, mirrors damage report data, and stores check-in mileage/fuel on reservation', async () => {
    vi.mocked(repo.findForInspection).mockResolvedValue(editableReservation as never)
    vi.mocked(prisma.damageInspection.upsert).mockResolvedValue({ id: 'inspection_1', type: 'CHECKIN' } as never)

    const body = {
      mileage: 42000,
      fuelLevel: 'FULL',
      fuelCharge: 0,
      generalCondition: 'Clean exterior',
      employeeNotes: 'Small scratch on rear bumper',
      customerAgreed: true,
      damagePoints: [
        { viewType: 'REAR', x: 52, y: 61, damageType: 'SCRATCH', description: 'Rear bumper scratch' },
      ],
    }

    await expect(upsertInspection('reservation_1', 'company_1', 'CHECKIN', body, 'employee_1')).resolves.toEqual({ id: 'inspection_1', type: 'CHECKIN' })

    expect(prisma.damageInspection.upsert).toHaveBeenCalledWith(expect.objectContaining({
      where: { reservationId_type: { reservationId: 'reservation_1', type: 'CHECKIN' } },
      update: expect.objectContaining({
        mileage: 42000,
        fuelLevel: 'FULL',
        customerAgreed: true,
        damagePoints: { deleteMany: {}, create: body.damagePoints },
      }),
      create: expect.objectContaining({ reservationId: 'reservation_1', companyId: 'company_1', type: 'CHECKIN' }),
      include: { damagePoints: true },
    }))
    expect(prisma.damageReport.upsert).toHaveBeenCalledWith(expect.objectContaining({
      where: { reservationId_type: { reservationId: 'reservation_1', type: 'CHECKIN' } },
      update: expect.objectContaining({
        customerName: 'Mina Alami',
        customerSignedAt: new Date('2026-06-09T12:00:00.000Z'),
        damages: [{ viewType: 'REAR', x: 52, y: 61, damageType: 'SCRATCH', severity: 'MINOR', note: 'Rear bumper scratch', isPreExisting: false }],
      }),
    }))
    expect(prisma.reservation.update).toHaveBeenCalledWith({
      where: { id: 'reservation_1' },
      data: { checkInMileage: 42000, checkInFuelLevel: 'FULL' },
    })
  })

  it('upserts checkout inspection only after completion and records checkout return values', async () => {
    vi.mocked(repo.findForInspection).mockResolvedValue({ ...editableReservation, status: 'COMPLETED' } as never)
    vi.mocked(prisma.damageInspection.upsert).mockResolvedValue({ id: 'inspection_2', type: 'CHECKOUT' } as never)

    await upsertInspection('reservation_1', 'company_1', 'CHECKOUT', {
      mileage: 42450,
      fuelLevel: 'HALF',
      customerAgreed: false,
      damagePoints: [{ viewType: 'FRONT', x: 10, y: 20, damageType: 'DENT', severity: 'MAJOR', isPreExisting: true }],
    }, 'employee_2')

    expect(prisma.damageReport.upsert).toHaveBeenCalledWith(expect.objectContaining({
      update: expect.objectContaining({ customerSignedAt: null }),
      create: expect.objectContaining({ photos: [], customerSignedAt: null }),
    }))
    expect(prisma.reservation.update).toHaveBeenCalledWith({
      where: { id: 'reservation_1' },
      data: { checkOutMileage: 42450, checkOutFuelLevel: 'HALF' },
    })
  })

  it('rejects check-in edits once reservation workflow is closed', async () => {
    vi.mocked(repo.findForInspection).mockResolvedValue({
      ...editableReservation,
      extras: { reservationClosedAt: '2026-06-08T10:00:00.000Z' },
    } as never)

    await expect(upsertInspection('reservation_1', 'company_1', 'CHECKIN', { fuelLevel: 'FULL' }, 'employee_1'))
      .rejects.toMatchObject({ error: 'reservation_closed', statusCode: 400 })
    expect(prisma.damageInspection.upsert).not.toHaveBeenCalled()
  })

  it('rejects checkout inspections before the reservation is completed', async () => {
    vi.mocked(repo.findForInspection).mockResolvedValue(editableReservation as never)

    await expect(upsertInspection('reservation_1', 'company_1', 'CHECKOUT', { fuelLevel: 'FULL' }, 'employee_1'))
      .rejects.toMatchObject({ error: 'invalid_status', statusCode: 400 })
  })
})
