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

vi.mock('../../lib/prisma', () => ({
  prisma: {
    company: { findFirstOrThrow: vi.fn() },
    vehicle: { findMany: vi.fn(), findFirstOrThrow: vi.fn() },
    offer: { findMany: vi.fn(), findFirst: vi.fn() },
    insurancePolicy: { findMany: vi.fn() },
    customer: { findUnique: vi.fn(), create: vi.fn(), update: vi.fn() },
    reservation: { create: vi.fn(), findUniqueOrThrow: vi.fn(), findFirstOrThrow: vi.fn(), update: vi.fn() },
    rentalPayment: { create: vi.fn(), findFirstOrThrow: vi.fn(), update: vi.fn() },
  },
}))

import { prisma } from '../../lib/prisma'
import * as repo from './site.repo'

describe('site.repo public booking boundaries', () => {
  beforeEach(() => vi.clearAllMocks())

  it('only exposes companies with public-rental statuses by slug', async () => {
    await repo.findCompanyBySlug('atlas-rentals')

    expect(prisma.company.findFirstOrThrow).toHaveBeenCalledWith({
      where: { slug: 'atlas-rentals', status: { in: ['ACTIVE', 'TRIALING', 'PAST_DUE', 'SUSPENDED'] } },
      include: { brand: true, contractSettings: true },
    })
  })

  it('loads only published vehicles for the public site', async () => {
    await repo.findPublishedVehicles('company_1')

    expect(prisma.vehicle.findMany).toHaveBeenCalledWith({
      where: { companyId: 'company_1', isPublished: true },
      orderBy: { createdAt: 'desc' },
    })
  })

  it('merges existing customer address metadata when upserting a public booking customer', async () => {
    vi.mocked(prisma.customer.findUnique).mockResolvedValue({
      id: 'customer_1',
      address: { city: 'Marrakesh', old: 'kept' },
    } as never)

    await repo.upsertCustomer('company_1', {
      email: 'customer@example.test',
      firstName: 'Nora',
      lastName: 'Guest',
      phone: '+212600000000',
      driverLicense: 'DL-1',
      dateOfBirth: '1995-01-02',
      licenseExpiry: '2031-05-06',
      licenseIssuedAt: '2021-05-06',
      nationality: 'MA',
      identityDocumentNumber: 'ID-9',
      fullAddress: 'New address',
      licenseCountry: 'MA',
      licenseCategory: 'B',
      internationalLicenseNumber: undefined,
    })

    expect(prisma.customer.update).toHaveBeenCalledWith(expect.objectContaining({
      where: { id: 'customer_1' },
      data: expect.objectContaining({
        firstName: 'Nora',
        address: expect.objectContaining({ city: 'Marrakesh', fullAddress: 'New address', identityDocumentNumber: 'ID-9' }),
        licenseNumber: 'DL-1',
      }),
    }))
  })

  it('creates pending rental payments with normalized charge metadata', async () => {
    await repo.createRentalPayment({
      companyId: 'company_1',
      reservationId: 'reservation_1',
      amount: 1200,
      currency: 'MAD',
      paymentProvider: 'PAYPAL',
      paypalCaptureId: 'order_1',
    })

    expect(prisma.rentalPayment.create).toHaveBeenCalledWith({
      data: {
        companyId: 'company_1',
        reservationId: 'reservation_1',
        amount: 1200,
        currency: 'MAD',
        paymentProvider: 'PAYPAL',
        paypalCaptureId: 'order_1',
        status: 'PENDING',
        type: 'CHARGE',
      },
    })
  })

  it('captures PayPal payments by updating payment and reservation atomically in sequence', async () => {
    vi.useFakeTimers()
    vi.setSystemTime(new Date('2026-11-10T09:30:00.000Z'))

    await repo.capturePaypalPayment('payment_1', 'capture_1', 'reservation_1', 1500)

    expect(prisma.rentalPayment.update).toHaveBeenCalledWith({
      where: { id: 'payment_1' },
      data: { status: 'SUCCEEDED', paidAt: new Date('2026-11-10T09:30:00.000Z'), paypalCaptureId: 'capture_1' },
    })
    expect(prisma.reservation.update).toHaveBeenCalledWith({
      where: { id: 'reservation_1' },
      data: { paymentStatus: 'PAID', paidAmount: { increment: 1500 } },
    })

    vi.useRealTimers()
  })
})
