import { Router } from 'express'
import { z } from 'zod'
import { prisma } from '../lib/prisma'
import { applyAdditionalDriversToReservation } from '../services/additionalDriverService'
import * as amanpay from '../services/amanpayService'
import { applyInsurancesToReservation } from '../services/insuranceService'
import { applyPricingRules } from '../services/pricingRuleService'
import { validateAndFlagLicense, validateLicense } from '../services/licenseValidationService'
import * as paypal from '../services/paypalService'
import { getMarketplaceHomepageContent } from '../services/platformContentService'
import { getVehicleAvailabilitySummary } from '../services/vehicleAvailabilityService'

const router = Router()

router.get('/platform/homepage', async (_req, res, next) => {
  try {
    res.json({ data: await getMarketplaceHomepageContent() })
  } catch (err) {
    next(err)
  }
})

function isDatabaseUnavailableError(error: unknown): boolean {
  if (!error || typeof error !== 'object') return false

  const candidate = error as { code?: string; message?: string }
  return candidate.code === 'P1001' || candidate.message?.includes("Can't reach database server") === true
}

async function findCompanyBySlug(slug: string) {
  return prisma.company.findFirstOrThrow({
    where: { slug, status: { in: ['ACTIVE', 'TRIALING', 'PAST_DUE', 'SUSPENDED'] } },
    include: { brand: true, contractSettings: true },
  })
}

router.get('/:slug/brand', async (req, res, next) => {
  try {
    const company = await findCompanyBySlug(req.params.slug)
    res.json({
      data: {
        company: {
          id: company.id,
          slug: company.slug,
          name: company.name,
          phone: company.phone,
        },
        brand: company.brand,
      },
    })
  } catch (err) {
    if (isDatabaseUnavailableError(err)) {
      return res.json({
        data: {
          company: { id: 'demo', slug: req.params.slug, name: 'Demo Company', phone: null },
          brand: null,
        },
      })
    }
    next(err)
  }
})

router.get('/:slug/vehicles', async (req, res, next) => {
  try {
    const company = await findCompanyBySlug(req.params.slug)
    const vehicles = await prisma.vehicle.findMany({
      where: { companyId: company.id, isPublished: true },
      orderBy: { createdAt: 'desc' },
    })
    const enrichedVehicles = await Promise.all(
      vehicles.map(async (vehicle) => {
        const availability = await getVehicleAvailabilitySummary(vehicle.id)
        return {
          ...vehicle,
          availability: availability.available,
          availabilityStatus: availability.status,
          nextAvailableAt: availability.nextAvailableAt,
        }
      }),
    )
    res.json({ data: enrichedVehicles })
  } catch (err) {
    if (isDatabaseUnavailableError(err)) return res.json({ data: [] })
    next(err)
  }
})

router.get('/:slug/vehicles/:id', async (req, res, next) => {
  try {
    const company = await findCompanyBySlug(req.params.slug)
    const vehicle = await prisma.vehicle.findFirstOrThrow({
      where: { id: req.params.id, companyId: company.id, isPublished: true },
    })
    const availability = await getVehicleAvailabilitySummary(vehicle.id)
    res.json({ data: { ...vehicle, availability: availability.available, availabilityStatus: availability.status, nextAvailableAt: availability.nextAvailableAt } })
  } catch (err) {
    if (isDatabaseUnavailableError(err)) {
      return res.status(503).json({ error: 'database_unavailable', message: 'Vehicle details are temporarily unavailable', statusCode: 503 })
    }
    next(err)
  }
})

router.get('/:slug/offers', async (req, res, next) => {
  try {
    const company = await findCompanyBySlug(req.params.slug)
    const offers = await prisma.offer.findMany({
      where: {
        companyId: company.id,
        isActive: true,
        validFrom: { lte: new Date() },
        validUntil: { gte: new Date() },
      },
      orderBy: [{ isFeatured: 'desc' }, { createdAt: 'desc' }],
    })
    res.json({ data: offers })
  } catch (err) {
    if (isDatabaseUnavailableError(err)) return res.json({ data: [] })
    next(err)
  }
})

router.get('/:slug/booking-options', async (req, res, next) => {
  try {
    const company = await findCompanyBySlug(req.params.slug)
    const insurancePolicies = await prisma.insurancePolicy.findMany({
      where: { companyId: company.id, isActive: true },
      orderBy: [{ isRequired: 'desc' }, { sortOrder: 'asc' }, { name: 'asc' }],
    })

    res.json({
      data: {
        insurancePolicies,
        contractSettings: company.contractSettings,
      },
    })
  } catch (err) {
    if (isDatabaseUnavailableError(err)) return res.json({ data: { insurancePolicies: [], contractSettings: null } })
    next(err)
  }
})

