import { HttpStatusCode } from '@peertube/peertube-models'
import { isIdValid } from '@server/helpers/custom-validators/misc.js'
import { EventRegistrationModel } from '@server/models/video/event-registration.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'

const phoneRegexp = /^[0-9+()\-\s]{7,20}$/

const requiredRegistrationFields = [
  body('firstName')
    .trim()
    .notEmpty()
    .withMessage('First name is required'),

  body('lastName')
    .trim()
    .notEmpty()
    .withMessage('Last name is required'),

  body('email')
    .trim()
    .isEmail()
    .withMessage('Email must be valid')
    .bail()
    .normalizeEmail(),

  body('phone')
    .trim()
    .matches(phoneRegexp)
    .withMessage('Phone must be valid'),

  body('companyName')
    .trim()
    .notEmpty()
    .withMessage('Company name is required'),

  body('jobTitle')
    .trim()
    .notEmpty()
    .withMessage('Job title is required'),

  body('country')
    .trim()
    .isLength({ min: 2, max: 100 })
    .withMessage('Country must be between 2 and 100 characters')
]

const optionalRegistrationFields = [
  body('firstName')
    .optional()
    .trim()
    .notEmpty()
    .withMessage('First name cannot be empty'),

  body('lastName')
    .optional()
    .trim()
    .notEmpty()
    .withMessage('Last name cannot be empty'),

  body('email')
    .optional()
    .trim()
    .isEmail()
    .withMessage('Email must be valid')
    .bail()
    .normalizeEmail(),

  body('phone')
    .optional()
    .trim()
    .matches(phoneRegexp)
    .withMessage('Phone must be valid'),

  body('companyName')
    .optional()
    .trim()
    .notEmpty()
    .withMessage('Company name cannot be empty'),

  body('jobTitle')
    .optional()
    .trim()
    .notEmpty()
    .withMessage('Job title cannot be empty'),

  body('country')
    .optional()
    .trim()
    .isLength({ min: 2, max: 100 })
    .withMessage('Country must be between 2 and 100 characters')
]

async function loadEventWithChannel (eventId: number) {
  return EventModel.findByPk(eventId, {
    include: [
      {
        model: VideoChannelModel.scope([ 'WITH_ACCOUNT' ]),
        required: true
      }
    ]
  })
}

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

  ...requiredRegistrationFields,

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

    const eventId = parseInt(req.params.eventId)
    const event = await loadEventWithChannel(eventId)

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

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

    return next()
  }
]

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

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

    const event = await loadEventWithChannel(parseInt(req.params.eventId))

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

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

    return next()
  }
]

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

  param('registrationId')
    .custom(isIdValid)
    .withMessage('Invalid registration id'),

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

    const eventId = parseInt(req.params.eventId)
    const registrationId = parseInt(req.params.registrationId)
    const [ event, registration ] = await Promise.all([
      loadEventWithChannel(eventId),
      EventRegistrationModel.loadByIdAndEventId(registrationId, eventId)
    ])

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

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

    const resLocals = res.locals as any
    resLocals.event = event
    resLocals.eventRegistration = registration

    return next()
  }
]

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

  param('registrationId')
    .custom(isIdValid)
    .withMessage('Invalid registration id'),

  ...optionalRegistrationFields,

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

    const eventId = parseInt(req.params.eventId)
    const registrationId = parseInt(req.params.registrationId)
    const [ event, registration ] = await Promise.all([
      loadEventWithChannel(eventId),
      EventRegistrationModel.loadByIdAndEventId(registrationId, eventId)
    ])

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

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

    const resLocals = res.locals as any
    resLocals.event = event
    resLocals.eventRegistration = registration

    return next()
  }
]

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

  param('registrationId')
    .custom(isIdValid)
    .withMessage('Invalid registration id'),

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

    const eventId = parseInt(req.params.eventId)
    const registrationId = parseInt(req.params.registrationId)
    const [ event, registration ] = await Promise.all([
      loadEventWithChannel(eventId),
      EventRegistrationModel.loadByIdAndEventId(registrationId, eventId)
    ])

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

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

    const resLocals = res.locals as any
    resLocals.event = event
    resLocals.eventRegistration = registration

    return next()
  }
]
