import { HttpStatusCode } from '@peertube/peertube-models'
import { isIdValid } from '@server/helpers/custom-validators/misc.js'
import { EventModel } from '@server/models/video/event.js'
import { VideoChannelModel } from '@server/models/video/video-channel.js'
import express from 'express'
import { body, param } from 'express-validator'
import { areValidationErrors } from './shared/utils.js'

export const createEventValidator = [
  body('title')
    .trim()
    .isLength({ min: 1, max: 255 })
    .withMessage('Event title must be between 1 and 255 characters'),

  body('description')
    .optional()
    .trim()
    .isLength({ max: 5000 })
    .withMessage('Event description must not exceed 5000 characters'),

  body('startTime')
    .isISO8601()
    .withMessage('Start time must be a valid ISO 8601 date'),

  body('endTime')
    .isISO8601()
    .withMessage('End time must be a valid ISO 8601 date'),

  body('state')
    .optional()
    .isIn([ 'active', 'disabled' ])
    .withMessage('Invalid event state'),

  body('liveUrl')
    .optional({ checkFalsy: true})
    .isLength({ max: 2048 })
    .withMessage('Live URL must not exceed 2048 characters')
    .bail()
    .isURL()
    .withMessage('Live URL must be a valid URL'),

  async (req: express.Request, res: express.Response, next: express.NextFunction) => {
    if (areValidationErrors(req, res)) return

    // Validate end time is after start time
    const startTime = new Date(req.body.startTime)
    const endTime = new Date(req.body.endTime)

    if (endTime <= startTime) {
      return res.fail({
        status: HttpStatusCode.UNPROCESSABLE_ENTITY_422,
        message: 'End time must be after start time'
      })
    }

    // Validate channel exists
    const { channelId } = req.params
    const channel = await VideoChannelModel.scope([ 'WITH_ACCOUNT' ]).findByPk(channelId)

    if (!channel) {
      return res.fail({
        status: HttpStatusCode.NOT_FOUND_404,
        message: 'Channel not found'
      })
    }

    const resLocals = res.locals as any
    resLocals.channel = channel

    return next()
  }
]

export const updateEventValidator = [
  param('eventId')
    .custom(isIdValid)
    .withMessage('Invalid event id'),

  body('title')
    .optional()
    .trim()
    .isLength({ min: 1, max: 255 })
    .withMessage('Event title must be between 1 and 255 characters'),

  body('description')
    .optional()
    .trim()
    .isLength({ max: 5000 })
    .withMessage('Event description must not exceed 5000 characters'),

  body('startTime')
    .optional()
    .isISO8601()
    .withMessage('Start time must be a valid ISO 8601 date'),

  body('endTime')
    .optional()
    .isISO8601()
    .withMessage('End time must be a valid ISO 8601 date'),

  body('state')
    .optional()
    .isIn([ 'active', 'disabled' ])
    .withMessage('Invalid event state'),

  body('liveUrl')
    .optional({ checkFalsy: true})
    .isLength({ max: 2048 })
    .withMessage('Live URL must not exceed 2048 characters')
    .bail()
    .isURL()
    .withMessage('Live URL must be a valid URL'),

  async (req: express.Request, res: express.Response, next: express.NextFunction) => {
    if (areValidationErrors(req, res)) return

    // Validate time range if both provided
    if (req.body.startTime && req.body.endTime) {
      const startTime = new Date(req.body.startTime)
      const endTime = new Date(req.body.endTime)

      if (endTime <= startTime) {
        return res.fail({
          status: HttpStatusCode.UNPROCESSABLE_ENTITY_422,
          message: 'End time must be after start time'
        })
      }
    }

    // Validate channel exists
    const { channelId, eventId } = req.params
    const channel = await VideoChannelModel.scope([ 'WITH_ACCOUNT' ]).findByPk(channelId)

    if (!channel) {
      return res.fail({
        status: HttpStatusCode.NOT_FOUND_404,
        message: 'Channel not found'
      })
    }

    // Validate event exists and belongs to channel
    const event = await EventModel.findByIdAndChannelId(parseInt(eventId), parseInt(channelId))

    if (!event) {
      return res.fail({
        status: HttpStatusCode.NOT_FOUND_404,
        message: 'Event not found'
      })
    }

    const resLocals = res.locals as any
    resLocals.channel = channel
    resLocals.event = event

    return next()
  }
]

export const deleteEventValidator = [
  param('eventId')
    .custom(isIdValid)
    .withMessage('Invalid event id'),

  async (req: express.Request, res: express.Response, next: express.NextFunction) => {
    if (areValidationErrors(req, res)) return

    const { channelId, eventId } = req.params
    const channel = await VideoChannelModel.scope([ 'WITH_ACCOUNT' ]).findByPk(channelId)

    if (!channel) {
      return res.fail({
        status: HttpStatusCode.NOT_FOUND_404,
        message: 'Channel not found'
      })
    }

    const event = await EventModel.findByIdAndChannelId(parseInt(eventId), parseInt(channelId))

    if (!event) {
      return res.fail({
        status: HttpStatusCode.NOT_FOUND_404,
        message: 'Event not found'
      })
    }

    const resLocals = res.locals as any
    resLocals.channel = channel
    resLocals.event = event

    return next()
  }
]

export const updateEventStateValidator = [
  param('eventId')
    .custom(isIdValid)
    .withMessage('Invalid event id'),

  body('state')
    .isIn([ 'active', 'disabled' ])
    .withMessage('Invalid event state'),

  async (req: express.Request, res: express.Response, next: express.NextFunction) => {
    if (areValidationErrors(req, res)) return

    const { channelId, eventId } = req.params
    const channel = await VideoChannelModel.scope([ 'WITH_ACCOUNT' ]).findByPk(channelId)

    if (!channel) {
      return res.fail({
        status: HttpStatusCode.NOT_FOUND_404,
        message: 'Channel not found'
      })
    }

    const event = await EventModel.findByIdAndChannelId(parseInt(eventId), parseInt(channelId))

    if (!event) {
      return res.fail({
        status: HttpStatusCode.NOT_FOUND_404,
        message: 'Event not found'
      })
    }

    const resLocals = res.locals as any
    resLocals.channel = channel
    resLocals.event = event

    return next()
  }
]

export const getEventValidator = [
  param('id')
    .custom(isIdValid)
    .withMessage('Invalid event id'),

  async (req: express.Request, res: express.Response, next: express.NextFunction) => {
    if (areValidationErrors(req, res)) return

    const event = await EventModel.findByPk(req.params.id)

    if (!event) {
      return res.fail({
        status: HttpStatusCode.NOT_FOUND_404,
        message: 'Event not found'
      })
    }

    (res.locals as any).event = event

    return next()
  }
]
