const fetch = require("node-fetch")
const path = require("path")
const cacheManager = require("./cache-manager")

const apiInstance = "https://sponsor.ajay.app"

async function internalGetSB(videoID) {
	// Check if data already exists
	const existingData = cacheManager.read("sb")[videoID]
	if (existingData)
		return existingData

	let data = []

	// Otherwise, fetch
	let r
	try {
		r = await fetch(path.join(apiInstance, `/api/skipSegments?videoID=${videoID}&service=youtube`))
		if (r.status == 400 || r.status == 404)
			throw new Error(r.statusText)
	} catch (e) {
		if (e instanceof fetch.FetchError)
			return new Error(`FetchError (${e.code})`)
		return e
	}

	let sbData = await r.json()

	if (!r.ok && !sbData && !sbData.randomTime)
		return new Error(`Status: ${r.status}`)

	if (sbData)
		data = sbData

	cacheManager.write("sb", videoID, data)
	return data
}

async function internalGetDeArrow(videoID) {
	// Check if data already exists
	const existingData = cacheManager.read("dearrow")[videoID]
	if (existingData && !existingData.loading)
		return existingData

	let data = {}

	// Otherwise, fetch
	let r
	try {
		r = await fetch(path.join(apiInstance, `/api/branding?videoID=${videoID}&service=youtube`))
	} catch (e) {
		if (e instanceof fetch.FetchError)
			return new Error(`FetchError (${e.code})`)
		return e
	}

	let dearrowData = await r.json()

	if (!r.ok && !dearrowData && !dearrowData.randomTime)
		return new Error(`Status: ${r.status}`)

	if (dearrowData) {
		for (const title of dearrowData.titles)
			if (title.votes > 0 || title.locked) {
				// Use original title
				if (title.original)
					break

				data.title = title.title
				break
			}

		for (const thumbnail of dearrowData.thumbnails)
			if (thumbnail.votes > 0) {
				// Use original thumbnail
				if (thumbnail.original)
					break

				data.thumbnail = `api/v1/getThumbnail?videoID=${videoID}&time=${thumbnail.timestamp}`
				break
			}
	}

	cacheManager.write("dearrow", videoID, data)
	return data
}

module.exports = {
	getSB: async function(id) {
		let error = undefined
		let outData = undefined

		await internalGetSB(id)
			.then(data => {
				if (data.message)
					error = `SponsorBlock for ${id}: ${data}`
				else
					outData = data
			})

		return{ error, data: outData }
	},
	getDeArrow: async function(id) {
		let error = undefined
		let outData = undefined

		await internalGetDeArrow(id)
			.then(data => {
				if (data.message)
					error = `DeArrow for ${id}: ${data}`
				else
					outData = data
			})

		return{ error, data: outData }
	},

	applyToAllDeArrow: async function(videos) {
		let errors = {}

		let queue = videos.map(v => { return { id: v.videoId, tries: 0 } })

		while (queue.length > 0) {
			let nextQueue = []
			for (let i = 0; i < Math.min(queue.length, 3) && nextQueue.length < 3; i++)
				nextQueue.push(queue.shift())

			await Promise.all(nextQueue.map(v => internalGetDeArrow(v.id)
				.then(data => {
					if (data.message)
						if (data.message.startsWith("FetchError"))
							if (v.tries < 3) {
								v.tries++
								queue.unshift(v)
							} else
								errors[data.name] = `DeArrow for ${v.id}: ${data}`
						else
							errors[data.name] = `DeArrow for ${v.id}: ${data}`
				})))
		}

		return { errors };
	},

	getAllDeArrowNoBlock: function(videos) {
		// Apply a "loading" value to each video before starting the operation
		let anyLoading = false
		for (const v of videos) {
			const oldData = cacheManager.read("dearrow")[v.videoId]
			if (!oldData) {
				cacheManager.write("dearrow", v.videoId, { loading: true })
				v.dearrowData = { loading: true }
				anyLoading = true
			} else
				v.dearrowData = oldData
		}

		if (anyLoading)
			this.applyToAllDeArrow(videos)
	}
}
