// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
//
// SPDX-License-Identifier: AGPL-3.0-only

import type { RegisterServerOptions } from '@peertube/peertube-types'
import type { Router, Request, Response, NextFunction } from 'express'
import { videoHasWebchat } from '../../../../shared/lib/video'
import { asyncMiddleware } from '../../middlewares/async'
import { getCheckAPIKeyMiddleware } from '../../middlewares/apikey'
import { Affiliations, getVideoAffiliations, getChannelAffiliations } from '../../prosody/config/affiliations'
import { fillVideoCustomFields } from '../../custom-fields'
import { getChannelInfosById } from '../../database/channel'
import { RoomChannel } from '../../room-channel'
import {
  getChannelConfigurationOptions,
  getDefaultChannelConfigurationOptions
} from '../../configuration/channel/storage'
import { Emojis } from '../../emojis'

// See here for description: https://modules.prosody.im/mod_muc_http_defaults.html
interface RoomDefaults {
  config: {
    name: string
    description: string
    language?: string
    persistent?: boolean
    public?: boolean
    members_only?: boolean
    allow_member_invites?: boolean
    public_jids?: boolean
    // subject_from: string
    // subject: string
    changesubject?: boolean
    // historylength: number
    moderated?: boolean
    archiving?: boolean

    // Following fields are specific to livechat (for now), and requires a customized version for mod_muc_http_defaults.
    slow_mode_duration?: number
    mute_anonymous?: boolean
    livechat_emoji_only?: boolean
    livechat_custom_emoji_regexp?: string
    livechat_muc_terms?: string
    moderation_delay?: number
    anonymize_moderation_actions?: boolean
  }
  affiliations?: Affiliations
}

async function _getChannelSpecificOptions (
  options: RegisterServerOptions,
  channelId: number
): Promise<Partial<RoomDefaults['config']>> {
  const channelOptions = await getChannelConfigurationOptions(options, channelId) ??
    getDefaultChannelConfigurationOptions(options)

  const customEmojisRegexp = await Emojis.singletonSafe()?.getChannelCustomEmojisRegexp(channelId)

  return {
    slow_mode_duration: channelOptions.slowMode.duration,
    mute_anonymous: channelOptions.mute.anonymous,
    livechat_custom_emoji_regexp: customEmojisRegexp,
    livechat_muc_terms: channelOptions.terms,
    moderation_delay: channelOptions.moderation.delay,
    anonymize_moderation_actions: channelOptions.moderation.anonymize
  }
}

/**
 * Instanciate the route for room APIs.
 * These APIs are used by Prosody to get room defaults from the Peertube server.
 * @param options server register options
 */
async function initRoomApiRouter (options: RegisterServerOptions, router: Router): Promise<void> {
  const logger = options.peertubeHelpers.logger

  router.get('/room', asyncMiddleware([
    getCheckAPIKeyMiddleware(options),
    async (req: Request, res: Response, _next: NextFunction) => {
      const jid: string = req.query.jid as string || ''
      logger.info(`Requesting room information for room '${jid}'.`)

      const settings = await options.settingsManager.getSettings([
        'prosody-room-type'
      ])
      // Now, we have two different room type: per video or per channel.
      if (settings['prosody-room-type'] === 'channel') {
        const matches = jid.match(/^channel\.(\d+)$/)
        if (!matches?.[1]) {
          logger.warn(`Invalid channel room jid '${jid}'.`)
          res.sendStatus(403)
          return
        }
        const channelId = parseInt(matches[1])
        const channelInfos = await getChannelInfosById(options, channelId)
        if (!channelInfos) {
          logger.warn(`Channel ${channelId} not found`)
          res.sendStatus(403)
          return
        }

        let affiliations: Affiliations
        try {
          affiliations = await getChannelAffiliations(options, channelId)
        } catch (error) {
          logger.error(`Failed to get channel affiliations for ${channelId}:`, error)
          // affiliations: should at least be {}, so that the first user will not be moderator/admin
          affiliations = {}
        }

        const roomDefaults: RoomDefaults = {
          config: Object.assign(
            {
              name: channelInfos.displayName,
              description: ''
              // subject: channelInfos.displayName
            },
            await _getChannelSpecificOptions(options, channelId)
          ),
          affiliations
        }

        RoomChannel.singleton().link(channelId, jid)

        res.json(roomDefaults)
      } else {
        // FIXME: @peertube/peertype-types@4.2.2: wrongly considere video as MVideoThumbnail.
        const video = await options.peertubeHelpers.videos.loadByIdOrUUID(jid)
        if (!video) {
          logger.warn(`Video ${jid} not found`)
          res.sendStatus(403)
          return
        }

        // Adding the custom fields and data:
        await fillVideoCustomFields(options, video)

        // check settings (chat enabled for this video?)
        const settings = await options.settingsManager.getSettings([
          'chat-per-live-video',
          'chat-all-lives',
          'chat-all-non-lives',
          'chat-videos-list'
        ])
        if (!videoHasWebchat({
          'chat-per-live-video': !!settings['chat-per-live-video'],
          'chat-all-lives': !!settings['chat-all-lives'],
          'chat-all-non-lives': !!settings['chat-all-non-lives'],
          'chat-videos-list': settings['chat-videos-list'] as string
        }, video)) {
          logger.warn(`Video ${jid} has not chat activated`)
          res.sendStatus(403)
          return
        }

        let affiliations: Affiliations
        try {
          affiliations = await getVideoAffiliations(options, video)
        } catch (error) {
          logger.error(`Failed to get video affiliations for ${video.uuid}:`, error)
          // affiliations: should at least be {}, so that the first user will not be moderator/admin
          affiliations = {}
        }

        const roomDefaults: RoomDefaults = {
          config: Object.assign(
            {
              name: video.name,
              description: '',
              language: video.language
              // subject: video.name
            },
            await _getChannelSpecificOptions(options, video.channelId)
          ),
          affiliations
        }

        RoomChannel.singleton().link(video.channelId, jid)

        res.json(roomDefaults)
      }
    }
  ]))
}

export {
  initRoomApiRouter
}