import { Dictionary, Collection } from '@freearhey/core'
import { ChannelData } from '../types/channel'
import { createChannelId } from '../utils'
import JoiDate from '@joi/date'
import { Model } from './model'
import { Feed } from './feed'
import BaseJoi from 'joi'
import { IssueData } from '../core'

const Joi = BaseJoi.extend(JoiDate)

export class Channel extends Model {
  id: string
  name: string
  altNames?: Collection
  networkName?: string
  ownerNames?: Collection
  countryCode: string
  subdivisionCode?: string
  cityName?: string
  categoryIds?: Collection
  isNSFW: boolean
  launchedDateString?: string
  closedDateString?: string
  replacedBy?: string
  websiteUrl?: string
  logoUrl: string
  feeds?: Collection

  constructor(data: ChannelData) {
    super()

    this.id = data.id
    this.name = data.name
    this.altNames = data.alt_names ? new Collection(data.alt_names) : undefined
    this.networkName = data.network
    this.ownerNames = data.owners ? new Collection(data.owners) : undefined
    this.countryCode = data.country
    this.subdivisionCode = data.subdivision
    this.cityName = data.city
    this.categoryIds = data.categories ? new Collection(data.categories) : undefined
    this.isNSFW = data.is_nsfw
    this.launchedDateString = data.launched
    this.closedDateString = data.closed
    this.replacedBy = data.replaced_by
    this.websiteUrl = data.website
    this.logoUrl = data.logo
  }

  setId(id: string): this {
    this.id = id

    return this
  }

  update(issueData: IssueData): this {
    const data = {
      channel_name: issueData.getString('channel_name'),
      alt_names: issueData.getArray('alt_names'),
      network: issueData.getString('network'),
      owners: issueData.getArray('owners'),
      country: issueData.getString('country'),
      subdivision: issueData.getString('subdivision'),
      city: issueData.getString('city'),
      categories: issueData.getArray('categories'),
      is_nsfw: issueData.getBoolean('is_nsfw'),
      launched: issueData.getString('launched'),
      closed: issueData.getString('closed'),
      replaced_by: issueData.getString('replaced_by'),
      website: issueData.getString('website'),
      logo: issueData.getString('logo')
    }

    if (data.channel_name !== undefined) this.name = data.channel_name
    if (data.alt_names !== undefined) this.altNames = new Collection(data.alt_names)
    if (data.network !== undefined) this.networkName = data.network
    if (data.owners !== undefined) this.ownerNames = new Collection(data.owners)
    if (data.country !== undefined) this.countryCode = data.country
    if (data.subdivision !== undefined) this.subdivisionCode = data.subdivision
    if (data.city !== undefined) this.cityName = data.city
    if (data.categories !== undefined) this.categoryIds = new Collection(data.categories)
    if (data.is_nsfw !== undefined) this.isNSFW = data.is_nsfw
    if (data.launched !== undefined) this.launchedDateString = data.launched
    if (data.closed !== undefined) this.closedDateString = data.closed
    if (data.replaced_by !== undefined) this.replacedBy = data.replaced_by
    if (data.website !== undefined) this.websiteUrl = data.website
    if (data.logo !== undefined) this.logoUrl = data.logo

    return this
  }

  withFeeds(feedsGroupedByChannelId: Dictionary): this {
    this.feeds = new Collection(feedsGroupedByChannelId.get(this.id))

    return this
  }

  getFeeds(): Collection {
    if (!this.feeds) return new Collection()

    return this.feeds
  }

  hasValidId(): boolean {
    const expectedId = createChannelId(this.name, this.countryCode)

    return expectedId === this.id
  }

  hasMainFeed(): boolean {
    const feeds = this.getFeeds()

    if (feeds.isEmpty()) return false

    const mainFeed = feeds.find((feed: Feed) => feed.isMain)

    return !!mainFeed
  }

  hasMoreThanOneMainFeed(): boolean {
    const mainFeeds = this.getFeeds().filter((feed: Feed) => feed.isMain)

    return mainFeeds.count() > 1
  }

  hasValidReplacedBy(channelsKeyById: Dictionary, feedsKeyByStreamId: Dictionary): boolean {
    if (!this.replacedBy) return true

    const [channelId, feedId] = this.replacedBy.split('@')

    if (channelsKeyById.missing(channelId)) return false
    if (feedId && feedsKeyByStreamId.missing(this.replacedBy)) return false

    return true
  }

  hasValidCountryCode(countriesKeyByCode: Dictionary): boolean {
    return countriesKeyByCode.has(this.countryCode)
  }

  hasValidSubdivisionCode(subdivisionsKeyByCode: Dictionary): boolean {
    return !this.subdivisionCode || subdivisionsKeyByCode.has(this.subdivisionCode)
  }

  hasValidCategoryIds(categoriesKeyById: Dictionary): boolean {
    const hasInvalid = this.getCategoryIds().find((id: string) => categoriesKeyById.missing(id))

    return !hasInvalid
  }

  getCategoryIds(): Collection {
    if (!this.categoryIds) return new Collection()

    return this.categoryIds
  }

  getAltNames(): Collection {
    if (!this.altNames) return new Collection()

    return this.altNames
  }

  getOwnerNames(): Collection {
    if (!this.ownerNames) return new Collection()

    return this.ownerNames
  }

  data(): ChannelData {
    return {
      id: this.id,
      name: this.name,
      alt_names: this.getAltNames().all(),
      network: this.networkName,
      owners: this.getOwnerNames().all(),
      country: this.countryCode,
      subdivision: this.subdivisionCode,
      city: this.cityName,
      categories: this.getCategoryIds().all(),
      is_nsfw: this.isNSFW,
      launched: this.launchedDateString,
      closed: this.closedDateString,
      replaced_by: this.replacedBy,
      website: this.websiteUrl,
      logo: this.logoUrl
    }
  }

  getSchema() {
    return Joi.object({
      id: Joi.string()
        .regex(/^[A-Za-z0-9]+\.[a-z]{2}$/)
        .required(),
      name: Joi.string()
        .regex(/^[a-z0-9-!:&.+'/»#%°$@?|¡–\s_—]+$/i)
        .regex(/^((?!\s-\s).)*$/)
        .required(),
      alt_names: Joi.array().items(
        Joi.string()
          .regex(/^[^",]+$/)
          .invalid(Joi.ref('name'))
      ),
      network: Joi.string()
        .regex(/^[^",]+$/)
        .allow(null),
      owners: Joi.array().items(Joi.string().regex(/^[^",]+$/)),
      country: Joi.string()
        .regex(/^[A-Z]{2}$/)
        .required(),
      subdivision: Joi.string()
        .regex(/^[A-Z]{2}-[A-Z0-9]{1,3}$/)
        .allow(null),
      city: Joi.string()
        .regex(/^[^",]+$/)
        .allow(null),
      categories: Joi.array().items(Joi.string().regex(/^[a-z]+$/)),
      is_nsfw: Joi.boolean().strict().required(),
      launched: Joi.date().format('YYYY-MM-DD').raw().allow(null),
      closed: Joi.date().format('YYYY-MM-DD').raw().allow(null).greater(Joi.ref('launched')),
      replaced_by: Joi.string()
        .regex(/^[A-Za-z0-9]+\.[a-z]{2}($|@[A-Za-z0-9]+$)/)
        .allow(null),
      website: Joi.string()
        .regex(/,/, { invert: true })
        .uri({
          scheme: ['http', 'https']
        })
        .allow(null),
      logo: Joi.string()
        .regex(/,/, { invert: true })
        .uri({
          scheme: ['https']
        })
        .required()
    })
  }
}
