import {setupPopover} from './popover';

const roiCache: Record<string, Promise<{lastModified: Date, profits: Profit[]}>> = {};
async function getROI(cx: string) {
	const response = await fetch(`/roi_${cx.toLowerCase()}.json`);
	const lastModified = new Date(response.headers.get('last-modified')!);
	const profits = await response.json();
	return {lastModified, profits};
}

type MetricType = 'vwap' | 'bid' | 'ask';

const lowVolume = document.querySelector('input#low-volume') as HTMLInputElement;

const cxSelect = document.querySelector('select#cx') as HTMLSelectElement;
const expertise = {
	AGRICULTURE: 'agri',
	CHEMISTRY: 'chem',
	CONSTRUCTION: 'const',
	ELECTRONICS: 'elec',
	FOOD_INDUSTRIES: 'food ind',
	FUEL_REFINING: 'fuel',
	MANUFACTURING: 'mfg',
	METALLURGY: 'metal',
	RESOURCE_EXTRACTION: 'res ext',
} as const;
const expertiseSelect = document.querySelector('select#expertise') as HTMLSelectElement;
for (const key of Object.keys(expertise)) {
	const option = document.createElement('option');
	option.value = key;
	option.textContent = key.replace('_', ' ').toLowerCase();
	expertiseSelect.appendChild(option);
}
const buildingSelect = document.querySelector('select#building') as HTMLSelectElement;

const formatDecimal = new Intl.NumberFormat(undefined,
		{maximumFractionDigits: 2, maximumSignificantDigits: 6, roundingPriority: 'lessPrecision'}).format;
const formatWhole = new Intl.NumberFormat(undefined, {maximumFractionDigits: 0}).format;

if (localStorage.getItem('roi-cx')) cxSelect.value = localStorage.getItem('roi-cx')!;
if (localStorage.getItem('roi-expertise')) expertiseSelect.value = localStorage.getItem('roi-expertise')!;
if (localStorage.getItem('roi-low-volume')) lowVolume.checked = localStorage.getItem('roi-low-volume') === 'true';

let savedBuilding = localStorage.getItem('roi-building') || '';

let currentSortKey: keyof ProfitWithMetrics | 'outputs' = (localStorage.getItem('roi-sort-key') as any) || 'break_even';
let currentSortAsc: boolean = localStorage.getItem('roi-sort-asc') !== 'false';
let headersInitialized = false;
let metricControlsInitialized = false;

let capexMetric: MetricType = (localStorage.getItem('roi-capex-metric') as MetricType) || 'vwap';
let opexMetric: MetricType = (localStorage.getItem('roi-opex-metric') as MetricType) || 'vwap';
let revenueMetric: MetricType = (localStorage.getItem('roi-revenue-metric') as MetricType) || 'vwap';

