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

vi.mock('../../lib/prisma', () => ({
  prisma: {
    reservation: { findFirst: vi.fn() },
    reservationPhoto: {
      findMany: vi.fn(),
      create: vi.fn(),
      findFirst: vi.fn(),
      delete: vi.fn(),
    },
  },
}))

vi.mock('../../lib/storage', () => ({
  uploadImage: vi.fn(),
  deleteImage: vi.fn(),
}))

import { NotFoundError } from '../../http/errors'
import { prisma } from '../../lib/prisma'
import { deleteImage, uploadImage } from '../../lib/storage'
import { deletePhoto, listPhotos, uploadPhoto } from './reservation.photo.service'

describe('reservation.photo.service', () => {
  beforeEach(() => vi.clearAllMocks())

  it('rejects photo listing when the reservation does not belong to the company', async () => {
    vi.mocked(prisma.reservation.findFirst).mockResolvedValue(null as never)

    await expect(listPhotos('reservation_1', 'company_1')).rejects.toBeInstanceOf(NotFoundError)
    expect(prisma.reservationPhoto.findMany).not.toHaveBeenCalled()
  })

  it('lists photos in creation order after confirming reservation ownership', async () => {
    vi.mocked(prisma.reservation.findFirst).mockResolvedValue({ id: 'reservation_1' } as never)
    vi.mocked(prisma.reservationPhoto.findMany).mockResolvedValue([{ id: 'photo_1' }] as never)

    await expect(listPhotos('reservation_1', 'company_1')).resolves.toEqual([{ id: 'photo_1' }])
    expect(prisma.reservationPhoto.findMany).toHaveBeenCalledWith({
      where: { reservationId: 'reservation_1' },
      orderBy: { createdAt: 'asc' },
    })
  })

  it('uploads images under the company/reservation scoped storage prefix before creating the photo row', async () => {
    vi.mocked(prisma.reservation.findFirst).mockResolvedValue({ id: 'reservation_1' } as never)
    vi.mocked(uploadImage).mockResolvedValue('/storage/companies/company_1/reservations/reservation_1/photos/photo.jpg')
    vi.mocked(prisma.reservationPhoto.create).mockResolvedValue({ id: 'photo_1' } as never)

    await uploadPhoto('reservation_1', 'company_1', 'PICKUP', Buffer.from('fake-image'))

    expect(uploadImage).toHaveBeenCalledWith(Buffer.from('fake-image'), 'companies/company_1/reservations/reservation_1/photos')
    expect(prisma.reservationPhoto.create).toHaveBeenCalledWith({
      data: {
        reservationId: 'reservation_1',
        type: 'PICKUP',
        url: '/storage/companies/company_1/reservations/reservation_1/photos/photo.jpg',
      },
    })
  })

  it('deletes the stored object before deleting the reservation photo row', async () => {
    vi.mocked(prisma.reservationPhoto.findFirst).mockResolvedValue({ id: 'photo_1', url: '/storage/photo.jpg' } as never)

    await deletePhoto('photo_1', 'reservation_1', 'company_1')

    expect(deleteImage).toHaveBeenCalledWith('/storage/photo.jpg')
    expect(prisma.reservationPhoto.delete).toHaveBeenCalledWith({ where: { id: 'photo_1' } })
  })
})
