import { HttpStatusCode } from '@peertube/peertube-models'
import { isIdValid } from '@server/helpers/custom-validators/misc.js'
import { isVideoFileMimeTypeValid, isVideoImageValid } from '@server/helpers/custom-validators/videos.js'
import { LiveVideoModel, LiveVideoStatus } from '@server/models/video/live-video.js'
import express from 'express'
import { body, param, query } from 'express-validator'
import { areValidationErrors } from './shared/utils.js'

function ensureAdmin (res: express.Response) {
  const user = res.locals.oauth?.token?.User
  return user?.role === 0
}

async function checkScheduleConflict (req: express.Request, res: express.Response, next: express.NextFunction, excludeId?: number) {
  const startTime = new Date(req.body.startTime)
  const endTime = new Date(req.body.endTime)

  const conflicts = await LiveVideoModel.listConflicts({
    targetCamera: req.body.targetCamera,
    startTime,
    endTime,
    excludeId
  })

  if (conflicts.length !== 0) {
    return res.fail({
      status: HttpStatusCode.CONFLICT_409,
      message: 'Target camera is already assigned to another live video'
    })
  }

  return next()
}

export const listLiveVideosValidator = [
  query('status')
    .optional()
    .isIn(Object.values(LiveVideoStatus))
    .withMessage('Invalid live video status'),

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

export const getLiveVideoValidator = [
  param('id')
    .custom(isIdValid)
    .withMessage('Invalid live video id'),

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

    const liveVideo = await LiveVideoModel.loadById(parseInt(req.params.id))
    if (!liveVideo) {
      return res.fail({
        status: HttpStatusCode.NOT_FOUND_404,
        message: 'Live video not found'
      })
    }

    ;(res.locals as any).liveVideo = liveVideo
    return next()
  }
]

export const createLiveVideoValidator = [
  body('title')
    .trim()
    .isLength({ min: 1, max: 255 })
    .withMessage('Live video title must be between 1 and 255 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('targetCamera')
    .trim()
    .isLength({ min: 1, max: 255 })
    .withMessage('Target camera is required'),

  body('thumbnailfile')
    .custom((_, { req }) => isVideoImageValid(req.files, 'thumbnailfile'))
    .withMessage('Should have a valid thumbnail file'),

  body('videofile')
    .custom((_, { req }) => isVideoFileMimeTypeValid(req.files))
    .withMessage('Should have a valid video file'),

  body('subtitlefile')
    .optional()
    .custom((_, { req }) => {
      if (!req.files || !req.files['subtitlefile']) return true
      const file = req.files['subtitlefile'][0]
      const validMimes = [ 'text/vtt', 'application/x-subrip', 'text/plain' ]
      return validMimes.includes(file.mimetype)
    })
    .withMessage('Should have a valid subtitle file (vtt or srt)'),


  async (req: express.Request, res: express.Response, next: express.NextFunction) => {
    if (areValidationErrors(req, res)) return
    if (!ensureAdmin(res)) {
      return res.fail({
        status: HttpStatusCode.FORBIDDEN_403,
        message: 'Only admins can manage live videos'
      })
    }

    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'
      })
    }

    return checkScheduleConflict(req, res, next)
  }
]

export const updateLiveVideoValidator = [
  param('id')
    .custom(isIdValid)
    .withMessage('Invalid live video id'),

  body('title')
    .optional()
    .trim()
    .isLength({ min: 1, max: 255 })
    .withMessage('Live video title must be between 1 and 255 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('targetCamera')
    .optional()
    .trim()
    .isLength({ min: 1, max: 255 })
    .withMessage('Target camera must be between 1 and 255 characters'),

  body('status')
    .optional()
    .isIn(Object.values(LiveVideoStatus))
    .withMessage('Invalid live video status'),

  body('thumbnailfile')
    .optional()
    .custom((_, { req }) => isVideoImageValid(req.files, 'thumbnailfile'))
    .withMessage('Should have a valid thumbnail file'),

  body('videofile')
    .optional()
    .custom((_, { req }) => isVideoFileMimeTypeValid(req.files))
    .withMessage('Should have a valid video file'),
  body('subtitlefile')
    .optional()
    .custom((_, { req }) => {
      if (!req.files || !req.files['subtitlefile']) return true
      const file = req.files['subtitlefile'][0]
      const validMimes = [ 'text/vtt', 'application/x-subrip', 'text/plain' ]
      return validMimes.includes(file.mimetype)
    })
    .withMessage('Should have a valid subtitle file (vtt or srt)'),


  async (req: express.Request, res: express.Response, next: express.NextFunction) => {
    if (areValidationErrors(req, res)) return
    if (!ensureAdmin(res)) {
      return res.fail({
        status: HttpStatusCode.FORBIDDEN_403,
        message: 'Only admins can manage live videos'
      })
    }

    const liveVideo = await LiveVideoModel.loadById(parseInt(req.params.id))
    if (!liveVideo) {
      return res.fail({
        status: HttpStatusCode.NOT_FOUND_404,
        message: 'Live video not found'
      })
    }

    const startTime = req.body.startTime ? new Date(req.body.startTime) : liveVideo.startTime
    const endTime = req.body.endTime ? new Date(req.body.endTime) : liveVideo.endTime

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

    ;(res.locals as any).liveVideo = liveVideo

    if (req.body.startTime || req.body.endTime || req.body.targetCamera) {
      req.body.startTime = startTime.toISOString()
      req.body.endTime = endTime.toISOString()
      req.body.targetCamera = req.body.targetCamera ?? liveVideo.targetCamera

      return checkScheduleConflict(req, res, next, liveVideo.id)
    }

    return next()
  }
]

export const deleteLiveVideoValidator = [
  param('id')
    .custom(isIdValid)
    .withMessage('Invalid live video id'),

  async (req: express.Request, res: express.Response, next: express.NextFunction) => {
    if (areValidationErrors(req, res)) return
    if (!ensureAdmin(res)) {
      return res.fail({
        status: HttpStatusCode.FORBIDDEN_403,
        message: 'Only admins can manage live videos'
      })
    }

    const liveVideo = await LiveVideoModel.loadById(parseInt(req.params.id))
    if (!liveVideo) {
      return res.fail({
        status: HttpStatusCode.NOT_FOUND_404,
        message: 'Live video not found'
      })
    }

    ;(res.locals as any).liveVideo = liveVideo
    return next()
  }
]
