import { Router } from 'express'
import { z } from 'zod'
import { requireCompanyAuth } from '../../middleware/requireCompanyAuth'
import { requireTenant } from '../../middleware/requireTenant'
import { requireSubscription } from '../../middleware/requireSubscription'
import { parseBody, parseQuery, parseParams } from '../../http/validate'
import { ok, created } from '../../http/respond'
import { imageUpload, assertImageFile } from '../../http/upload'
import * as service from './reservation.service'
import * as lifecycle from './reservation.lifecycle.service'
import * as inspectionService from './reservation.inspection.service'
import * as additionalDriverService from './reservation.additional-driver.service'
import * as photoService from './reservation.photo.service'
import { getContract, getBilling } from './reservation.document.service'
import {
  createSchema, updateSchema, listQuerySchema,
  checkinSchema, checkoutSchema, extendSchema, cancelSchema, approvalSchema,
  inspectionSchema, inspectionTypeParam,
  idParamSchema, driverParamSchema, inspectionParamSchema,
} from './reservation.schemas'

const photoTypeSchema = z.enum(['PICKUP', 'DROPOFF'])
const photoParamSchema = z.object({ id: z.string(), photoId: z.string() })

const router = Router()
router.use(requireCompanyAuth, requireTenant, requireSubscription)

router.get('/', async (req, res, next) => {
  try {
    const query = parseQuery(listQuerySchema, req)
    const result = await service.listReservations(req.companyId, query)
    res.json(result)
  } catch (err) { next(err) }
})

router.post('/', async (req, res, next) => {
  try {
    const body = parseBody(createSchema, req)
    const reservation = await service.createReservation(req.companyId, body)
    created(res, reservation)
  } catch (err) { next(err) }
})

router.get('/:id', async (req, res, next) => {
  try {
    const { id } = parseParams(idParamSchema, req)
    const reservation = await service.getReservation(id, req.companyId)
    ok(res, reservation)
  } catch (err) { next(err) }
})

router.patch('/:id', async (req, res, next) => {
  try {
    const { id } = parseParams(idParamSchema, req)
    const body = parseBody(updateSchema, req)
    const reservation = await service.updateReservation(id, req.companyId, body)
    ok(res, reservation)
  } catch (err) { next(err) }
})

router.get('/:id/contract', async (req, res, next) => {
  try {
    const { id } = parseParams(idParamSchema, req)
    const contract = await getContract(id, req.companyId)
    ok(res, contract)
  } catch (err) { next(err) }
})

router.get('/:id/billing', async (req, res, next) => {
  try {
    const { id } = parseParams(idParamSchema, req)
    const billing = await getBilling(id, req.companyId)
    ok(res, billing)
  } catch (err) { next(err) }
})

router.post('/:id/confirm', async (req, res, next) => {
  try {
    const { id } = parseParams(idParamSchema, req)
    const updated = await lifecycle.confirmReservation(id, req.companyId)
    ok(res, updated)
  } catch (err) { next(err) }
})

router.post('/:id/checkin', async (req, res, next) => {
  try {
    const { id } = parseParams(idParamSchema, req)
    const { mileage } = parseBody(checkinSchema, req)
    const updated = await lifecycle.checkinReservation(id, req.companyId, mileage)
    ok(res, updated)
  } catch (err) { next(err) }
})

router.post('/:id/checkout', async (req, res, next) => {
  try {
    const { id } = parseParams(idParamSchema, req)
    const { mileage } = parseBody(checkoutSchema, req)
    const updated = await lifecycle.checkoutReservation(id, req.companyId, mileage)
    ok(res, updated)
  } catch (err) { next(err) }
})

router.post('/:id/close', async (req, res, next) => {
  try {
    const { id } = parseParams(idParamSchema, req)
    const closedBy = `${req.employee.firstName} ${req.employee.lastName}`
    const reservation = await lifecycle.closeReservation(id, req.companyId, closedBy)
    ok(res, reservation)
  } catch (err) { next(err) }
})

router.post('/:id/extend', async (req, res, next) => {
  try {
    const { id } = parseParams(idParamSchema, req)
    const { newEndDate, reason } = parseBody(extendSchema, req)
    ok(res, await lifecycle.extendReservation(id, req.companyId, new Date(newEndDate), reason))
  } catch (err) { next(err) }
})

router.post('/:id/cancel', async (req, res, next) => {
  try {
    const { id } = parseParams(idParamSchema, req)
    const { reason } = parseBody(cancelSchema, req)
    const updated = await lifecycle.cancelReservation(id, req.companyId, reason)
    ok(res, updated)
  } catch (err) { next(err) }
})

router.get('/:id/inspections', async (req, res, next) => {
  try {
    const { id } = parseParams(idParamSchema, req)
    const inspections = await inspectionService.getInspections(id, req.companyId)
    ok(res, inspections)
  } catch (err) { next(err) }
})

router.put('/:id/inspections/:type', async (req, res, next) => {
  try {
    const { id, type: rawType } = parseParams(inspectionParamSchema, req)
    const type = inspectionTypeParam.parse(rawType.toUpperCase())
    const body = parseBody(inspectionSchema, req)
    const inspectedBy = `${req.employee.firstName} ${req.employee.lastName}`
    const result = await inspectionService.upsertInspection(id, req.companyId, type, body, inspectedBy)
    ok(res, result)
  } catch (err) { next(err) }
})

router.patch('/:id/additional-drivers/:driverId/approval', async (req, res, next) => {
  try {
    const { id, driverId } = parseParams(driverParamSchema, req)
    const { approved, note } = parseBody(approvalSchema, req)
    const approvedBy = `${req.employee.firstName} ${req.employee.lastName}`
    const updated = await additionalDriverService.approveAdditionalDriver(id, driverId, req.companyId, approved, note, approvedBy)
    ok(res, updated)
  } catch (err) { next(err) }
})

router.get('/:id/photos', async (req, res, next) => {
  try {
    const { id } = parseParams(idParamSchema, req)
    ok(res, await photoService.listPhotos(id, req.companyId))
  } catch (err) { next(err) }
})

router.post('/:id/photos', imageUpload.single('photo'), async (req, res, next) => {
  try {
    const { id } = parseParams(idParamSchema, req)
    assertImageFile(req.file, 'photo')
    const type = photoTypeSchema.parse(req.body.type)
    const photo = await photoService.uploadPhoto(id, req.companyId, type, req.file.buffer)
    created(res, photo)
  } catch (err) { next(err) }
})

router.delete('/:id/photos/:photoId', async (req, res, next) => {
  try {
    const { id, photoId } = parseParams(photoParamSchema, req)
    await photoService.deletePhoto(photoId, id, req.companyId)
    ok(res, { deleted: true })
  } catch (err) { next(err) }
})

export default router