router.post('/:slug/availability', async (req, res, next) => {
  try {
    const company = await findCompanyBySlug(req.params.slug)
    const { vehicleId, startDate, endDate } = z.object({
      vehicleId: z.string().cuid(),
      startDate: z.string().datetime(),
      endDate: z.string().datetime(),
    }).parse(req.body)

    const availability = await getVehicleAvailabilitySummary(vehicleId, {
      range: { startDate: new Date(startDate), endDate: new Date(endDate) },
    })
    res.json({ data: { available: availability.available, nextAvailableAt: availability.nextAvailableAt } })
  } catch (err) {
    if (isDatabaseUnavailableError(err)) {
      return res.status(503).json({ error: 'database_unavailable', message: 'Availability checks are temporarily unavailable', statusCode: 503 })
    }
    next(err)
  }
})

router.post('/:slug/book/validate-code', async (req, res, next) => {
  try {
    const company = await findCompanyBySlug(req.params.slug)
    const { code } = z.object({ code: z.string().min(1) }).parse(req.body)
    const offer = await prisma.offer.findFirst({
      where: {
        companyId: company.id,
        promoCode: code,
        isActive: true,
        validFrom: { lte: new Date() },
        validUntil: { gte: new Date() },
      },
    })
    if (!offer) {
      return res.status(404).json({ error: 'invalid_code', message: 'Promo code not found or expired', statusCode: 404 })
    }
    res.json({ data: offer })
  } catch (err) {
    if (isDatabaseUnavailableError(err)) {
      return res.status(503).json({ error: 'database_unavailable', message: 'Promo code validation is temporarily unavailable', statusCode: 503 })
    }
    next(err)
  }
})