async function render() {
	const tbody = document.querySelector('tbody')!;
	tbody.innerHTML = '';

	const cx = cxSelect.value;
	if (!roiCache[cx])
		roiCache[cx] = getROI(cx);
	const {lastModified, profits} = await roiCache[cx];

	if (!metricControlsInitialized) {
		const controls = document.createElement('div');
		controls.style.marginBottom = '15px';
		controls.innerHTML = `
			<label style="margin-right: 15px;">CapEx Price: 
				<select id="capex-metric"><option value="vwap">VWAP</option><option value="bid">Bid</option><option value="ask">Ask</option></select>
			</label>
			<label style="margin-right: 15px;">OpEx Price: 
				<select id="opex-metric"><option value="vwap">VWAP</option><option value="bid">Bid</option><option value="ask">Ask</option></select>
			</label>
			<label>Revenue (Outputs) Price: 
				<select id="revenue-metric"><option value="vwap">VWAP</option><option value="bid">Bid</option><option value="ask">Ask</option></select>
			</label>
		`;
		const table = document.querySelector('table');
		if (table) table.parentNode?.insertBefore(controls, table);

		(document.getElementById('capex-metric') as HTMLSelectElement).value = capexMetric;
		(document.getElementById('opex-metric') as HTMLSelectElement).value = opexMetric;
		(document.getElementById('revenue-metric') as HTMLSelectElement).value = revenueMetric;

		document.getElementById('capex-metric')!.addEventListener('change', (e) => {
			capexMetric = (e.target as HTMLSelectElement).value as MetricType;
			render();
		});
		document.getElementById('opex-metric')!.addEventListener('change', (e) => {
			opexMetric = (e.target as HTMLSelectElement).value as MetricType;
			render();
		});
		document.getElementById('revenue-metric')!.addEventListener('change', (e) => {
			revenueMetric = (e.target as HTMLSelectElement).value as MetricType;
			render();
		});
		metricControlsInitialized = true;
	}

	if (!headersInitialized) {
		const ths = document.querySelectorAll('th');
		const keys: (keyof ProfitWithMetrics | 'outputs')[] = [
			'outputs', 'expertise', 'profit_per_area', 'break_even', 
			'capex_val', 'opex_val', 'logistics_per_area', 'market_capacity_area'
		];
		
		ths.forEach((th, i) => {
			if (keys[i]) {
				th.style.cursor = 'pointer';
				th.title = ''; // Remove native title to prevent double-tooltips
				
				// EXTREME DETAIL: We inject dynamic string content and custom tooltips directly into the DOM headers.
				// By routing these explanations into `dataset.tooltip`, the existing popover engine picks them up
				// automatically, rendering a clean, stylized popup on hover.
				if (keys[i] === 'market_capacity_area') {
					th.textContent = 'Market Cap (Areas)';
					th.dataset.tooltip = 'Click to sort.\n\nMarket Capacity: 7-day average traded volume ÷ daily output per area. Indicates how many areas you can build before saturating the market.';
				} else if (keys[i] === 'break_even') {
					th.dataset.tooltip = 'Click to sort.\n\nBreak Even: (CapEx + 3 days of OpEx) ÷ daily profit. Includes 3 days of operating costs as working capital.';
				} else {
					th.dataset.tooltip = 'Click to sort.';
				}

				th.addEventListener('click', () => {
					if (currentSortKey === keys[i]) {
						currentSortAsc = !currentSortAsc;
					} else {
						currentSortKey = keys[i];
						currentSortAsc = keys[i] === 'break_even' ? true : false;
					}
					render();
				});
			}
		});
		headersInitialized = true;
	}

	const buildingTickers = new Set(profits.map(p => p.building));
	const buildings: {ticker: string, expertise: keyof typeof expertise}[] = Array.from(buildingTickers)
			.map((building) => ({ticker: building, expertise: profits.find(p => p.building === building)!.expertise}))
			.sort((a, b) => a.ticker.localeCompare(b.ticker));
	
	let selectedBuilding = buildingSelect.value || savedBuilding;
	let buildingFound = false;
	buildingSelect.innerHTML = '<option value="">(all)</option>';
	for (const building of buildings)
		if (expertiseSelect.value === '' || expertiseSelect.value === building.expertise) {
			const option = document.createElement('option');
			option.value = building.ticker;
			option.textContent = building.ticker;
			if (building.ticker === selectedBuilding) {
				buildingFound = true;
				option.selected = true;
			}
			buildingSelect.appendChild(option);
		}
	if (!buildingFound)
		selectedBuilding = '';
	
	savedBuilding = ''; 

	const filteredProfits = profits.filter(p => {
		const volumeRatio = p.output_per_day / p.average_traded_7d;
		if (!lowVolume.checked && volumeRatio > 0.05) return false;
		if (expertiseSelect.value !== '' && p.expertise !== expertiseSelect.value) return false;
		if (selectedBuilding !== '' && p.building !== selectedBuilding) return false;
		return true;
	});

	const profitsWithMetrics: ProfitWithMetrics[] = filteredProfits.map(p => {
		const capex_val = p.capex[capexMetric];
		const opex_val = p.opex[opexMetric];
		const revenue_val = p.revenue[revenueMetric];
		
		const profit_per_day = revenue_val - opex_val;
		const profit_per_area = profit_per_day / p.area;
		const break_even = profit_per_day > 0 ? (capex_val + 3 * opex_val) / profit_per_day : Infinity;
		
		return { ...p, capex_val, opex_val, revenue_val, profit_per_day, profit_per_area, break_even };
	});

	profitsWithMetrics.sort((a, b) => {
		let valA: any = a[currentSortKey as keyof ProfitWithMetrics];
		let valB: any = b[currentSortKey as keyof ProfitWithMetrics];
		
		if (currentSortKey === 'outputs') {
			valA = a.outputs.map(o => o.ticker).join(', ');
			valB = b.outputs.map(o => o.ticker).join(', ');
		}
		
		if (valA < valB) return currentSortAsc ? -1 : 1;
		if (valA > valB) return currentSortAsc ? 1 : -1;
		return 0;
	});

	for (const p of profitsWithMetrics) {
		const tr = document.createElement('tr');
		
		tr.innerHTML = `
			<td>${p.outputs.map(o => o.ticker).join(', ')}</td>
			<td>${expertise[p.expertise]}</td>
			<td style="color: ${color(p.profit_per_area, 0, 300)}">${formatDecimal(p.profit_per_area)}</td>
			<td><span style="color: ${color(p.break_even, 30, 3)}">${formatDecimal(p.break_even)}</span>d</td>
			<td style="color: ${color(p.capex_val, 300_000, 40_000)}">${formatWhole(p.capex_val)}</td>
			<td style="color: ${color(p.opex_val, 40_000, 1_000)}">${formatWhole(p.opex_val)}</td>
			<td style="color: ${color(p.logistics_per_area, 2, 0.2)}">${formatDecimal(p.logistics_per_area)}</td>
			<td style="color: ${color(p.market_capacity_area, 20, 500)}">${formatWhole(p.market_capacity_area)}</td>
		`;

		const output = tr.querySelector('td')!;
		output.dataset.tooltip = p.recipe;

		const profitCell = tr.querySelectorAll('td')[2];
		profitCell.dataset.tooltip = formatMatPrices(p.outputs, revenueMetric, p.runs_per_day) + '\n\n' +
			formatMatPrices(p.input_costs, opexMetric, p.runs_per_day) + '\n' +
			`(${formatWhole(p.revenue_val)} - ${formatWhole(p.opex_val)}) = ${formatDecimal(p.profit_per_day)}\n` +
			`${formatDecimal(p.profit_per_day)} / ${formatWhole(p.area)} area = ${formatDecimal(p.profit_per_area)}`;

		const marketCell = tr.querySelectorAll('td')[7];
		marketCell.dataset.tooltip = `Market Capacity: ${formatWhole(p.average_traded_7d)} traded/day ÷ ${formatDecimal(p.output_per_day / p.area)} produced/day/area = ${formatWhole(p.market_capacity_area)} equivalent areas`;

		tbody.appendChild(tr);
	}
	document.getElementById('last-updated')!.textContent =
		`last updated: ${lastModified.toLocaleString(undefined, {dateStyle: 'full', timeStyle: 'long', hour12: false})}`;

	saveState();
}

