from __future__ import annotations

import collections
import typing

import cache
import roi

BUY = frozenset([
	# definitely buy
	'C', 'FLX', 'H', 'H2O', 'HAL', 'HCP', 'HE', 'LST', 'MG', 'N', 'NA', 'NCS', 'NS', 'O', 'PE', 'PG', 'S', 'TCL', 'THF',
	# maybe buy
	'AIR', 'AU', 'BE', 'BRM', 'CU', 'FAN', 'FC', 'FE', 'HCC', 'HD', 'LDI', 'LI', 'MFK', 'MWF', 'REA', 'RG', 'RGO', 'ROM', 'SFK', 'SI', 'STL', 'TI', 'TPU',
	# import
	'AAR', 'AWF', 'CAP', 'CF',
	# skip
	'LFE', 'LHP', 'MFE', 'SFE', 'SSC',
])

def main() -> None:
	blueprint = {
		'FFC': 1,
		'FSE': 1,
		'LFE': 2,
		'MFE': 2,
		'QCR': 1,
		'SFE': 1,
		'LCB': 1,
		'MFL': 1,
		'MSL': 1,
		'LHP': 94,
		'SSC': 128,
		'BR1': 1,
		'CQM': 1,
	}
	prices: typing.Mapping[str, RawPrice] = {p['MaterialTicker']: p
			for p in cache.get('https://refined-prun.github.io/refined-prices/all.json') if p['ExchangeCode'] == 'IC1'}
	recipes = recipe_for_mats()

	production: dict[str, dict[str, float]] = collections.defaultdict(lambda: collections.defaultdict(float))
	buy: dict[str, float] = collections.defaultdict(float)
	cost = 0.0
	for mat, amount in blueprint.items():
		cost += analyze_mat(0, mat, amount, production, buy, prices, recipes)
		print()
	print(f'total cost: {cost:,}')

	buildings: typing.Sequence[roi.Building] = cache.get('https://api.prunplanner.org/data/buildings/', expiry=cache.ONE_DAY)
	expertise = collections.defaultdict(list)
	for building in buildings:
		if building['building_ticker'] in production:
			expertise[building['expertise']].append(building['building_ticker'])
	for expertise, buildings in expertise.items():
		print(expertise)
		for building in buildings:
			print(f'\t{building:3}:', end='')
			for mat, amount in production[building].items():
				traded = prices[mat]['AverageTraded30D'] or 0
				if traded > amount * 2:
					print(f' {amount:g}×\033[32m{mat}\033[0m', end='')
				else:
					print(f' {amount:g}×\033[31m{mat}\033[0m', end='')
			print()

	print('buy')
	for mat, amount in buy.items():
		price = prices[mat]
		print(f'{amount:g}×{mat}', price['Demand'])

def recipe_for_mats() -> dict[str, roi.Recipe]:
	all_recipes: list[roi.Recipe] = cache.get('https://api.prunplanner.org/data/recipes/', expiry=cache.ONE_DAY)
	mat_recipes = collections.defaultdict(list) # all ways to make a mat
	for recipe in all_recipes:
		for output in recipe['outputs']:
			mat_recipes[output['material_ticker']].append(recipe)

	mat_recipe: dict[str, roi.Recipe] = {} # mats for which there's only one recipe to make
	for mat, recipes in mat_recipes.items():
		if len(recipes) == 1:
			mat_recipe[mat] = recipes[0]
	return mat_recipe

def analyze_mat(level: int, mat: str, amount: float, production: dict[str, dict[str, float]], buy: dict[str, float],
		prices: typing.Mapping[str, RawPrice], recipes: dict[str, roi.Recipe]) -> float:
	price = prices[mat]
	traded = price['AverageTraded30D'] or 0
	if mat in BUY:
		print('\t' * level + f'{amount:g}×{mat} buy: {price["Ask"]}, daily traded {traded:5.1f}')
		buy[mat] += amount
		assert price['Ask'] is not None
		return price['Ask'] * amount
	else:
		if (recipe := recipes.get(mat)) is None:
			print('\t' * level + f'{amount:g}×{mat} make (unknown recipe)')
			return 0
		else:
			building = recipe['building_ticker']
			production[building][mat] += amount

			liquid = '\033[31mnot liquid\033[0m'
			if traded > amount * 2:
				liquid = '\033[32mliquid\033[0m'
			print('\t' * level + f'{amount:g}×{mat} make ({building}, {liquid})')

			total_cost = 0.0
			for input_mat in recipe['inputs']:
				input_amount = input_mat['material_amount'] * amount / recipe['outputs'][0]['material_amount']
				total_cost += analyze_mat(level + 1, input_mat['material_ticker'], input_amount, production, buy, prices, recipes)
			print('\t' * level + f'\tcost: {total_cost:,.0f}')
		return total_cost

class RawPrice(typing.TypedDict):
	MaterialTicker: str
	ExchangeCode: str
	Ask: float | None
	AverageTraded30D: float | None # averaged daily traded volume over last 30 days
	Supply: int
	Demand: int

if __name__ == '__main__':
	main()
