import { createHash } from 'node:crypto'
import { Router } from 'express'
import { optionalRenterAuth } from '../../middleware/requireRenterAuth'
import { parseBody, parseParams, parseQuery } from '../../http/validate'
import { created, ok } from '../../http/respond'
import { isDatabaseUnavailableError } from '../../lib/isDatabaseUnavailable'
import * as service from './carplace.service'
import {
  carplaceQuoteSchema,
  companiesQuerySchema,
  carplaceFunnelEventSchema,
  offerCodeParamSchema,
  reviewBodySchema,
  reviewTokenSchema,
  paginationSchema,
  searchSchema,
  slugParamSchema,
  carplaceReservationSchema,
  vehicleAvailabilityQuerySchema,
  vehicleParamSchema,
} from './carplace.schemas'

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

const idempotencyCache = new Map<string, { expiresAt: number; fingerprint: string; result: unknown }>()
const IDEMPOTENCY_TTL_MS = 15 * 60 * 1000

function cleanupIdempotencyCache(now = Date.now()) {
  for (const [key, value] of idempotencyCache) if (value.expiresAt <= now) idempotencyCache.delete(key)
}

router.get('/home', async (_req, res, next) => {
  try {
    const [cities, offers, companies, search] = await Promise.all([
      service.getCities(),
      service.getPublicOffers(),
      service.getListedCompanies({ page: 1, pageSize: 8 }),
      service.searchVehiclesPage({ page: 1, pageSize: 9 }),
    ])
    ok(res, { cities, offers: offers.slice(0, 6), companies, vehicles: search.items })
  } catch (error) {
    if (isDatabaseUnavailableError(error)) return ok(res, { cities: [], offers: [], companies: [], vehicles: [] })
    next(error)
  }
})

router.get('/cities', async (_req, res, next) => {
  try { ok(res, await service.getCities()) }
  catch (error) { if (isDatabaseUnavailableError(error)) return ok(res, []); next(error) }
})

router.get('/offers', async (_req, res, next) => {
  try { ok(res, await service.getPublicOffers()) }
  catch (error) { if (isDatabaseUnavailableError(error)) return ok(res, []); next(error) }
})

router.get('/companies', async (req, res, next) => {
  try {
    const filters = parseQuery(companiesQuerySchema, req)
    const pagination = parseQuery(paginationSchema, req)
    ok(res, await service.getListedCompanies({ ...filters, ...pagination }))
  } catch (error) { if (isDatabaseUnavailableError(error)) return ok(res, []); next(error) }
})

router.get('/search', async (req, res, next) => {
  try {
    const filters = parseQuery(searchSchema, req)
    const pagination = parseQuery(paginationSchema, req)
    ok(res, await service.searchVehiclesPage({ ...filters, ...pagination }))
  } catch (error) {
    if (isDatabaseUnavailableError(error)) return ok(res, { items: [], pagination: { page: 1, pageSize: 20, totalItems: 0, totalPages: 0 }, facets: { categories: [], transmissions: [], makes: [], price: { min: null, max: null } } })
    next(error)
  }
})

router.post('/quotes', async (req, res, next) => {
  try { ok(res, await service.createCarplaceQuote(parseBody(carplaceQuoteSchema, req))) }
  catch (error) { next(error) }
})

router.post('/reservations', async (req, res, next) => {
  try {
    cleanupIdempotencyCache()
    const body = parseBody(carplaceReservationSchema, req)
    const key = body.idempotencyKey ?? req.header('Idempotency-Key')?.trim()
    const fingerprint = createHash('sha256').update(JSON.stringify(body)).digest('hex')
    if (key) {
      const cached = idempotencyCache.get(key)
      if (cached && cached.expiresAt > Date.now()) {
        if (cached.fingerprint !== fingerprint) return res.status(409).json({ error: 'idempotency_conflict', message: 'This idempotency key was already used with a different request', statusCode: 409 })
        return ok(res, cached.result)
      }
    }
    const result = await service.createCarplaceReservation(body)
    if (key) idempotencyCache.set(key, { expiresAt: Date.now() + IDEMPOTENCY_TTL_MS, fingerprint, result })
    created(res, result)
  } catch (error) {
    if (isDatabaseUnavailableError(error)) return res.status(503).json({ error: 'database_unavailable', message: 'Service temporarily unavailable', statusCode: 503 })
    next(error)
  }
})

router.post('/events', async (req, res, next) => {
  try { ok(res, await service.trackCarplaceFunnelEvent(parseBody(carplaceFunnelEventSchema, req), req.renterId)) }
  catch (error) { if (isDatabaseUnavailableError(error)) return ok(res, { success: false }); next(error) }
})

router.get('/review/:token', async (req, res, next) => {
  try {
    const { token } = parseParams(reviewTokenSchema, req)
    ok(res, await service.getReviewContext(token))
  } catch (error) { next(error) }
})

router.post('/review/:token', async (req, res, next) => {
  try {
    const { token } = parseParams(reviewTokenSchema, req)
    created(res, await service.submitReview(token, parseBody(reviewBodySchema, req)))
  } catch (error) { next(error) }
})

router.post('/offers/:code/validate', async (req, res, next) => {
  try {
    const { code } = parseParams(offerCodeParamSchema, req)
    ok(res, await service.validateOfferCode(code))
  } catch (error) { next(error) }
})

router.get('/:slug', async (req, res, next) => {
  try { const { slug } = parseParams(slugParamSchema, req); ok(res, await service.getCompanyPage(slug)) }
  catch (error) { next(error) }
})

router.get('/:slug/reviews', async (req, res, next) => {
  try { const { slug } = parseParams(slugParamSchema, req); ok(res, await service.getCompanyReviews(slug)) }
  catch (error) { if (isDatabaseUnavailableError(error)) return ok(res, []); next(error) }
})


router.get('/:slug/vehicles', async (req, res, next) => {
  try { const { slug } = parseParams(slugParamSchema, req); ok(res, await service.getCompanyVehicles(slug)) }
  catch (error) { next(error) }
})

router.get('/:slug/offers', async (req, res, next) => {
  try { const { slug } = parseParams(slugParamSchema, req); ok(res, await service.getCompanyOffers(slug)) }
  catch (error) { next(error) }
})

router.get('/:slug/vehicles/:id', async (req, res, next) => {
  try { const { slug, id } = parseParams(vehicleParamSchema, req); ok(res, await service.getVehicleDetail(slug, id)) }
  catch (error) { next(error) }
})

router.get('/:slug/vehicles/:id/availability', async (req, res, next) => {
  try {
    const { slug, id } = parseParams(vehicleParamSchema, req)
    const range = parseQuery(vehicleAvailabilityQuerySchema, req)
    ok(res, await service.getCarplaceVehicleAvailability(slug, id, range))
  } catch (error) { next(error) }
})

export default router
