const dayjs = require('dayjs')
const axios = require('axios')
const utc = require('dayjs/plugin/utc')

dayjs.extend(utc)

const paths = {
  ad: { zone: 'cpfra',  location: 'ad' },
  au: { zone: 'cpncl',  location: 'au' },
  bf: { zone: 'cpafr',  location: 'bf' },
  bi: { zone: 'cpafr',  location: 'bi' },
  bj: { zone: 'cpafr',  location: 'bj' },
  bl: { zone: 'cpant',  location: 'bl' },
  cd: { zone: 'cpafr',  location: 'cd' },
  cf: { zone: 'cpafr',  location: 'cf' },
  cg: { zone: 'cpafr',  location: 'cg' },
  ch: { zone: 'cpche',  location: null },
  ch_de: { zone: 'cpchd', location: null },
  ci: { zone: 'cpafr',  location: 'ci' },
  cm: { zone: 'cpafr',  location: 'cm' },
  cv: { zone: 'cpafr',  location: 'cv' },
  dj: { zone: 'cpafr',  location: 'dj' },
  et: { zone: 'cpeth',  location: 'et' },
  fr: { zone: null,     location: null },
  ga: { zone: 'cpafr',  location: 'ga' },
  gf: { zone: 'cpant',  location: 'gf' },
  gh: { zone: 'cpafr',  location: 'gh' },
  gm: { zone: 'cpafr',  location: 'gm' },
  gn: { zone: 'cpafr',  location: 'gn' },
  gp: { zone: 'cpafr',  location: 'gp' },
  gw: { zone: 'cpafr',  location: 'gw' },
  ht: { zone: 'cpant',  location: 'ht' },
  km: { zone: 'cpafr',  location: 'km' },
  mc: { zone: 'cpfra',  location: 'mc' },
  mf: { zone: 'cpant',  location: 'mf' },
  mg: { zone: 'cpmdg',  location: 'mg' },
  ml: { zone: 'cpafr',  location: 'ml' },
  mq: { zone: 'cpant',  location: 'mq' },
  mr: { zone: 'cpafr',  location: 'mr' },
  mu: { zone: 'cpmus',  location: 'mu' },
  nc: { zone: 'cpncl',  location: 'nc' },
  ne: { zone: 'cpafr',  location: 'ne' },
  pf: { zone: 'cppyf',  location: 'pf' },
  pl: { zone: null,     location: null },
  re: { zone: 'cpreu',  location: 're' },
  rw: { zone: 'cpafr',  location: 'rw' },
  sl: { zone: 'cpafr',  location: 'sl' },
  sn: { zone: 'cpafr',  location: 'sn' },
  td: { zone: 'cpafr',  location: 'td' },
  tg: { zone: 'cpafr',  location: 'tg' },
  wf: { zone: 'cpncl',  location: 'wf' },
  yt: { zone: 'cpreu',  location: 'yt' },
}

const globalHeaders = {
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36',
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8',
  'Accept-Language': 'fr-FR,fr;q=0.6',
  'Accept-Encoding': 'gzip, deflate, br',
  'Pragma': 'no-cache',
  'Priority': 'u=0, i',
  'Sec-CH-UA': '"Not:A-Brand";v="99", "Brave";v="145", "Chromium";v="145"',
  'sec-ch-ua-mobile': '?0',
  'sec-ch-ua-platform': '"Windows"',
  'sec-fetch-dest': 'document',
  'sec-fetch-mode': 'navigate',
  'sec-fetch-site': 'none',
  'sec-fetch-user': '?1',
  'sec-gpc': '1',
  'upgrade-insecure-requests': '1'
}

// Per-region token caching to avoid multiple concurrent calls and redundant token fetches
const tokenCache = {}
const tokenPending = {}

// ARCOM (ex-CSA) internal ratings mapping (https://www.arcom.fr/se-documenter/ressources-pedagogiques/protection-des-mineurs)
// values are negative to be sorted before other ratings if any
const CSA_RATING_MAP = { '2': '-10', '3': '-12', '4': '-16', '5': '-18' }

