// @flow
import {AsyncStorage} from 'react-native'
import moment from 'moment'
import {reportNetworkProblem} from '@frogpond/analytics'
import delay from 'delay'

// global.AsyncStorage = AsyncStorage

type _BaseCacheResult<T> = {
	isExpired: boolean,
	isCached: boolean,
	value: ?T,
	source: ?string,
}

export type CacheResult<T> = Promise<_BaseCacheResult<T>>

function needsUpdate(time: Date, [count, unit]: [number, string]): boolean {
	return moment(time).isBefore(moment().subtract(count, unit))
}

function annotateCacheEntry({ts, ttl}: {ts: string, ttl: string}) {
	// handle uncached entries and entries that aren't caches, like the homescreen order
	if (!ts || !ttl) {
		return {isCached: true, isExpired: false}
	}

	return {
		isCached: true,
		isExpired: needsUpdate(new Date(ts), JSON.parse(ttl)),
	}
}

/// MARK: Utilities

const ROOT = 'fp'

async function setItem(
	key: string,
	value: any,
	{ttl, source}: {ttl?: [number, string], source?: string},
) {
	console.log('setItem', {key, value, ttl, source})
	await AsyncStorage.multiSet([
		[`${ROOT}:${key}:data`, JSON.stringify(value)],
		[`${ROOT}:${key}:ts`, new Date().toUTCString()],
		[`${ROOT}:${key}:ttl`, JSON.stringify(ttl)],
		[`${ROOT}:${key}:source`, source],
	])
}

async function getItem(key: string): CacheResult<any> {
	console.log('getItem', key)

	let [[, value], [, ts], [, ttl], [, source]] = await AsyncStorage.multiGet([
		`${ROOT}:${key}:data`,
		`${ROOT}:${key}:ts`,
		`${ROOT}:${key}:ttl`,
		`${ROOT}:${key}:source`,
	])

	return {
		value: JSON.parse(value),
		source,
		...annotateCacheEntry({ts, ttl}),
	}
}

// global._setItem = setItem
// global._getItem = getItem
// global._fetchAndCacheItem = fetchAndCacheItem

export async function fetchAndCacheItem(args: {
	key: string,
	url?: string,
	cbForBundledData?: () => Promise<mixed>,
	ttl?: [number, string],
	afterFetch?: (parsed: any) => any,
	force?: boolean,
	delay?: boolean,
}): CacheResult<any> {
	let {
		key,
		url,
		cbForBundledData,
		ttl,
		afterFetch,
		force: shouldForce,
		delay: shouldDelay,
	} = args

	let start = Date.now()

	console.log('fetchAndCacheItem', {key, url, cbForBundledData, ttl})

	// if (process.env.NODE_ENV === 'development' && cbForBundledData) {
	// 	let data = await cbForBundledData()
	// 	await setItem(key, data, {source: url})
	// 	return getItem(key)
	// }

	let {value, isExpired, isCached, source} = await getItem(key)

	if (!isExpired && value != null && !shouldForce) {
		return {value, isCached, isExpired, source}
	}

	if (!source && url) {
		source = url
	}

	try {
		// try fetching first
		let body = await fetchJson(url)

		if (afterFetch) {
			body = afterFetch(body)
		}

		await setItem(key, body, {source: url, ttl})

		let elapsed = Date.now() - start
		if (shouldDelay && elapsed < 500) {
			// 0.5s delay for ListViews – if we let them go at full speed, it feels broken
			await delay(500 - elapsed)
		}

		return getItem(key)
	} catch (error) {
		// fall back to the local data
		if (error.message.startsWith('Failed to fetch')) {
			reportNetworkProblem(error)
		}

		// if there's no data still, return null
		if (!isCached) {
			if (cbForBundledData) {
				let data = await cbForBundledData()
				await setItem(key, data, {source: url, ttl})
				return getItem(key)
			}

			return {value: null, isCached, isExpired, source}
		}

		// if we have data, though, return it
		return {value, isCached, isExpired, source}
	}
}

@@ -223,16 +223,17 @@
 t%3CT%3E = %7B
+%7C
 %0A%09isExpi
@@ -282,26 +282,9 @@
 ?T,%0A
-%09source: ?string,%0A
+%7C
 %7D%0A%0Ae
@@ -892,24 +892,16 @@
 y,%0A%09%7Bttl
-, source
 %7D: %7Bttl?
@@ -922,25 +922,8 @@
 ing%5D
-, source?: string
 %7D,%0A)
@@ -965,24 +965,16 @@
 lue, ttl
-, source
 %7D)%0A%09awai
@@ -1151,45 +1151,8 @@
 )%5D,%0A
-%09%09%5B%60$%7BROOT%7D:$%7Bkey%7D:source%60, source%5D,%0A
 %09%5D)%0A
@@ -1276,20 +1276,8 @@
 ttl%5D
-, %5B, source%5D
 %5D = 
@@ -1382,35 +1382,8 @@
 l%60,%0A
-%09%09%60$%7BROOT%7D:$%7Bkey%7D:source%60,%0A
 %09%5D)%0A
@@ -1425,18 +1425,8 @@
 e),%0A
-%09%09source,%0A
 %09%09..
@@ -2242,24 +2242,16 @@
 isCached
-, source
 %7D = awai
@@ -2358,59 +2358,9 @@
 ired
-, source%7D%0A%09%7D%0A%0A%09if (!source && url) %7B%0A%09%09source = url
+%7D
 %0A%09%7D%0A
@@ -2506,29 +2506,16 @@
  body, %7B
-source: url, 
 ttl%7D)%0A%0A%09
@@ -3029,21 +3029,8 @@
 a, %7B
-source: url, 
 ttl%7D
@@ -3104,24 +3104,16 @@
 sExpired
-, source
 %7D%0A%09%09%7D%0A%0A%09
@@ -3191,16 +3191,8 @@
 ired
-, source
 %7D%0A%09%7D

remove source: key