import { VideoInfo, VideoPageInfo } from '@/components/video/video-info'
import { VideoQuality } from '@/components/video/video-quality'
import { bilibiliApi, getJsonWithCredentials } from '@/core/ajax'
import { meta } from '@/core/meta'
import { getComponentSettings } from '@/core/settings'
import { Toast } from '@/core/toast'
import { name as componentName, title as pluginTitle } from '.'
import { FieldsMode, Options } from './options'

export type MetadataType = 'ffmetadata' | 'ogm'

function escape(s: string) {
  return s.replace(/[=;#\\\n]/g, r => `\\${r}`)
}

interface ViewPoint {
  content: string
  from: number
  to: number
  image: string
}

class VideoMetadata {
  #aid: string
  #cid: number | string

  basic: VideoInfo

  viewPoints: ViewPoint[]
  page: VideoPageInfo
  quality?: VideoQuality

  constructor(aid: string, cid: number | string) {
    this.#aid = aid
    this.#cid = cid
    this.basic = new VideoInfo(aid)
  }

  async fetch() {
    await this.basic.fetchInfo()
    this.page = this.basic.pages.filter(p => p.cid === parseInt(<any>this.#cid))[0]

    const playInfo = await bilibiliApi(
      getJsonWithCredentials(
        `https://api.bilibili.com/x/player/wbi/v2?aid=${this.#aid}&cid=${this.#cid}`,
      ),
    )

    this.viewPoints = lodash.get(playInfo, 'view_points', []) as ViewPoint[]
  }
}

async function fetchMetadata(aid: string = unsafeWindow.aid, cid: string = unsafeWindow.cid) {
  const data = new VideoMetadata(aid, cid)
  await data.fetch()
  return data
}

function ff(key: string, value: any, prefix = true) {
  return `${prefix ? 'bilibili_' : ''}${key}=${escape(lodash.toString(value))}`
}

async function generateFFMetadata(aid: string = unsafeWindow.aid, cid: string = unsafeWindow.cid) {
  const data = await fetchMetadata(aid, cid)
  const info = data.basic

  const {
    options: { fieldsMode },
  } = getComponentSettings<Options>(componentName)

  const lines = [
    ';FFMETADATA1',
    `;generated by Bilibili-Evolved v${meta.compilationInfo.version}`,
    `;generated on ${new Date().toLocaleString()}`,
    // Standard fields
    ff('title', `${info.title} - ${data.page.title}`, false),
    ff('description', info.description, false),
    ff('artist', info.up.name, false),
  ]

  if (fieldsMode === FieldsMode.ALL) {
    // Custom fields
    lines.push(
      ff('title', info.title),
      ff('description', info.description),
      ff('publish_date', new Date(info.pubdate * 1000).toLocaleString()),
      ff('aid', info.aid),
      ff('bvid', info.bvid),
      ff('cid', data.page.cid),
      ff('category_id', info.tagId),
      ff('category_name', info.tagName),
      ff('page_title', data.page.title),
      ff('page', data.page.pageNumber),
      ff('pages', info.pages.length),
      ff('up_name', info.up.name),
      ff('up_uid', info.up.uid),
    )
    if (data.quality) {
      lines.push(ff('quality', data.quality.value))
      lines.push(ff('quality_label', data.quality.name))
    }
  }

  if (data.viewPoints.length > 0) {
    for (const chapter of data.viewPoints) {
      lines.push(
        ...[
          '[CHAPTER]',
          'TIMEBASE=1/1',
          ff('START', chapter.from, false),
          ff('END', chapter.to, false),
          ff('title', chapter.content, false),
        ],
      )
    }
  }

  const result = lines.join('\n')

  console.debug(result)
  return result
}

async function generateChapterFile(aid: string = unsafeWindow.aid, cid: string = unsafeWindow.cid) {
  const { viewPoints } = await fetchMetadata(aid, cid)
  console.debug(viewPoints)
  if (viewPoints.length > 0) {
    const result = viewPoints
      .reduce((p, v, i) => {
        const n = `${i + 1}`.padStart(3, '0')
        return [
          ...p,
          `CHAPTER${n}=${new Date(v.from * 1000).toISOString().slice(11, -1)}`,
          `CHAPTER${n}NAME=${v.content}`,
        ]
      }, [])
      .join('\n')

    console.debug(result)
    return result
  }
  Toast.info('此视频没有章节', pluginTitle, 3000)
  return null
}

export async function generateByType(
  type: MetadataType,
  aid: string = unsafeWindow.aid,
  cid: string = unsafeWindow.cid,
) {
  let method: (aid, cid) => Promise<string>
  switch (type) {
    case 'ogm':
      method = generateChapterFile
      break
    default:
    case 'ffmetadata':
      method = generateFFMetadata
      break
  }
  return method(aid, cid)
}
