const fs = require('fs')
const path = require('path')
const https = require('https')
const axios = require('axios')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')

dayjs.extend(utc)

const API_ENDPOINT = 'https://api.toonamiaftermath.com'

// Let's Encrypt YR1 and YR2 will be expired on Sep  2 23:59:59 2028 GMT
const certfile = path.join(__dirname, '__data__', 'certificate.pem')
const httpsAgent = new https.Agent({ ca: fs.readFileSync(certfile) })
const caches = {}

module.exports = {
  site: 'toonamiaftermath.com',
  days: 3,
  request: {
    cache: {
      ttl: 24 * 60 * 60 * 1000 // 1 day
    },
    httpsAgent
  },
  async url({ channel, date }) {
    return await getUrl(channel, date)
  },
  async parser({ channel, date, content }) {
    const programs = []
    const delay = parseInt(channel.site_id.split('+')[1]) || 0
    const cdate = date.startOf('d')
    const items = parseItems(content)
    // fetch other playlist URLs if exist
    if (items.length && Array.isArray(date.urls)) {
      for (const url of date.urls) {
        if (caches[url] !== undefined) {
          items.push(...caches[url])
        } else {
          const result = await axios.get(url, { httpsAgent })
            .then(res => res.data)
            .catch(console.error)
          const newItems = parseItems(result)
          caches[url] = newItems
          items.push(...newItems)
        }
      }
    }
    items.forEach(item => {
      const start = getDate(item.startDate, delay)
      const stop = getDate(item.endDate, delay)
      if (isDate(start, cdate) || isDate(stop, cdate, true)) {
        programs.push({
          title: item.name,
          description: item.info?.fullname,
          sub_title: item.info?.episode,
          image: item.info?.image,
          start,
          stop
        })
      }
    })

    return programs
  },
  async channels() {
    const channels = []
    const result = await axios.get('https://www.toonamiaftermath.com/bundle.js')
      .then(res => res.data)
      .catch(err => console.error(err.toString()))

    const matches = result.match(/channels:(\[.*?\]),siteConfigs:/)
    if (matches[1]) {
      const vm = require('vm')
      const items = vm.runInNewContext(`const o=${matches[1]}; o`)
      if (Array.isArray(items)) {
        channels.push(...items
          .filter(item => item.scheduleName)
          .map(item => ({
            lang: 'en',
            site_id: `${item.scheduleName}${item.streamDelay ? '+' + item.streamDelay : ''}`,
            name: item.name
          }))
        )
      }
    }

    return channels
  }
}

async function getUrl(channel, date) {
  const scheduleName = channel.site_id.split('+')[0]
  const url = new URL(`${API_ENDPOINT}/playlists?scheduleName=${
    scheduleName
  }&startDate=${
    date.startOf('d').toJSON()
  }&thisWeek=true&weekStartDay=monday`)
  const playlists = await axios.get(url.toString(), { httpsAgent })
    .then(res => res.data)
    .catch(err => console.error(err.toString()))

  const urls = []
  if (Array.isArray(playlists)) {
    const dt = date.startOf('d')
    playlists
      .filter(p => isDate(getDate(p.startDate), dt) || isDate(getDate(p.endDate), dt, true))
      .forEach(p => {
        urls.push(`${API_ENDPOINT}/playlist?id=${p._id}&addInfo=true`)
      })
  }
  if (!urls.length) {
    throw new Error(`Unable to get schedule URL for ${scheduleName}!`)
  }
  const playlistUrl = urls.shift()
  if (urls.length) {
    // pass next playlist URLs in date object
    date.urls = urls
  }

  return playlistUrl
}

function getDate(dt, delay = 0) {
  return dayjs.utc(dt).add(delay, 'm')
}

function isDate(dt, ref, end) {
  return dt.isSame(ref, 'd') && (end ? dt > ref : true)
}

function parseItems(content) {
  if (!content) return []
  if (typeof content === 'string' || Buffer.isBuffer(content)) {
    content = JSON.parse(content)
  }
  if (!content || !content.playlist) return []

  return content.playlist.blocks.reduce((acc, curr) => {
    // short media type has no title, use block title instead
    curr.mediaList.forEach(media => {
      if (media.name === undefined) {
        media.name = curr.name
      }
    })

    return acc.concat(curr.mediaList)
  }, [])
}