router.post('/:slug/book', async (req, res, next) => {
  try {
    const company = await findCompanyBySlug(req.params.slug)
    const body = z.object({
      vehicleId: z.string().cuid(),
      startDate: z.string().datetime(),
      endDate: z.string().datetime(),
      firstName: z.string().min(1),
      lastName: z.string().min(1),
      email: z.string().email(),
      phone: z.string().optional(),
      driverLicense: z.string().optional(),
      dateOfBirth: z.string().datetime().optional(),
      licenseExpiry: z.string().datetime().optional(),
      licenseIssuedAt: z.string().datetime().optional(),
      nationality: z.string().optional(),
      offerId: z.string().cuid().optional(),
      promoCodeUsed: z.string().optional(),
      notes: z.string().optional(),
      source: z.enum(['PUBLIC_SITE', 'MARKETPLACE']).default('PUBLIC_SITE'),
      selectedInsurancePolicyIds: z.array(z.string()).default([]),
      additionalDrivers: z.array(z.object({
        firstName: z.string().min(1),
        lastName: z.string().min(1),
        email: z.string().email().optional(),
        phone: z.string().optional(),
        driverLicense: z.string().min(1),
        licenseExpiry: z.string().datetime().optional(),
        licenseIssuedAt: z.string().datetime().optional(),
        dateOfBirth: z.string().datetime().optional(),
        nationality: z.string().optional(),
      })).default([]),
    }).parse(req.body)

    const vehicle = await prisma.vehicle.findFirstOrThrow({
      where: { id: body.vehicleId, companyId: company.id, isPublished: true },
    })
    const start = new Date(body.startDate)
    const end = new Date(body.endDate)
    if (end <= start) {
      return res.status(400).json({ error: 'invalid_dates', message: 'End date must be after start date', statusCode: 400 })
    }

    const availability = await getVehicleAvailabilitySummary(vehicle.id, {
      range: { startDate: start, endDate: end },
    })
    if (!availability.available) {
      return res.status(409).json({
        error: 'unavailable',
        message: 'Vehicle is not available for the selected dates',
        statusCode: 409,
        nextAvailableAt: availability.nextAvailableAt?.toISOString() ?? null,
      })
    }

    const customer = await prisma.customer.upsert({
      where: { companyId_email: { companyId: company.id, email: body.email } },
      update: {
        firstName: body.firstName,
        lastName: body.lastName,
        phone: body.phone ?? null,
        driverLicense: body.driverLicense ?? null,
        dateOfBirth: body.dateOfBirth ? new Date(body.dateOfBirth) : null,
        licenseExpiry: body.licenseExpiry ? new Date(body.licenseExpiry) : null,
        licenseIssuedAt: body.licenseIssuedAt ? new Date(body.licenseIssuedAt) : null,
        nationality: body.nationality ?? null,
      },
      create: {
        companyId: company.id,
        firstName: body.firstName,
        lastName: body.lastName,
        email: body.email,
        phone: body.phone ?? null,
        driverLicense: body.driverLicense ?? null,
        dateOfBirth: body.dateOfBirth ? new Date(body.dateOfBirth) : null,
        licenseExpiry: body.licenseExpiry ? new Date(body.licenseExpiry) : null,
        licenseIssuedAt: body.licenseIssuedAt ? new Date(body.licenseIssuedAt) : null,
        nationality: body.nationality ?? null,
      },
    })
    const totalDays = Math.max(1, Math.ceil((end.getTime() - start.getTime()) / 86400000))
    const baseAmount = vehicle.dailyRate * totalDays

    let discountAmount = 0
    if (body.promoCodeUsed) {
      const offer = await prisma.offer.findFirst({
        where: {
          companyId: company.id,
          promoCode: body.promoCodeUsed,
          isActive: true,
          validFrom: { lte: new Date() },
          validUntil: { gte: new Date() },
        },
      })
      if (offer) {
        if (offer.type === 'PERCENTAGE') discountAmount = Math.round(baseAmount * offer.discountValue / 100)
        else if (offer.type === 'FIXED_AMOUNT') discountAmount = offer.discountValue
        else if (offer.type === 'FREE_DAY') discountAmount = vehicle.dailyRate * offer.discountValue
      }
    }

    const { applied, total: pricingRulesTotal } = await applyPricingRules(
      company.id,
      customer.id,
      body.additionalDrivers as any[],
      vehicle.dailyRate,
      totalDays,
    )

    const primaryLicenseResult = validateLicense(body.licenseExpiry ? new Date(body.licenseExpiry) : null)
    if (primaryLicenseResult.status === 'EXPIRED') {
      return res.status(400).json({ error: 'license_expired', message: 'The primary driver license is expired', statusCode: 400 })
    }

    const reservation = await prisma.reservation.create({
      data: {
        companyId: company.id,
        vehicleId: vehicle.id,
        customerId: customer.id,
        offerId: body.offerId ?? null,
        promoCodeUsed: body.promoCodeUsed ?? null,
        source: body.source,
        startDate: start,
        endDate: end,
        dailyRate: vehicle.dailyRate,
        totalDays,
        totalAmount: baseAmount - discountAmount + pricingRulesTotal,
        discountAmount,
        pricingRulesApplied: applied,
        pricingRulesTotal,
        notes: body.notes ?? null,
        status: 'DRAFT',
      },
    })

    if (body.selectedInsurancePolicyIds.length > 0) {
      await applyInsurancesToReservation(reservation.id, company.id, body.selectedInsurancePolicyIds, totalDays, baseAmount)
    }

    if (body.additionalDrivers.length > 0) {
      await applyAdditionalDriversToReservation(reservation.id, company.id, body.additionalDrivers, totalDays)
    }

    if (body.licenseExpiry) {
      await validateAndFlagLicense(customer.id).catch(() => null)
    }

    const refreshedReservation = await prisma.reservation.findUniqueOrThrow({
      where: { id: reservation.id },
      include: { insurances: true, additionalDrivers: true },
    })

    res.status(201).json({
      data: {
        ...refreshedReservation,
        requiresManualApproval:
          primaryLicenseResult.requiresApproval ||
          refreshedReservation.additionalDrivers.some((driver) => driver.requiresApproval),
      },
    })
  } catch (err) {
    if (isDatabaseUnavailableError(err)) {
      return res.status(503).json({ error: 'database_unavailable', message: 'Booking is temporarily unavailable', statusCode: 503 })
    }
    next(err)
  }
})

router.get('/:slug/booking/:id', async (req, res, next) => {
  try {
    const company = await findCompanyBySlug(req.params.slug)
    const reservation = await prisma.reservation.findFirstOrThrow({
      where: { id: req.params.id, companyId: company.id },
      include: { vehicle: true, customer: true },
    })
    res.json({ data: reservation })
  } catch (err) {
    if (isDatabaseUnavailableError(err)) {
      return res.status(503).json({ error: 'database_unavailable', message: 'Booking details are temporarily unavailable', statusCode: 503 })
    }
    next(err)
  }
})

