import { Router } from 'express'
import { z } from 'zod'
import { prisma } from '../lib/prisma'
import { optionalRenterAuth } from '../middleware/requireRenterAuth'
import { sendNotification, sendTransactionalEmail } from '../services/notificationService'
import { getVehicleAvailabilitySummary } from '../services/vehicleAvailabilityService'

const router = Router()
router.use(optionalRenterAuth)

const paginationSchema = z.object({
  page:     z.coerce.number().int().min(1).max(10000).default(1),
  pageSize: z.coerce.number().int().min(1).max(100).default(20),
})

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
}

router.get('/offers', async (req, res, next) => {
  try {
    const offers = await prisma.offer.findMany({
      where: { isPublic: true, isActive: true, validFrom: { lte: new Date() }, validUntil: { gte: new Date() } },
      include: { company: { include: { brand: { select: { displayName: true, logoUrl: true, subdomain: true, primaryColor: true } } } } },
      orderBy: [{ isFeatured: 'desc' }, { createdAt: 'desc' }],
      take: 50,
    })
    res.json({ data: offers })
  } catch (err) {
    if (isDatabaseUnavailableError(err)) return res.json({ data: [] })
    next(err)
  }
})

router.get('/companies', async (req, res, next) => {
  try {
    const { city, hasOffer } = req.query as Record<string, string>
    const { page, pageSize } = paginationSchema.parse(req.query)
    const safeCity = city ? city.trim().slice(0, 100) : undefined
    const where: any = { status: { in: ['ACTIVE', 'TRIALING'] }, brand: { isListedOnMarketplace: true } }
    if (safeCity) where.brand = { ...where.brand, publicCity: { contains: safeCity, mode: 'insensitive' } }
    if (hasOffer === 'true') {
      where.offers = { some: { isPublic: true, isActive: true, validUntil: { gte: new Date() } } }
    }

    const companies = await prisma.company.findMany({
      where,
      include: {
        brand: { select: { displayName: true, logoUrl: true, subdomain: true, publicCity: true, publicCountry: true, marketplaceRating: true, primaryColor: true } },
        _count: { select: { vehicles: { where: { isPublished: true } } } },
      },
      skip: (page - 1) * pageSize,
      take: pageSize,
    })
    res.json({ data: companies })
  } catch (err) {
    if (isDatabaseUnavailableError(err)) return res.json({ data: [] })
    next(err)
  }
})

router.get('/search', async (req, res, next) => {
  try {
    const searchSchema = z.object({
      city:         z.string().trim().max(100).optional(),
      startDate:    z.string().datetime().optional(),
      endDate:      z.string().datetime().optional(),
      category:     z.string().trim().max(50).optional(),
      maxPrice:     z.coerce.number().int().min(0).max(1_000_000).optional(),
      transmission: z.string().trim().max(20).optional(),
      make:         z.string().trim().max(60).optional(),
      model:        z.string().trim().max(60).optional(),
    })
    const { city, startDate, endDate, category, maxPrice, transmission, make, model } = searchSchema.parse(req.query)
    const { page, pageSize } = paginationSchema.parse(req.query)

    const where: any = { isPublished: true, company: { status: { in: ['ACTIVE', 'TRIALING'] }, brand: { isListedOnMarketplace: true } } }
    if (category) where.category = category
    if (maxPrice !== undefined) where.dailyRate = { lte: maxPrice }
    if (transmission) where.transmission = transmission
    if (make) where.make = { contains: make, mode: 'insensitive' }
    if (model) where.model = { contains: model, mode: 'insensitive' }
    if (city) where.company = { ...where.company, brand: { isListedOnMarketplace: true, publicCity: { contains: city, mode: 'insensitive' } } }

    const vehicles = await prisma.vehicle.findMany({
      where,
      include: {
        company: { include: { brand: { select: { displayName: true, logoUrl: true, subdomain: true, marketplaceRating: true, primaryColor: true } } } },
      },
      skip: (page - 1) * pageSize,
      take: pageSize,
      orderBy: { dailyRate: 'asc' },
    })

    const typedVehicles = vehicles as Array<(typeof vehicles)[number]>

    const availabilityByVehicleId = new Map(
      await Promise.all(
        typedVehicles.map(async (vehicle) => {
          const availability = await getVehicleAvailabilitySummary(vehicle.id, {
            range: startDate && endDate
              ? { startDate: new Date(startDate), endDate: new Date(endDate) }
              : undefined,
          })
          return [vehicle.id, availability] as const
        }),
      ),
    )

    res.json({
      data: typedVehicles.map((vehicle) => ({
        ...vehicle,
        availability: availabilityByVehicleId.get(vehicle.id)?.available ?? null,
        availabilityStatus: availabilityByVehicleId.get(vehicle.id)?.status ?? 'AVAILABLE',
        nextAvailableAt: availabilityByVehicleId.get(vehicle.id)?.nextAvailableAt ?? null,
      })),
    })
  } catch (err) {
    if (isDatabaseUnavailableError(err)) return res.json({ data: [] })
    next(err)
  }
})

