import { CSV, IssueLoader, CSVParser, IDCreator, Issue, IssueData } from '../core'
import { Channel, Blocked } from '../models'
import { DATA_DIR } from '../constants'
import { Storage, Collection } from '@freearhey/core'

let blocklist = new Collection()
let channels = new Collection()
const processedIssues = new Collection()

async function main() {
  const idCreator = new IDCreator()
  const dataStorage = new Storage(DATA_DIR)
  const parser = new CSVParser()

  const _channels = await dataStorage.load('channels.csv')
  channels = (await parser.parse(_channels)).map(data => new Channel(data))

  const _blocklist = await dataStorage.load('blocklist.csv')
  blocklist = (await parser.parse(_blocklist)).map(data => new Blocked(data))

  const loader = new IssueLoader()

  await removeChannels({ loader })
  await editChannels({ loader, idCreator })
  await addChannels({ loader, idCreator })
  await blockChannels({ loader })
  await unblockChannels({ loader })

  channels = sortBy(channels, 'id')
  const channelsOutput = new CSV({ items: channels }).toString()
  await dataStorage.save('channels.csv', channelsOutput)

  blocklist = sortBy(blocklist, 'channel')
  const blocklistOutput = new CSV({ items: blocklist }).toString()
  await dataStorage.save('blocklist.csv', blocklistOutput)

  const output = processedIssues.map((issue: Issue) => `closes #${issue.number}`).join(', ')
  process.stdout.write(`OUTPUT=${output}`)
}

main()

function sortBy(channels: Collection, key: string) {
  const items = channels.all().sort((a, b) => {
    const normA = a[key].toLowerCase()
    const normB = b[key].toLowerCase()
    if (normA < normB) return -1
    if (normA > normB) return 1
    return 0
  })

  return new Collection(items)
}

async function removeChannels({ loader }: { loader: IssueLoader }) {
  const issues = await loader.load({ labels: ['channels:remove,approved'] })
  issues.forEach((issue: Issue) => {
    if (issue.data.missing('channel_id')) return

    const found = channels.first(
      (channel: Channel) => channel.id === issue.data.getString('channel_id')
    )
    if (!found) return

    channels.remove((channel: Channel) => channel.id === found.id)

    processedIssues.push(issue)
  })
}

async function editChannels({ loader, idCreator }: { loader: IssueLoader; idCreator: IDCreator }) {
  const issues = await loader.load({ labels: ['channels:edit,approved'] })
  issues.forEach((issue: Issue) => {
    const data: IssueData = issue.data
    if (data.missing('channel_id')) return

    const found: Channel = channels.first(
      (channel: Channel) => channel.id === data.getString('channel_id')
    )
    if (!found) return

    let channelId = found.id
    if (data.has('name') || data.has('country')) {
      const name = data.getString('name') || found.name
      const country = data.getString('country') || found.country
      if (name && country) {
        channelId = idCreator.create(name, country)
      }
    }

    const updated = new Channel({
      id: channelId,
      name: data.getString('name'),
      alt_names: data.getArray('alt_names'),
      network: data.getString('network'),
      owners: data.getArray('owners'),
      country: data.getString('country'),
      subdivision: data.getString('subdivision'),
      city: data.getString('city'),
      broadcast_area: data.getArray('broadcast_area'),
      languages: data.getArray('languages'),
      categories: data.getArray('categories'),
      is_nsfw: data.getBoolean('is_nsfw'),
      launched: data.getString('launched'),
      closed: data.getString('closed'),
      replaced_by: data.getString('replaced_by'),
      website: data.getString('website'),
      logo: data.getString('logo')
    })

    found.merge(updated)

    processedIssues.push(issue)
  })
}

async function addChannels({ loader, idCreator }: { loader: IssueLoader; idCreator: IDCreator }) {
  const issues = await loader.load({ labels: ['channels:add,approved'] })
  issues.forEach((issue: Issue) => {
    const data: IssueData = issue.data
    const name = data.getString('name')
    const country = data.getString('country')

    if (!name || !country) return

    const channelId = idCreator.create(name, country)

    const found: Channel = channels.first((channel: Channel) => channel.id === channelId)
    if (found) return

    channels.push(
      new Channel({
        id: channelId,
        name: data.getString('name'),
        alt_names: data.getArray('alt_names'),
        network: data.getString('network'),
        owners: data.getArray('owners'),
        country: data.getString('country'),
        subdivision: data.getString('subdivision'),
        city: data.getString('city'),
        broadcast_area: data.getArray('broadcast_area'),
        languages: data.getArray('languages'),
        categories: data.getArray('categories'),
        is_nsfw: data.getBoolean('is_nsfw'),
        launched: data.getString('launched'),
        closed: data.getString('closed'),
        replaced_by: data.getString('replaced_by'),
        website: data.getString('website'),
        logo: data.getString('logo')
      })
    )

    processedIssues.push(issue)
  })
}

async function unblockChannels({ loader }: { loader: IssueLoader }) {
  const issues = await loader.load({ labels: ['blocklist:remove,approved'] })
  issues.forEach((issue: Issue) => {
    const data = issue.data
    if (data.missing('channel_id')) return

    const found: Blocked = blocklist.first(
      (blocked: Blocked) => blocked.channel === data.getString('channel_id')
    )
    if (!found) return

    blocklist.remove((blocked: Blocked) => blocked.channel === found.channel)

    processedIssues.push(issue)
  })
}

async function blockChannels({ loader }: { loader: IssueLoader }) {
  const issues = await loader.load({ labels: ['blocklist:add,approved'] })
  issues.forEach((issue: Issue) => {
    const data = issue.data
    if (data.missing('channel_id')) return

    const found: Blocked = blocklist.first(
      (blocked: Blocked) => blocked.channel === data.getString('channel_id')
    )
    if (found) return

    const channel = data.getString('channel_id')
    const ref = data.getString('ref')
    if (!channel || !ref) return

    blocklist.push(
      new Blocked({
        channel,
        ref
      })
    )

    processedIssues.push(issue)
  })
}