import { ITradeIdea } from 'contract/interfaces'
import { CONTRACT_INTERFACES } from '../../'
import CI = CONTRACT_INTERFACES

// order by nftId. if nftId is undefined, put it at the end
export const orderByNftId = (ideas: CI.ITradeIdea[]): CI.ITradeIdea[] => {
	const orderedIdeas = ideas.sort((a, b) => {
		if (a.nftId === undefined && b.nftId === undefined) return 0
		if (a.nftId === undefined) return 1
		if (b.nftId === undefined) return -1
		return a.nftId - b.nftId
	})
	return orderedIdeas
}

export const filterNullifiedIdeas = (
	ideas: CI.ITradeIdea[],
): CI.ITradeIdea[] => {
	const filteredIdeas = ideas.filter((idea) => idea.nullified === undefined)
	return filteredIdeas
}

export const filterDuplicatedIdeas = (
	ideas: CI.ITradeIdea[],
): CI.ITradeIdea[] => {
	const alreadySeenNftIds = new Set<number>()
	const filteredIdeas: CI.ITradeIdea[] = []
	for (const idea of ideas) {
		if (idea.nftId === undefined) continue
		if (alreadySeenNftIds.has(idea.nftId)) continue
		alreadySeenNftIds.add(idea.nftId)
		filteredIdeas.push(idea)
	}
	return filteredIdeas
}

export const sanitizeIdeas = (ideas: CI.ITradeIdea[]): CI.ITradeIdea[] => {
	return orderByNftId(filterDuplicatedIdeas(filterNullifiedIdeas(ideas)))
}

export const getValidatedIdeas = (cachedStrategyState: CI.IStrategyState) => {
	let validatedIdeas = [] as CI.ITradeIdea[]
	const check = cachedStrategyState?.validatedIdeaKeys
	if (check) {
		for (const validatedIdeaKey of cachedStrategyState.validatedIdeaKeys.values()) {
			const validatedIdeaSet = getValidatedIdeasByKey(
				validatedIdeaKey,
				cachedStrategyState,
			)
			validatedIdeas = [...validatedIdeas, ...validatedIdeaSet]
		}
	}
	return sanitizeIdeas(validatedIdeas)
}

export const getValidatedIdeasByKey = (
	ideaKey: number,
	cachedStrategyState: CI.IStrategyState,
) => {
	const validatedIdeas = [] as CI.ITradeIdea[]
	const check = cachedStrategyState?.validatedIdeasByKey
	if (check) {
		const validatedIdeasNftIds =
			cachedStrategyState.validatedIdeasByKey.get(ideaKey) || []
		for (const validatedIdeaNftId of validatedIdeasNftIds) {
			const validatedIdea =
				cachedStrategyState.ideas.get(validatedIdeaNftId)!
			validatedIdeas.push(validatedIdea)
		}
	}
	return sanitizeIdeas(validatedIdeas)
}

export const getEncryptedIdeas = (cachedStrategyState: CI.IStrategyState) => {
	const encryptedIdeas = [] as CI.ITradeIdea[]
	const check = cachedStrategyState?.encryptedIdeas
	if (check) {
		const encryptedIdeasNftIds = cachedStrategyState.encryptedIdeas.values()
		for (const encryptedIdeaNftId of encryptedIdeasNftIds) {
			const encryptedIdea =
				cachedStrategyState.ideas.get(encryptedIdeaNftId)!
			encryptedIdeas.push(encryptedIdea)
		}
	}
	return sanitizeIdeas(encryptedIdeas)
}

export const getEncryptedIdeasByKey = (
	ideaKey: number,
	cachedStrategyState: CI.IStrategyState,
) => {
	const encryptedIdeas = [] as CI.ITradeIdea[]
	const check = cachedStrategyState?.encryptedIdeasByKey
	if (check) {
		const encryptedIdeasNftIds =
			cachedStrategyState.encryptedIdeasByKey.get(ideaKey) || []
		for (const encryptedIdeaNftId of encryptedIdeasNftIds) {
			const encryptedIdea =
				cachedStrategyState.ideas.get(encryptedIdeaNftId)!
			encryptedIdeas.push(encryptedIdea)
		}
	}
	return sanitizeIdeas(encryptedIdeas)
}

export const getAllIdeas = (
	cachedStrategyState: CI.IStrategyState,
): CI.ITradeIdea[] => {
	const allIdeas = [
		...getValidatedIdeas(cachedStrategyState),
		...getEncryptedIdeas(cachedStrategyState),
	]
	return sanitizeIdeas(allIdeas)
}

export const getAllIdeasByKey = (
	ideaKey: number,
	cachedStrategyState: CI.IStrategyState,
) => {
	const allIdeas = [
		...getValidatedIdeasByKey(ideaKey, cachedStrategyState),
		...getEncryptedIdeasByKey(ideaKey, cachedStrategyState),
	]
	return sanitizeIdeas(allIdeas)
}

export const getLastStageOfIdeaByKey = (
	ideaKey: number,
	cachedStrategyState: CI.IStrategyState,
): CI.ITradeIdea | undefined => {
	const lastStageOfIdea = getAllIdeasByKey(ideaKey, cachedStrategyState).pop()
	return lastStageOfIdea
}

