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

vi.mock('@rentaldrivego/types', () => ({
  PLAN_FEATURES: { STARTER: ['fallback feature'] },
  PLAN_PRICES: { STARTER: { MONTHLY: { MAD: 199 } } },
}))
vi.mock('../../lib/prisma', () => ({
  prisma: {
    pricingConfig: { findMany: vi.fn() },
    planFeature: { findMany: vi.fn() },
  },
}))
vi.mock('../../services/platformContentService', () => ({ getCarplaceHomepageContent: vi.fn() }))
vi.mock('../../services/vehicleAvailabilityService', () => ({ getVehicleAvailabilitySummary: vi.fn() }))
vi.mock('../../services/insuranceService', () => ({ applyInsurancesToReservation: vi.fn() }))
vi.mock('../../services/additionalDriverService', () => ({ applyAdditionalDriversToReservation: vi.fn() }))
vi.mock('../../services/pricingRuleService', () => ({ applyPricingRules: vi.fn() }))
vi.mock('../../services/licenseValidationService', () => ({ validateLicense: vi.fn(), validateAndFlagLicense: vi.fn().mockResolvedValue(undefined) }))
vi.mock('../../services/amanpayService', () => ({ isConfigured: vi.fn(), createCheckout: vi.fn(), verifyWebhookSignature: vi.fn(), findTransactionFromWebhook: vi.fn() }))
vi.mock('../../services/paypalService', () => ({ createOrder: vi.fn(), captureOrder: vi.fn() }))
vi.mock('./site.presenter', () => ({
  presentBrand: vi.fn((company: any) => ({ company: { id: company.id, slug: company.slug }, brand: company.brand ?? null })),
  presentPublicBooking: vi.fn((reservation: any) => ({
    bookingReference: reservation.bookingReference ?? null,
    status: reservation.status === 'DRAFT' ? 'PENDING' : reservation.status,
  })),
}))
vi.mock('./site.repo', () => ({
  findCompanyBySlug: vi.fn(),
  findPublishedVehicles: vi.fn(),
  findVehicleById: vi.fn(),
  findActiveOffers: vi.fn(),
  findInsurancePolicies: vi.fn(),
  findOfferByPromoCode: vi.fn(),
  upsertCustomer: vi.fn(),
  createReservation: vi.fn(),
  findReservationWithDetails: vi.fn(),
  findBooking: vi.fn(),
  findReservationForPayment: vi.fn(),
  createReservationPublicAccess: vi.fn(),
  findReservationPublicAccess: vi.fn(),
  markReservationPublicAccessUsed: vi.fn(),
  createRentalPayment: vi.fn(),
  findPaymentByPaypalOrderId: vi.fn(),
  capturePaypalPayment: vi.fn(),
}))

import { AppError } from '../../http/errors'
import { prisma } from '../../lib/prisma'
import { getVehicleAvailabilitySummary } from '../../services/vehicleAvailabilityService'
import { applyPricingRules } from '../../services/pricingRuleService'
import { applyInsurancesToReservation } from '../../services/insuranceService'
import { applyAdditionalDriversToReservation } from '../../services/additionalDriverService'
import { validateLicense, validateAndFlagLicense } from '../../services/licenseValidationService'
import * as amanpay from '../../services/amanpayService'
import * as paypal from '../../services/paypalService'
import * as repo from './site.repo'
import * as service from './site.service'

const company = { id: 'company_1', slug: 'atlas', email: 'office@example.test', contractSettings: { depositRequired: true }, brand: { publicEmail: 'hello@example.test' } }

function futureDateRange(startOffsetDays: number, durationDays: number) {
  const start = new Date(Date.now() + startOffsetDays * 24 * 60 * 60 * 1000)
  const end = new Date(start.getTime() + durationDays * 24 * 60 * 60 * 1000)
  return { start, end }
}

function bookingBody(overrides: Record<string, unknown> = {}) {
  const { start, end } = futureDateRange(30, 3)

  return {
    vehicleId: 'vehicle_1',
    startDate: start.toISOString(),
    endDate: end.toISOString(),
    firstName: 'Nora',
    lastName: 'Renter',
    email: 'nora@example.test',
    phone: '+212600000000',
    pickupLocation: 'Casablanca',
    returnLocation: 'Casablanca',
    ...overrides,
  } as any
}