router.post('/:slug/booking/:id/pay', async (req, res, next) => {
  try {
    const company = await findCompanyBySlug(req.params.slug)
    const reservation = await prisma.reservation.findFirstOrThrow({
      where: { id: req.params.id, companyId: company.id },
      include: { vehicle: true, customer: true, additionalDrivers: true },
    })

    if (reservation.paymentStatus === 'PAID') {
      return res.status(409).json({ error: 'already_paid', message: 'This reservation is already paid', statusCode: 409 })
    }

    const customerLicenseResult = validateLicense(reservation.customer.licenseExpiry)
    if (
      reservation.customer.licenseValidationStatus === 'DENIED' ||
      customerLicenseResult.status === 'EXPIRED' ||
      (customerLicenseResult.requiresApproval && reservation.customer.licenseValidationStatus !== 'APPROVED') ||
      reservation.additionalDrivers.some((driver) => driver.licenseExpired || (driver.requiresApproval && !driver.approvedAt))
    ) {
      return res.status(409).json({
        error: 'license_review_required',
        message: 'This reservation requires license review before payment can be processed',
        statusCode: 409,
      })
    }

    const { provider, currency, successUrl, failureUrl } = z.object({
      provider: z.enum(['AMANPAY', 'PAYPAL']),
      currency: z.enum(['MAD', 'USD', 'EUR']).default('MAD'),
      successUrl: z.string().url(),
      failureUrl: z.string().url(),
    }).parse(req.body)

    const amount = reservation.totalAmount
    const description = `Rental: ${reservation.vehicle.make} ${reservation.vehicle.model}`
    const orderId = `res-${reservation.id}-${Date.now()}`
    const webhookBase = process.env.API_URL ?? 'http://localhost:4000'

    let checkoutUrl: string
    let amanpayTransactionId: string | null = null
    let paypalCaptureId: string | null = null

    if (provider === 'AMANPAY') {
      if (!amanpay.isConfigured()) {
        return res.status(503).json({ error: 'provider_not_configured', message: 'Online payment is not available for this company', statusCode: 503 })
      }
      const brand = company.brand as any
      const merchantId = brand?.amanpayMerchantId ?? process.env.AMANPAY_MERCHANT_ID ?? ''
      const secretKey = brand?.amanpaySecretKey ?? process.env.AMANPAY_SECRET_KEY ?? ''
      if (!merchantId || !secretKey) {
        return res.status(503).json({ error: 'provider_not_configured', message: 'AmanPay is not configured for this company', statusCode: 503 })
      }
      const result = await amanpay.createCheckout({
        amount,
        currency,
        orderId,
        description,
        customerEmail: reservation.customer.email,
        customerName: `${reservation.customer.firstName} ${reservation.customer.lastName}`,
        successUrl,
        failureUrl,
        webhookUrl: `${webhookBase}/api/v1/payments/webhooks/amanpay`,
      })
      checkoutUrl = result.checkoutUrl
      amanpayTransactionId = result.transactionId
    } else {
      if (!paypal.isConfigured()) {
        return res.status(503).json({ error: 'provider_not_configured', message: 'PayPal is not available for this company', statusCode: 503 })
      }
      const result = await paypal.createOrder({
        amount,
        currency,
        orderId,
        description,
        returnUrl: successUrl,
        cancelUrl: failureUrl,
      })
      checkoutUrl = result.approveUrl
      paypalCaptureId = result.orderId
    }

    await prisma.rentalPayment.create({
      data: {
        companyId: company.id,
        reservationId: reservation.id,
        amount,
        currency,
        status: 'PENDING',
        type: 'CHARGE',
        paymentProvider: provider,
        amanpayTransactionId,
        paypalCaptureId,
      },
    })

    res.json({ data: { checkoutUrl } })
  } catch (err) {
    if (isDatabaseUnavailableError(err)) {
      return res.status(503).json({ error: 'database_unavailable', message: 'Payment initiation is temporarily unavailable', statusCode: 503 })
    }
    next(err)
  }
})

router.post('/:slug/booking/:id/capture-paypal', async (req, res, next) => {
  try {
    const company = await findCompanyBySlug(req.params.slug)
    const { paypalOrderId } = z.object({ paypalOrderId: z.string() }).parse(req.body)

    const payment = await prisma.rentalPayment.findFirstOrThrow({
      where: { paypalCaptureId: paypalOrderId, companyId: company.id },
    })

    const capture = await paypal.captureOrder(paypalOrderId) as Record<string, any>
    const captureId = capture.purchase_units?.[0]?.payments?.captures?.[0]?.id ?? paypalOrderId

    await prisma.rentalPayment.update({
      where: { id: payment.id },
      data: { status: 'SUCCEEDED', paidAt: new Date(), paypalCaptureId: captureId },
    })
    await prisma.reservation.update({
      where: { id: payment.reservationId },
      data: { paymentStatus: 'PAID', paidAmount: { increment: payment.amount } },
    })

    res.json({ data: { success: true } })
  } catch (err) { next(err) }
})

router.post('/:slug/contact', async (req, res, next) => {
  try {
    const company = await findCompanyBySlug(req.params.slug)
    const body = z.object({
      name: z.string().min(1),
      email: z.string().email(),
      message: z.string().min(1),
    }).parse(req.body)
    res.json({
      data: {
        success: true,
        deliveredTo: company.brand?.publicEmail ?? company.email,
        preview: body,
      },
    })
  } catch (err) { next(err) }
})

export default router
