"use strict"

const log = new LogClient()
const optStore = new OptRelay()

//const timeStart = Date.now()
const pvVersion = rt.getManifest().version
const pvName = rt.getManifest().name
const pvBuilt = rt.getManifest().version_name.substr(-11, 10)
const pvShortU = pvName.replace(/[^A-Z]/g, '')
const pvShortL = pvShortU.toLowerCase()
const pvCode = pvShortU.charCodeAt(0)-0x41
const ls = localStorage

const q = sel => document.querySelector(sel)
const qa = sel => document.querySelectorAll(sel)
const qi = sel => document.getElementById(sel)

const fieldSymbol = ["symbol", "instrument", "pair", "product_id", "marketSymbol", "instrument_name", "instId"]
const fieldAvgPrice = ["avgPrice", "avgPx", "avg_execution_price", "avgFillPrice", "price_avg", "average_filled_price"]
const fieldPrice = ["price", ...fieldAvgPrice, "limitPrice", "orderPrice", "px", "limit_price"]
const fieldQuantity = ["orderQty", "quantity", "units", "qty", "size", "original_amount", "startingAmount", "origQty", "volume", "vol", "sz", "amount", "orderQtyRq", "sizeRq", "base_size"]
const fieldId = ["orderID", "orderId", "order_id", "ordId", "id", "txid", "algoId", "uid", "comment"]
const fieldType = ["ordType", "type", "order_type", "orderType", "ordertype"]

document.addEventListener('DOMContentLoaded', init)