module.exports = {
  site: 'canalplus.com',
  days: 2,
  url: async function ({ channel, date }) {
    const [region, site_id] = channel.site_id.split('#')
    const currentRegion = region || 'fr'

    if (!tokenCache[currentRegion]) {
      // Prevents concurrent calls from same region
      if (!tokenPending[currentRegion]) {
        tokenPending[currentRegion] = parseToken(currentRegion).then(result => {
          tokenCache[currentRegion] = result
          if (Object.prototype.hasOwnProperty.call(tokenPending, currentRegion)) {
            tokenPending[currentRegion] = undefined
          }
          return result
        })
      }
      await tokenPending[currentRegion]
    }

    const path = currentRegion === 'pl' ? 'mycanalint' : 'mycanal'
    const diff = date.diff(dayjs.utc().startOf('d'), 'd')
    const token = tokenCache[currentRegion]?.token

    return `https://hodor.canalplus.pro/api/v2/${path}/channels/${token}/${site_id}/broadcasts/day/${diff}`
  },
  request: {
    headers() {
      return globalHeaders
    }
  },
  async parser({ content }) {
    const items = parseItems(content)

    // Parallel loading of all program details
    const detailsArray = await Promise.all(items.map(loadProgramDetails))

    const programs = items.map((item, i) => {
      const info = parseInfo(detailsArray[i])
      const start = parseStart(item)
      return {
        title: item.title,
        description: parseDescription(info),
        image: parseImage(info),
        actors: parseCast(info, 'Avec :'),
        director: parseCast(info, 'De :'),
        writer: parseCast(info, 'Scénario :'),
        composer: parseCast(info, 'Musique :'),
        presenter: parseCast(info, 'Présenté par :'),
        date: parseDate(info),
        rating: parseRating(info),
        start,
        stop: null
      }
    })

    // Sort programs by start time and set stop time of each program to the start time of the next one
    for (let i = 0; i < programs.length - 1; i++) {
      programs[i].stop = programs[i + 1].start
    }

    // Last program: fallback +1h if there is no next program
    const last = programs[programs.length - 1]
    if (last && last.start) {
      last.stop = last.start.add(1, 'h')
    }

    return programs
  },
  async channels({ country }) {
    const { zone, location } = paths[country] || {}
    const pathSegment = location ? `${zone}/${location}` : zone || country
    const url = `https://secure-webtv-static.canal-plus.com/metadata/${pathSegment}/all/v2.2/globalchannels.json`

    const data = await axios
      .get(url)
      .then(r => r.data)
      .catch(console.log)

    return data.channels
      .filter(channel => channel.name !== '.')
      .map(channel => ({
        lang: 'fr',
        site_id: country === 'fr' ? `#${channel.id}` : `${country}#${channel.id}`,
        name: channel.name
      }))
  }
}

async function parseToken(country) {
  const { zone, location } = paths[country] || {}

  let url
  if (country === 'fr') {
    url = 'https://hodor.canalplus.pro/api/v2/mycanal/authenticate.json/webapp/6.0?experiments=beta-test-one-tv-guide:control'
  } else if (country === 'pl') {
    url = 'https://hodor.canalplus.pro/api/v2/mycanalint/authenticate.json/webapp/6.0?experiments=beta-test-one-tv-guide:control'
  } else {
    url = `https://hodor.canalplus.pro/api/v2/mycanal/authenticate.json/webapp/6.0?experiments=beta-test-one-tv-guide:control&offerZone=${zone}&offerLocation=${location}`
  }

  const data = await axios
    .get(url, { headers: globalHeaders, timeout: 5000 })
    .then(r => r.data)
    .catch(console.error)

  return { country, token: data?.token }
}

function parseStart(item) {
  return item?.startTime ? dayjs(item.startTime) : null
}

function parseImage(info) {
  return info?.URLImage ?? null
}

function parseDescription(info) {
  return info?.summary ?? null
}

function parseInfo(data) {
  return data?.detail?.informations ?? null
}

async function loadProgramDetails(item) {
  if (!item?.onClick?.URLPage) return {}
  return axios
    .get(item.onClick.URLPage, { headers: globalHeaders })
    .then(r => r.data)
    .catch(console.error)
}

function parseItems(content) {
  const data = JSON.parse(content)
  if (!data || !Array.isArray(data.timeSlices)) return []
  return data.timeSlices.flatMap(s => s.contents)
}

function parseCast(info, type) {
  if (!info?.personnalities) return []
  const group = info.personnalities.find(i => i.prefix === type)
  if (!group) return []
  return group.personnalitiesList.map(p => p.title)
}

function parseDate(info) {
  return info?.productionYear ?? null
}

function parseRating(info) {
  if (!info?.parentalRatings) return null
  const rating = info.parentalRatings.find(i => i.authority === 'CSA')
  if (!rating || Array.isArray(rating) || rating.value === '1') return null
  return {
    system: rating.authority,
    value: CSA_RATING_MAP[rating.value] ?? rating.value
  }
}