describe('site.service public booking/payment boundaries', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.mocked(repo.createReservationPublicAccess).mockResolvedValue({ id: 'access_1' } as never)
    vi.mocked(repo.findReservationPublicAccess).mockResolvedValue({ id: 'access_1' } as never)
    vi.mocked(repo.markReservationPublicAccessUsed).mockResolvedValue({ id: 'access_1' } as never)
    vi.mocked(repo.findCompanyBySlug).mockResolvedValue(company as never)
    vi.mocked(getVehicleAvailabilitySummary).mockResolvedValue({ available: true, status: 'AVAILABLE', nextAvailableAt: null } as never)
    vi.mocked(applyPricingRules).mockResolvedValue({ applied: [{ code: 'WEEKEND' }], total: 90 } as never)
    vi.mocked(validateLicense).mockReturnValue({ status: 'VALID', requiresApproval: false, expired: false, expiringSoon: false } as never)
  })

  it('uses configured pricing and configured feature labels when platform pricing exists', async () => {
    vi.mocked(prisma.pricingConfig.findMany).mockResolvedValue([
      { plan: 'STARTER', billingPeriod: 'MONTHLY', amount: 299 },
      { plan: 'PRO', billingPeriod: 'YEARLY', amount: 2999 },
    ] as never)
    vi.mocked(prisma.planFeature.findMany).mockResolvedValue([
      { plan: 'STARTER', label: '3 vehicles' },
      { plan: 'PRO', label: 'Priority support' },
    ] as never)

    await expect(service.getPlatformPricing()).resolves.toEqual({
      prices: { STARTER: { MONTHLY: { MAD: 299 } }, PRO: { YEARLY: { MAD: 2999 } } },
      planFeatures: { STARTER: ['3 vehicles'] },
    })
  })

  it('calculates booking totals from base rate, free-day promo, pricing rules, insurance and additional drivers', async () => {
    const body = bookingBody({
      promoCodeUsed: 'FREEDAY',
      selectedInsurancePolicyIds: ['insurance_1'],
      additionalDrivers: [{ firstName: 'Second' }],
    })
    vi.mocked(repo.findVehicleById).mockResolvedValue({ id: 'vehicle_1', companyId: 'company_1', dailyRate: 500, category: 'SUV' } as never)
    vi.mocked(repo.upsertCustomer).mockResolvedValue({ id: 'customer_1' } as never)
    vi.mocked(repo.findOfferByPromoCode).mockResolvedValue({ id: 'offer_1', type: 'FREE_DAY', discountValue: 1 } as never)
    vi.mocked(repo.createReservation).mockResolvedValue({ id: 'reservation_1' } as never)
    vi.mocked(repo.findReservationWithDetails).mockResolvedValue({
      id: 'reservation_1',
      bookingReference: 'BK-2026-AB12CD',
      status: 'DRAFT',
      startDate: new Date(body.startDate),
      endDate: new Date(body.endDate),
      pickupLocation: 'Casablanca',
      returnLocation: 'Casablanca',
      vehicle: { make: 'Dacia', model: 'Duster', year: 2024 },
      customer: { firstName: 'Aya', lastName: 'Benali', email: 'aya@example.test', phone: '+212600000000' },
      company: { name: 'Atlas Cars', brand: { displayName: 'Atlas Cars' } },
      additionalDrivers: [],
      insurances: [],
      totalAmount: 1090,
      paidAmount: 0,
      paymentStatus: 'UNPAID',
    } as never)

    const result = await service.createBooking('atlas', body)

    expect(repo.createReservation).toHaveBeenCalledWith(expect.objectContaining({
      totalDays: 3,
      dailyRate: 500,
      discountAmount: 500,
      pricingRulesTotal: 90,
      totalAmount: 1090,
      pickupLocation: 'Casablanca',
      returnLocation: 'Casablanca',
      status: 'DRAFT',
      source: 'PUBLIC_SITE',
    }))
    expect(applyInsurancesToReservation).not.toHaveBeenCalled()
    expect(applyAdditionalDriversToReservation).not.toHaveBeenCalled()
    expect(validateAndFlagLicense).not.toHaveBeenCalled()
    expect(result.status).toBe('PENDING')
    expect(result.bookingReference).toBe('BK-2026-AB12CD')
  })

  it('rejects payment initialization for already-paid reservations before provider calls', async () => {
    vi.mocked(repo.findReservationForPayment).mockResolvedValue({ paymentStatus: 'PAID' } as never)

    await expect(service.initPayment('atlas', 'reservation_1', {
      provider: 'AMANPAY',
      accessToken: 'booking-access-token-123',
      successUrl: 'https://example.test/success',
      failureUrl: 'https://example.test/failure',
    })).rejects.toMatchObject({ statusCode: 409, error: 'already_paid' })
    expect(amanpay.createCheckout).not.toHaveBeenCalled()
  })

  it('blocks payment when the primary or additional driver license requires review', async () => {
    vi.mocked(validateLicense).mockReturnValue({ status: 'EXPIRING_SOON', requiresApproval: true } as never)
    vi.mocked(repo.findReservationForPayment).mockResolvedValue({
      id: 'reservation_1',
      paymentStatus: 'UNPAID',
      totalAmount: 400,
      customer: { licenseExpiry: new Date('2026-07-01'), licenseValidationStatus: 'PENDING' },
      additionalDrivers: [],
      vehicle: { make: 'Dacia', model: 'Duster' },
    } as never)

    await expect(service.initPayment('atlas', 'reservation_1', {
      provider: 'PAYPAL',
      accessToken: 'booking-access-token-123',
      successUrl: 'https://example.test/success',
      failureUrl: 'https://example.test/failure',
    })).rejects.toMatchObject({ statusCode: 409, error: 'license_review_required' })
    expect(paypal.createOrder).not.toHaveBeenCalled()
  })

  it('routes public contact messages to the brand public email when configured', async () => {
    await expect(service.handleContact('atlas', { name: 'Visitor', email: 'visitor@example.test', message: 'Hi' })).resolves.toEqual({
      success: true,
      deliveredTo: 'hello@example.test',
      preview: { name: 'Visitor', email: 'visitor@example.test', message: 'Hi' },
    })
  })
})