router.post('/reservations', async (req, res, next) => {
  try {
    const schema = z.object({
      vehicleId:  z.string().cuid(),
      companySlug: z.string().trim().max(100),
      firstName:  z.string().min(1).max(100),
      lastName:   z.string().min(1).max(100),
      email:      z.string().email(),
      phone:      z.string().max(30).optional(),
      startDate:  z.string().datetime(),
      endDate:    z.string().datetime(),
      notes:      z.string().max(500).optional(),
    })

    const body = schema.parse(req.body)
    const startDate = new Date(body.startDate)
    const endDate = new Date(body.endDate)

    if (endDate <= startDate) {
      return res.status(400).json({ error: 'invalid_dates', message: 'End date must be after start date', statusCode: 400 })
    }

    const vehicle = await prisma.vehicle.findFirst({
      where: { id: body.vehicleId, isPublished: true, company: { slug: body.companySlug, status: { in: ['ACTIVE', 'TRIALING'] } } },
      include: { company: { include: { brand: true } } },
    })
    if (!vehicle) return res.status(404).json({ error: 'not_found', message: 'Vehicle not found', statusCode: 404 })

    const availability = await getVehicleAvailabilitySummary(body.vehicleId, {
      range: { startDate, endDate },
    })
    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: vehicle.companyId, email: body.email } },
      create: { companyId: vehicle.companyId, firstName: body.firstName, lastName: body.lastName, email: body.email, phone: body.phone },
      update: { firstName: body.firstName, lastName: body.lastName, ...(body.phone ? { phone: body.phone } : {}) },
    })

    const totalDays = Math.max(1, Math.ceil((endDate.getTime() - startDate.getTime()) / 86_400_000))
    const totalAmount = vehicle.dailyRate * totalDays

    const reservation = await prisma.reservation.create({
      data: {
        companyId: vehicle.companyId,
        vehicleId: body.vehicleId,
        customerId: customer.id,
        source: 'MARKETPLACE',
        status: 'DRAFT',
        startDate,
        endDate,
        dailyRate: vehicle.dailyRate,
        totalDays,
        totalAmount,
        notes: body.notes,
      },
    })

    const companyName = vehicle.company.brand?.displayName ?? vehicle.company.name
    const rateDisplay = (vehicle.dailyRate / 100).toFixed(2)
    const totalDisplay = (totalAmount / 100).toFixed(2)
    const dateOpts: Intl.DateTimeFormatOptions = { year: 'numeric', month: 'long', day: 'numeric' }
    const startStr = startDate.toLocaleDateString('en-GB', dateOpts)
    const endStr = endDate.toLocaleDateString('en-GB', dateOpts)

    try {
      await sendTransactionalEmail({
        to: body.email,
        subject: `Reservation Request Received — ${vehicle.make} ${vehicle.model}`,
        html: `
          <div style="font-family:sans-serif;max-width:560px;margin:auto;padding:32px;color:#1c1917">
            <h2 style="margin-bottom:4px">Hi ${body.firstName},</h2>
            <p style="color:#57534e">Your reservation request has been received and is pending company confirmation.</p>
            <hr style="border:none;border-top:1px solid #e7e5e4;margin:24px 0"/>
            <h3 style="margin-bottom:12px">${vehicle.year} ${vehicle.make} ${vehicle.model}</h3>
            <p><strong>Company:</strong> ${companyName}</p>
            <p><strong>Pick-up date:</strong> ${startStr}</p>
            <p><strong>Return date:</strong> ${endStr}</p>
            <p><strong>Duration:</strong> ${totalDays} day${totalDays > 1 ? 's' : ''}</p>
            <p><strong>Daily rate:</strong> ${rateDisplay} MAD</p>
            <p><strong>Estimated total:</strong> ${totalDisplay} MAD</p>
            <hr style="border:none;border-top:1px solid #e7e5e4;margin:24px 0"/>
            <p style="color:#57534e;font-size:13px">The company will review your request and get in touch with you shortly. You may be contacted at ${body.email}${body.phone ? ` or ${body.phone}` : ''}.</p>
          </div>
        `,
        text: `Hi ${body.firstName},\n\nYour reservation request for the ${vehicle.year} ${vehicle.make} ${vehicle.model} from ${companyName} has been received.\n\nDates: ${startStr} → ${endStr}\nDuration: ${totalDays} day(s)\nDaily rate: ${rateDisplay} MAD\nEstimated total: ${totalDisplay} MAD\n\nThe company will confirm your request shortly.\n\nThank you!`,
      })
    } catch {
      // email failure is non-blocking — reservation still created
    }

    try {
      await sendNotification({
        type: 'NEW_BOOKING',
        title: 'New Marketplace Reservation Request',
        body: `${body.firstName} ${body.lastName} requested ${vehicle.make} ${vehicle.model} from ${startStr} to ${endStr}.`,
        companyId: vehicle.companyId,
        channels: ['IN_APP'],
      })
    } catch {
      // notification failure is non-blocking
    }

    res.status(201).json({ data: { reservationId: reservation.id } })
  } catch (err) {
    if (isDatabaseUnavailableError(err)) {
      return res.status(503).json({ error: 'database_unavailable', message: 'Service temporarily unavailable', statusCode: 503 })
    }
    next(err)
  }
})