export const getFirstStageOfIdeaByKey = (
	ideaKey: number,
	cachedStrategyState: CI.IStrategyState,
): CI.ITradeIdea | undefined => {
	const firstStageOfIdea = getAllIdeasByKey(
		ideaKey,
		cachedStrategyState,
	).shift()
	return firstStageOfIdea
}

export const getExistingOpenNftByTicker = (
	ticker: string,
	cachedStrategyState: CI.IStrategyState,
): CI.ITradeIdea | undefined => {
	const valids = getAllIdeas(cachedStrategyState)

	const tickersMapOpenNftId = new Map<string, number>()
	for (const valid of valids) {
		if (valid.idea === undefined) continue
		const idea = valid.idea as CI.ITradeIdeaIdea
		if (idea.kind === 'open') {
			tickersMapOpenNftId.set(idea.asset.ticker, valid.nftId!)
		} else if (idea.kind === 'close') {
			tickersMapOpenNftId.delete(idea.asset.ticker)
		}
	}
	if (tickersMapOpenNftId.has(ticker)) {
		return cachedStrategyState.ideas.get(tickersMapOpenNftId.get(ticker)!)
	} else {
		return undefined
	}
}

export const getExistingAdjustNftByTicker = (
	ticker: string,
	cachedStrategyState: CI.IStrategyState,
): CI.ITradeIdea | undefined => {
	const valids = getAllIdeas(cachedStrategyState)
	let lookingForNftId: number | undefined = undefined

	const tickersMapOpenNftId = new Map<string, number>()
	for (const valid of valids) {
		if (valid.idea === undefined) continue
		const idea = valid.idea as CI.ITradeIdeaIdea
		if (idea.kind === 'open') {
			tickersMapOpenNftId.set(idea.asset.ticker, valid.nftId!)
		} else if (idea.kind === 'adjust') {
			lookingForNftId = valid.nftId
		} else if (idea.kind === 'close') {
			tickersMapOpenNftId.delete(idea.asset.ticker)
			lookingForNftId = undefined
		}
	}
	if (tickersMapOpenNftId.has(ticker) && lookingForNftId !== undefined) {
		return cachedStrategyState.ideas.get(lookingForNftId)
	} else {
		return undefined
	}
}

export const getExistingCloseNftByTicker = (
	ticker: string,
	cachedStrategyState: CI.IStrategyState,
): CI.ITradeIdea | undefined => {
	const valids = getAllIdeas(cachedStrategyState)
	let lookingForNftId: number | undefined = undefined

	const tickersMapOpenNftId = new Map<string, number>()
	for (const valid of valids) {
		if (valid.idea === undefined) continue
		const idea = valid.idea as CI.ITradeIdeaIdea
		if (idea.kind === 'open') {
			lookingForNftId = undefined
			tickersMapOpenNftId.set(idea.asset.ticker, valid.nftId!)
		} else if (idea.kind === 'adjust') {
		} else if (idea.kind === 'close') {
			lookingForNftId = valid.nftId
			tickersMapOpenNftId.delete(idea.asset.ticker)
		}
	}
	if (!tickersMapOpenNftId.has(ticker) && lookingForNftId !== undefined) {
		return cachedStrategyState.ideas.get(lookingForNftId)
	} else {
		return undefined
	}
}

export const getLastIdea = (
	cachedStrategyState: CI.IStrategyState,
): CI.ITradeIdea => {
	return cachedStrategyState.ideas.get(cachedStrategyState.lastNftId)!
}

export const getCreatorLatestData = (
	cachedStrategyState: CI.IStrategyState,
): CI.ITradeIdeaCreator => {
	const lastIdea = getLastIdea(cachedStrategyState)
	const creator = lastIdea.creator as CI.ITradeIdeaCreator
	return creator
}

export const getOnlyLastStageOfEachIdea = (
	cachedStrategyState: CI.IStrategyState,
): CI.ITradeIdea[] => {
	const allOrderedIdeas = getAllIdeas(cachedStrategyState)
	const allLatestIdeas = new Map<number, CI.ITradeIdea>()
	for (const idea of allOrderedIdeas) {
		allLatestIdeas.set(idea.content.ideaKey!, idea)
	}
	const latestStages = Array.from(allLatestIdeas.values())
	// now order it again
	const orderedLatestStages = latestStages.sort((a, b) => {
		if (a.nftId === undefined && b.nftId === undefined) return 0
		if (a.nftId === undefined) return 1
		if (b.nftId === undefined) return -1
		return a.nftId - b.nftId
	})
	return orderedLatestStages
}

export const getAllStillNotClosedLatestIdeas = (
	cachedStrategyState: CI.IStrategyState,
): CI.ITradeIdea[] => {
	const allLatestStages = getOnlyLastStageOfEachIdea(cachedStrategyState)
	return allLatestStages.filter((ones) => {
		if (typeof ones.idea === 'string') {
			return true
		} else {
			const idea = ones.idea as CI.ITradeIdeaIdea
			if (idea.kind === 'close') {
				return false
			} else {
				return true
			}
		}
	})
}
