import { Logger, Storage, Collection, File } from '@freearhey/core'
import { PlaylistParser } from '../../core'
import { Stream, Category, Channel, Language, Country, Region, Subdivision } from '../../models'
import _ from 'lodash'
import {
  CategoriesGenerator,
  CountriesGenerator,
  LanguagesGenerator,
  RegionsGenerator,
  IndexGenerator,
  IndexNsfwGenerator,
  IndexCategoryGenerator,
  IndexCountryGenerator,
  IndexLanguageGenerator,
  IndexRegionGenerator
} from '../../generators'
import { DATA_DIR, LOGS_DIR, STREAMS_DIR } from '../../constants'

async function main() {
  const logger = new Logger()
  const dataStorage = new Storage(DATA_DIR)

  logger.info('loading data from api...')
  const channelsContent = await dataStorage.json('channels.json')
  const channels = new Collection(channelsContent).map(data => new Channel(data))
  const categoriesContent = await dataStorage.json('categories.json')
  const categories = new Collection(categoriesContent).map(data => new Category(data))
  const countriesContent = await dataStorage.json('countries.json')
  const countries = new Collection(countriesContent).map(data => new Country(data))
  const languagesContent = await dataStorage.json('languages.json')
  const languages = new Collection(languagesContent).map(data => new Language(data))
  const regionsContent = await dataStorage.json('regions.json')
  const regions = new Collection(regionsContent).map(data => new Region(data))
  const subdivisionsContent = await dataStorage.json('subdivisions.json')
  const subdivisions = new Collection(subdivisionsContent).map(data => new Subdivision(data))

  logger.info('loading streams...')
  let streams = await loadStreams({ channels, categories, languages })
  let totalStreams = streams.count()
  streams = streams.uniqBy((stream: Stream) => (stream.channel || _.uniqueId()) + stream.timeshift)
  logger.info(`found ${totalStreams} streams (including ${streams.count()} unique)`)

  const generatorsLogger = new Logger({
    stream: await new Storage(LOGS_DIR).createStream(`generators.log`)
  })

  logger.info('generating categories/...')
  await new CategoriesGenerator({ categories, streams, logger: generatorsLogger }).generate()
  logger.info('generating countries/...')
  await new CountriesGenerator({
    countries,
    streams,
    regions,
    subdivisions,
    logger: generatorsLogger
  }).generate()
  logger.info('generating languages/...')
  await new LanguagesGenerator({ streams, logger: generatorsLogger }).generate()
  logger.info('generating regions/...')
  await new RegionsGenerator({
    streams,
    regions,
    subdivisions,
    logger: generatorsLogger
  }).generate()
  logger.info('generating index.m3u...')
  await new IndexGenerator({ streams, logger: generatorsLogger }).generate()
  logger.info('generating index.category.m3u...')
  await new IndexCategoryGenerator({ streams, logger: generatorsLogger }).generate()
  logger.info('generating index.country.m3u...')
  await new IndexCountryGenerator({
    streams,
    countries,
    regions,
    subdivisions,
    logger: generatorsLogger
  }).generate()
  logger.info('generating index.language.m3u...')
  await new IndexLanguageGenerator({ streams, logger: generatorsLogger }).generate()
  logger.info('generating index.region.m3u...')
  await new IndexRegionGenerator({ streams, regions, logger: generatorsLogger }).generate()
}

main()

async function loadStreams({
  channels,
  categories,
  languages
}: {
  channels: Collection
  categories: Collection
  languages: Collection
}) {
  const groupedChannels = channels.keyBy(channel => channel.id)
  const groupedCategories = categories.keyBy(category => category.id)
  const groupedLanguages = languages.keyBy(language => language.code)

  const storage = new Storage(STREAMS_DIR)
  const parser = new PlaylistParser({ storage })
  const files = await storage.list('**/*.m3u')
  let streams = await parser.parse(files)

  streams = streams
    .orderBy(
      [
        (stream: Stream) => stream.channel,
        (stream: Stream) => stream.timeshift,
        (stream: Stream) => parseInt(stream.quality.replace('p', '')),
        (stream: Stream) => stream.label
      ],
      ['asc', 'asc', 'desc', 'asc']
    )
    .map((stream: Stream) => {
      const channel: Channel | undefined = groupedChannels.get(stream.channel)

      if (channel) {
        const channelCategories = channel.categories
          .map((id: string) => groupedCategories.get(id))
          .filter(Boolean)
        const channelLanguages = channel.languages
          .map((id: string) => groupedLanguages.get(id))
          .filter(Boolean)

        stream.categories = channelCategories
        stream.languages = channelLanguages
        stream.broadcastArea = channel.broadcastArea
        stream.isNSFW = channel.isNSFW
        if (channel.logo) stream.logo = channel.logo
      } else {
        const file = new File(stream.filepath)
        const [_, countryCode] = file.name().match(/^([a-z]{2})(_|$)/) || [null, null]
        const defaultBroadcastArea = countryCode ? [`c/${countryCode.toUpperCase()}`] : []

        stream.broadcastArea = new Collection(defaultBroadcastArea)
      }

      return stream
    })

  return streams
}