function saveState() {
	localStorage.setItem('roi-cx', cxSelect.value);
	localStorage.setItem('roi-expertise', expertiseSelect.value);
	localStorage.setItem('roi-building', buildingSelect.value);
	localStorage.setItem('roi-low-volume', lowVolume.checked.toString());
	localStorage.setItem('roi-sort-key', currentSortKey);
	localStorage.setItem('roi-sort-asc', currentSortAsc.toString());
	localStorage.setItem('roi-capex-metric', capexMetric);
	localStorage.setItem('roi-opex-metric', opexMetric);
	localStorage.setItem('roi-revenue-metric', revenueMetric);
}

function color(n: number, low: number, high: number): string {
	const scale = Math.min(Math.max((n - low) / (high - low), 0), 1);
	return `color-mix(in xyz, #0aa ${scale * 100}%, #f80)`;
}

function formatMatPrices(matPrices: MatPrice[], metric: MetricType, runs_per_day: number): string {
	return matPrices.map(({ticker, amount, vwap_7d, bid, ask}) => {
		const val = metric === 'vwap' ? vwap_7d : metric === 'bid' ? (bid ?? vwap_7d) : (ask ?? vwap_7d);
		const daily_amount = amount * runs_per_day;
		return `${ticker}: ${formatDecimal(daily_amount)} × ${formatDecimal(val)} = ${formatWhole(daily_amount * val)}`;
	}).join('\n');
}

setupPopover();
lowVolume.addEventListener('change', render);
cxSelect.addEventListener('change', render);
expertiseSelect.addEventListener('change', render);
buildingSelect.addEventListener('change', render);
render();

interface Metrics {
	vwap: number;
	bid: number;
	ask: number;
}

interface Profit {
	outputs: MatPrice[]
	recipe: string
	expertise: keyof typeof expertise
	building: string
	area: number
	capex: Metrics
	opex: Metrics
	revenue: Metrics
	input_costs: MatPrice[]
	runs_per_day: number
	logistics_per_area: number
	output_per_day: number
	average_traded_7d: number
	market_capacity_area: number
}

interface ProfitWithMetrics extends Profit {
	capex_val: number;
	opex_val: number;
	revenue_val: number;
	profit_per_day: number;
	profit_per_area: number;
	break_even: number;
}

interface MatPrice {
	ticker: string
	amount: number
	vwap_7d: number
	bid: number | null
	ask: number | null
}

interface Building {
	building_type: 'INFRASTRUCTURE' | 'PLANETARY' | 'PRODUCTION';
	building_ticker: string;
	expertise: keyof typeof expertise;
}