router.get('/:slug', async (req, res, next) => {
  try {
    const company = await prisma.company.findFirst({
      where: { slug: req.params.slug, status: { in: ['ACTIVE', 'TRIALING'] } },
      include: {
        brand: true,
        vehicles: { where: { isPublished: true }, orderBy: { createdAt: 'desc' } },
        offers:   { where: { isPublic: true, isActive: true, validUntil: { gte: new Date() } }, orderBy: { isFeatured: 'desc' } },
      },
    })
    if (!company) return res.status(404).json({ error: 'not_found', message: 'Company not found', statusCode: 404 })
    const vehicles = await Promise.all(
      company.vehicles.map(async (vehicle) => {
        const availability = await getVehicleAvailabilitySummary(vehicle.id)
        return {
          ...vehicle,
          availability: availability.available,
          availabilityStatus: availability.status,
          nextAvailableAt: availability.nextAvailableAt,
        }
      }),
    )
    res.json({ data: { ...company, vehicles } })
  } catch (err) {
    if (isDatabaseUnavailableError(err)) {
      return res.status(503).json({ error: 'database_unavailable', message: 'Marketplace data is temporarily unavailable', statusCode: 503 })
    }
    next(err)
  }
})

router.get('/:slug/reviews', async (req, res, next) => {
  try {
    const company = await prisma.company.findFirstOrThrow({ where: { slug: req.params.slug } })
    const reviews = await prisma.review.findMany({
      where: { companyId: company.id, isPublished: true },
      include: { renter: { select: { firstName: true, lastName: true } } },
      orderBy: { createdAt: 'desc' },
      take: 50,
    })
    res.json({ data: reviews })
  } catch (err) {
    if (isDatabaseUnavailableError(err)) return res.json({ data: [] })
    next(err)
  }
})

router.get('/:slug/vehicles', async (req, res, next) => {
  try {
    const company = await prisma.company.findFirstOrThrow({ where: { slug: req.params.slug, status: { in: ['ACTIVE', 'TRIALING'] } } })
    const vehicles = await prisma.vehicle.findMany({
      where: { companyId: company.id, isPublished: true },
      orderBy: { createdAt: 'desc' },
    })
    res.json({ data: vehicles })
  } catch (err) {
    if (isDatabaseUnavailableError(err)) return res.json({ data: [] })
    next(err)
  }
})

router.get('/:slug/vehicles/:id', async (req, res, next) => {
  try {
    const company = await prisma.company.findFirstOrThrow({ where: { slug: req.params.slug, status: { in: ['ACTIVE', 'TRIALING'] } } })
    const vehicle = await prisma.vehicle.findFirstOrThrow({
      where: { id: req.params.id, companyId: company.id, isPublished: true },
      include: {
        company: { include: { brand: true } },
      },
    })
    const availability = await getVehicleAvailabilitySummary(vehicle.id)
    res.json({ data: { ...vehicle, availabilityStatus: availability.status, availability: availability.available, 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 prisma.company.findFirstOrThrow({ where: { slug: req.params.slug, status: { in: ['ACTIVE', 'TRIALING'] } } })
    const offers = await prisma.offer.findMany({
      where: {
        companyId: company.id,
        isPublic: true,
        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.post('/offers/:code/validate', async (req, res, next) => {
  try {
    const offer = await prisma.offer.findFirst({
      where: { promoCode: req.params.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 })
    if (offer.maxRedemptions && offer.redemptionCount >= offer.maxRedemptions) {
      return res.status(409).json({ error: 'code_exhausted', message: 'Promo code has reached its maximum redemptions', statusCode: 409 })
    }
    res.json({ data: offer })
  } catch (err) {
    if (isDatabaseUnavailableError(err)) {
      return res.status(503).json({ error: 'database_unavailable', message: 'Offer validation is temporarily unavailable', statusCode: 503 })
    }
    next(err)
  }
})

export default router