function formatEvent(evt, target) {
	const format = num => {
		num = parseFloat(num)
		const val = Math.abs(num)
		return isNaN(num) ? 'n/a' : num.toFixed(val >= 1000 ? 1 : val >= 100 ? 2 : val >= 10 ? 3 : 4)
	}

	try {
		evt = safeJSON(evt)
		const cmd = evt.command || {}
		const order = evt.order || {}

		let lev = order.leverage || cmd._lev || cmd.l
		lev = lev && parseFloat(lev).toFixed(1).replace(".0", '')
		target.innerText = `${format(order.getAny(fieldQuantity) || cmd.q)} ${(order.getAny(fieldSymbol) || cmd.s).substr(0, 16)} @ ${format(order.getAny(fieldPrice) || cmd.p)}${lev ? ` (${lev}x)`:''}`
		target.setAttribute('title', 
			(evt.alert ? `Alert: <code>${evt.name ? `${evt.name} #${evt.alert}` : (getAlerts(evt.alert) || {names:[evt.alert]}).names.join(', ')}</code>:${cmd._line}${cmd._count > 1 ? ':'+cmd._count:''}${cmd.a !== '*' ? ' @ '+cmd.a:''}${evt.side ? ` // ${evt.side.toUpperCase()}`:''}${evt.res ? ` (${formatTF(evt.res)})`:''}<br/>`:'')+
			`${evt.error || `Order: ${order.getAny(fieldId) || 'n/a'}${cmd.id ? ` (${cmd.id})`:''}`}<br/>`+
			(cmd.c ? `${cmd.c == "order" ? "Canceled" : "Placed"} ${cmd.b||''} ${order.getAny(fieldType) || cmd.t || ''} ${cmd.c}${cmd.c != "order" ? " closing order":''}!<br/>`:'')+
			`${cmd.e}${cmd.b ? ' // '+cmd.b:''} // ${cmd.t} (${cmd.u})<br/>`+
			formatDate(evt.date || Date.now()))
	} catch(ex) {}
}

function getDisableAllText() {
	return `${opt.disableAll ? 'Resume' : 'Disable'} running of newly received alerts (<code>Shift</code> to also ${opt.disableAll ? 'resume' : 'pause'} already running)`
}

async function toggleDisableAll(ev) {
	await optStore.load()
	opt.disableAll = !opt.disableAll
	optStore.save(), relayMsg('disableAllChanged', opt.disableAll)
	if (ev.shiftKey) relayMsg('pauseAlerts', opt.disableAll)

	if (opt.closeBG && opt.disableAll) {
		browser.storage.local.set({closeBG: true})
		relayMsg('closeBG', true)
		setTimeout(() => window.close(), 100)
	} else {
		if (!browser.extension.getViews().filter(view => view.location.pathname === '/background.html').length) {
			optStore.saveDirect()
			browser.tabs.create({url: rt.getURL("background.html"), active: false, selected: false})
			setTimeout(() => window.close(), 100)
		}
		browser.storage.local.set({closeBG: false})
		relayMsg('closeBG', false)
	}

	const disableAll = qi('disableAll')
	disableAll.classList.remove(opt.disableAll ? 'btn-danger' : 'btn-success')
	disableAll.classList.add(opt.disableAll ? 'btn-success' : 'btn-danger')
	$(disableAll).attr('data-original-title', getDisableAllText()).tooltip('show')
	return false
}

function checkZoom() {
	if (ls.zoomLevel && window.devicePixelRatio.toFixed(2) != ls.zoomLevel) {
		const zoom = Number(ls.zoomLevel) / window.devicePixelRatio
		document.body.style.zoom = zoom.toFixed(2)
	}
}

async function init() {
	setTimeout(checkZoom, 20)
	setTimeout(checkZoom, 40)
	setTimeout(checkZoom, 120)
	document.body.classList.add('animOff')
	q('#ver').innerText = pvVersion

	const info = (await relayMsg('ui.popup')) || {}
	formatEvent(info.lastSuccess, q('#success'))
	formatEvent(info.lastError, q('#error'))

	await optStore.load(true)
	const disableAll = qi('disableAll')
	if (opt.disableAll) {
		disableAll.classList.remove('btn-danger')
		disableAll.classList.add('btn-success')
	}
	disableAll.title = getDisableAllText()
	disableAll.addEventListener('click', toggleDisableAll)

	const ui = isEmail(info.id)
	const acc = qi('pv-account')
	acc.classList.add(ui ? 'panel-success' : 'panel-danger')
	acc.querySelector('.btn').innerText = ui ? "Connected" : "Disconnected!"
	acc.querySelector('.btn').classList.add(ui && 'btn-success' || 'btn-danger')

	let stats = {success: 0, warning: 0, danger: 0, total: 0}
	info.brokers?.forEach(ex => {
		ex.sub = ex.perm && ex.sub, ex.sub && ex.cred && stats.success++, ex.sub && !ex.cred && stats.warning++, !ex.sub && stats.danger++, stats.total++
		new Template('#exchange-link', null, {
			'exchange.state': ex.sub && ex.cred ? 'success' : !ex.perm ? 'default' : 'danger',
			'exchange.page': ex.sub ? "exchange-"+ex.alias.toLowerCase() : !ex.perm ? 'permissions' : 'license',
			'exchange.title': ex.sub ? "Setup Accounts" : !ex.perm ? "Setup Permissions" : "Check license status",
			'exchange.btn': ex.sub ? (ex.cred ? '<span class="glyphicon glyphicon-ok"></span>&nbsp; Active' : "Not set up") : 
				!ex.perm ? '<span class="text-muted"><span class="glyphicon glyphicon-remove"></span> Disabled</span>' : '<b>PRO ONLY!</b>',
			'exchange.url': ex.web,
			'exchange.name': ex.name,
		}).render()
	})
	const list = qi('exchanges')
	list.classList.add(stats.success ? 'panel-success' : stats.warning ? 'panel-warning' : stats.danger ? 'panel-danger' : 'nop')
	const btn = list.querySelector(".panel-heading .btn")
	btn.innerText = stats.success + " / " + stats.total
	btn.classList.add(stats.success ? 'btn-success' : stats.warning ? 'btn-warning' : stats.danger ? 'btn-danger' : 'nop')

	initBalances()
	opt.popupMax && maxBalances()
	checkTradingView(info)
	document.addEventListener('click', onClick)

	$(document.body).tooltip({selector: '[title]', placement: 'auto', container: 'body', html: true, trigger: 'hover', delay: {show: 250, hide: 50}})
	setTimeout(function(){document.body.classList.remove('animOff')}, 100)

	$.contextMenu2({
		selector: '#balances li',
		animation: {duration: 0, show: 'show', hide: 'hide'},
		zIndex: 99999,
		items: {
			update: {name: "Update Balance", icon: "glyphicon-refresh"},
			delete: {name: "Delete Balance", icon: "delete", ctrlKey: true},
			delete2: {name: "Delete Acct. Balances", icon: "delete", ctrlKey: true, all: true},
			s0: "-----",
			exportBal1: {name: "Export Balances (CSV)", icon: "glyphicon-open", action: "exportBalances"},
			exportBal2: {name: "Export Balances (Excel)", icon: "glyphicon-open", action: "exportBalances", altKey: true},
			copyBal: {name: "Copy Balances (Clipboard)", icon: "glyphicon-copy", action: "exportBalances", shiftKey: true},
			s1: "-----",
			stats: {name: "Show Positions", icon: "glyphicon-th-list", shiftKey: true},
			order: {name: "Open/Add Position...", icon: "add", altKey: true},
			close: {name: "Close Position...", icon: "glyphicon-remove-sign", altKey: true, ctrlKey: true},
			s2: "-----",
			cancel: {name: "Cancel Orders...", icon: "delete", ctrlKey: true, shiftKey: true},
			stops: {name: "Change TP/SL...", icon: "glyphicon-adjust", altKey: true, ctrlKey: true, shiftKey: true},
		},
		callback: function(key, item, ev) {
			item = item.items[key]
			key === "order" && (item.shiftKey = ev.shiftKey)
			window[item.action || "refreshBalance"](this[0], item)
		}
	})

	if (info.id) qi('account').innerText = info.id.substr(0, 27)
	if (info.uid) qi('tv-account').innerText = info.uid.substr(0, 20)
}

function openApp(page) {
	const url = rt.getURL("options.html"+(page === '#' ? '' : '#/'+page))
	const options = browser.extension.getViews({type:"tab"}).filter(view => view.location.pathname === '/options.html')[0]
	if (options?.location?.href) {
		options.location.href = url
		options.browser.tabs.getCurrent(tab => {
			browser.tabs.update(tab.id, {selected: true})
			browser.windows.update(tab.windowId, {focused: true})
		})
	} else {
		browser.tabs.create({url: url})
	}
}

function onClick(ev) {
	let el = ev.target
	if (el.nodeName === 'SPAN' && el.parentNode) {
		el = el.parentNode
	}

	if (['LI', 'BUTTON'].includes(el.nodeName)) {
		const data = el.dataset
		if (data.page) {
			ev.preventDefault()
			return openApp(data.page)
		}
		if (data.action) {
			if (typeof window[data.action] !== "function") {
				throw new SyntaxError("Action not found: " + data.action)
			}
			ev.preventDefault()
			window[data.action](el, ev)
		}
	}
}

async function selectTradingView(ev) {
	ev.preventDefault()
	const tabs = await getTradingViewTabs()
	if (tabs?.length) {
		browser.tabs.update(tabs.last().id, {selected: true})
		browser.windows.update(tabs.last().windowId, {focused: true})
	}
}

function checkTradingView(info) {
	let msg = "Disconnected!"
	let state = 'danger'
	if (info.isConnecting || info.isConnected) {
		msg = `${info.isConnecting ? "Connecting.." : "Connected"} (Stream)`
		state = 'success'
	} else if (info.hasAddon) {
		msg = "Connected (Addon)"
		state = 'warning'
	}

	const panel = qi('tradingview')
	const btn = panel.querySelector('.btn')
	btn.innerText = msg
	btn.classList.add('btn-'+state)
	panel.classList.add('panel-'+state)
	if (info.tvTabs) {
		panel.querySelector('.panel-heading a').addEventListener('click', selectTradingView)
	}
}

async function initBalances(clear) {
	const list = await relayMsg('broker.getBalances')
	if (!list) return

	clear && qa(".list-group-item-info").forEach(e => e.parentNode.removeChild(e))
	for(const entry of list) {
		const digits = entry.total > 999 ? 1 : entry.total > 99 ? 2 : entry.total > 9 ? 3 : 4
		new Template('#balance-item', null, {
			'balance.exchange': entry.name,
			'balance.exchangeS': smartTrim(entry.name, 19, true),
			'balance.account': entry.acc === '*' ? '' : entry.acc,
			'balance.accountS': entry.acc === '*' ? '' : smartTrim(entry.acc, 31),
			'balance.amount': entry.avail.toFixed(digits) === entry.total.toFixed(digits) ? 
				toFixed(entry.total, digits) : toFixed(entry.avail, digits-1)+" / "+toFixed(entry.total, digits-1),
			'balance.currency': entry.ccy,
			'balance.currencyUC': formatCcy(entry.ccy),
			'balance.updated': formatDate(entry.update),
			'balance.alias': entry.alias
		}).render()
	}
	if (list.length < 1) {
		qi('balances').classList.add('hide')
		opt.popupMax = false
	}
}

async function maxBalances(el) {
	el && (await optStore.load())
	opt.popupMax = document.body.classList.toggle('max')
	el && optStore.save()
}

async function refreshBalance(el, ev) {
	const data = el.dataset

	if (ev.shiftKey || ev.altKey) {
		const isCancel = !ev.altKey && ev.ctrlKey && ev.shiftKey
		if (await relayMsg('ui.positions', {ex: data.exchange, acc: data.account, do: ev.altKey || isCancel ? (ev.ctrlKey ? 'close' : 'add') : null, shiftKey: ev.shiftKey && !isCancel, ctrlKey: isCancel})) {
			openApp('positions')
		}
		return 
	}
	if (ev.ctrlKey) {
		relayMsg('broker.removeBalance', data.exchange, data.account, !ev.all && data.currency)
		$(".tooltip").tooltip("destroy")
		initBalances(true)
		return
	}

	q("#balances").classList.add('loading')
	q("button[data-action=refreshBalance]").disabled = true

	if (data.exchange) {
		await relayMsg('broker.updateBalance', data.exchange, data.account, data.currency)
	} else {
		const list = await relayMsg('broker.listBalances', opt.refreshAll)

		// $$$$ progressbar + cancel!
		for (const i of list) {
			await relayMsg('broker.updateBalance', i.alias, i.acc, i.ccy)
		}
	}

	initBalances(true)
	q("#balances").classList.remove('loading')
	q("button[data-action=refreshBalance]").disabled = false
}

async function exportBalances(el, ev) {
	const cols = ["Exchange","Alias","Account","Asset","Available","Total","Updated"]
	if (ev.shiftKey) {
		let output = cols.join() + newLine
		const list = await relayMsg('broker.getBalances')
		list.forEach(e => output += `${e.name},${e.alias},${e.acc},${e.ccy},${e.avail},${e.total},${formatDate(e.update)}${newLine}`)
		clipboardWrite("text/plain", output)
		return
	}

	const format = ev.altKey ? 'excel' : 'csv'
	$(document.body).append(`<table id="export" style="display:none"></table>`)
	$('#export').DataTable({
		searching: false,
		ordering: false,
		paging: false,
		info: false,
		dom: 'Bt',
		buttons: [
			{extend: format, filename: `${pvName.toLowerCase()}${opt.instName ? '-'+opt.instName : ''}-${formatDate(new Date()).replace(/[^0-9]/g, '-')}-balances`, title: ''}
		],
		columns: cols.map(e => {return {title: e}}),
		data: await relayMsg('broker.getBalances', true),
		initComplete: function() {
			$('#export').DataTable().button(0).trigger()
			$('#export_wrapper').remove()
			const msg = `All balance info exported as ${format.toUpperCase()}!`
			log.notice(msg)
		}
	})
}
