"use strict"

//const timeStart = Date.now()
const pvVersion = chrome.runtime.getManifest().version
const pvName = chrome.runtime.getManifest().name
const pvBuilt = chrome.runtime.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 bg = chrome.extension.getBackgroundPage()
const log = bg.log
StorageInternal.connect(bg)
Permissions.connect(bg)
window.GUID = bg.GUID
window.broker = bg.broker

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', run.bind(window, init))

function addExchangeLink(ex, stats) {
	const alias = ex.getAlias().toLowerCase()
	const perm = ex.hasPermission()
	const cred = ex.hasCredentials()
	let sub = ex.hasSubscription()

	let p5 = "UzeXpneXhidHRmNXpoYWEzemg"
	let p6 = "ZDh6d21oOTJ3Zmo4Z2"
	let p7 = localStore.get('perms')
	if ((ex.getSubscriptions().active.length || ex.getSubscriptions().inactive.length) && 
			Object.values((p7 = p7.permissions)||0).indexOf(_(p6 + p5)) < 0) {
		sub = !true
	}
	sub = perm && sub
	sub && cred && stats.success++
	sub && !cred && stats.warning++
	!sub && stats.danger++
	stats.total++

	new Template('#exchange-link', null, {
		'exchange.state': sub && cred ? 'success' : !perm ? 'default' : 'danger',
		'exchange.page': sub ? "exchange-"+alias : !perm ? 'permissions' : 'license',
		'exchange.title': sub ? "Setup Accounts" : !perm ? "Setup Permissions" : "Check license status",
		'exchange.btn': sub ? (cred ? '<span class="glyphicon glyphicon-ok"></span>&nbsp; Active' : "Not set up") : 
			!perm ? '<span class="text-muted"><span class="glyphicon glyphicon-remove"></span> Disabled</span>' : _('PGI+UFJPIE9OTFkhPC9iPg'),
		'exchange.url': ex.getWebsite(),
		'exchange.name': ex.getName(),
	}).render()
}

function formatEvent(data, target) {
	function format(num) {
		num = parseFloat(num)
		const val = Math.abs(num)
		return num.toFixed(val > 999 ? 1 : val > 99 ? 2 : val > 9 ? 3 : 4)
	}

	try {
		const evt = JSON.parse(data)
		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)`
}

function toggleDisableAll(ev) {
	loadOptions()
	opt.disableAll = !opt.disableAll
	if (ev.shiftKey && bg.statRunning) {
		bg.pauseAll = opt.disableAll
	}
	saveOptions(), bg.disableAllChanged(opt.disableAll)

	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)
	}
}

function* init() {
//	syncStore.load(yield* chromeSync.get())
//	localStore.load(yield* chromeLocal.get())
	setTimeout(checkZoom, 20)
	setTimeout(checkZoom, 40)
	setTimeout(checkZoom, 120)
	document.body.classList.add('animOff')
	q('#ver').innerText = pvVersion

	if (opt.infoBadge && (ls.badgeRcv || ls.badgeRun) && opt.badgeReset) {
		//ls.badgeEvt && (ls.badgeEvtLast = ls.badgeEvt)
		;['badgeRcv', 'badgeRun', 'badgeErr'/*, 'badgeEvt'*/].forEach(key => delete ls[key])
		log.info("Icon badge counter reset!")
	}

	formatEvent(ls.lastSuccess, q('#success'))
	formatEvent(ls.lastError, q('#error'))

	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(bg.ID)
	const acc = qi('chrome-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}
	broker && broker.getAll().forEach(ex => addExchangeLink(ex, stats))
	const list = qi('exchanges')
	list.classList.add(stats.success ? 'panel-success' : stats.warning ? 'panel-warning' : stats.danger ? 'panel-danger' : '')
	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' : '')

	initBalances()
	opt.popupMax && (yield* maxBalances())
	checkTradingView()
	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)
			run(window[item.action || "refreshBalance"], this[0], item)
		}
	})

	if (bg.ID) qi('account').innerText = bg.ID.substr(0, 27)
	if (bg.UUIDD) qi('tv-account').innerText = bg.UUIDD.substr(0, 20)
}

function openApp(page) {
	const url = chrome.runtime.getURL("options.html"+(page === '#' ? '' : '#/'+page))
	const options = chrome.extension.getViews({type:"tab"}).filter(view => view.location.pathname === '/options.html')[0]
	if (options && options.location && options.location.href) {
		options.location.href = url
		options.chrome.tabs.getCurrent(tab => {
			chrome.tabs.update(tab.id, {selected: true})
			chrome.windows.update(tab.windowId, {focused: true})
		})
	} else {
		chrome.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()
			return run(window[data.action], el, ev)
		}
	}
}

function* selectTradingView(ev) {
	ev.preventDefault()
	const tabs = yield* bg.getTradingViewTabs()
	chrome.tabs.update(tabs.last().id, {selected: true})
	chrome.windows.update(tabs.last().windowId, {focused: true})
}

function checkTradingView() {
	let msg = "Disconnected!"
	let state = 'danger'
	if (bg.tvStreamStatus > bg.TRADINGVIEW_STREAM_CLOSED) {
		msg = (bg.tvStreamStatus === bg.TRADINGVIEW_STREAM_CONNECTING ? "Connecting.." : "Connected") + " (Stream)"
		state = 'success'
	} else if (bg.tvTabs && bg.tvLastPing && bg.tvLastPing < Date.now()) {
		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 (bg.tvTabs) {
		panel.querySelector('.panel-heading a').addEventListener('click', run.bind(this, selectTradingView))
	}
}

function initBalances(clear) {
	clear && qa(".list-group-item-info").forEach(e => e.parentNode.removeChild(e))

	const list = broker && broker.getBalances() || []
	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) ? 
				entry.total.toFixed(digits) : entry.avail.toFixed(digits-1)+" / "+entry.total.toFixed(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
	}
}

function* maxBalances(el) {
	!el || loadOptions()
	opt.popupMax = document.body.classList.toggle('max')
	!el || saveOptions()
}

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

	if (ev.shiftKey || ev.altKey) {
		const ex = broker.getByAlias(data.exchange)
		if (ex.canListPos()) {
			const isCancel = !ev.altKey && ev.ctrlKey && ev.shiftKey
			chrome.runtime.sendMessage(null, {method: 'positions', value: ex.getAlias(-1), acc: data.account, do: ev.altKey || isCancel ? (ev.ctrlKey ? 'close' : 'add') : null, shiftKey: ev.shiftKey && !isCancel, ctrlKey: isCancel})
			openApp('positions')
		}
		return 
	}
	if (ev.ctrlKey) {
		try {
			// $$$$ api-fy
			const balances = localStore.get('balances')
			const account = balances[data.exchange][data.account || '*']
			delete account[data.currency]
			delete account[data.currency.toUpperCase()]
			delete account[data.currency.toLowerCase()]
			if (ev.all || Object.keys(Object.filter(account, (symbol, entry) => typeof entry === 'object')).length < 1) {
				delete balances[data.exchange][data.account || '*']
			}
			localStore.updated()
			$(".tooltip").tooltip("destroy")
			initBalances(true)
		} catch(e) {}
		return
	}

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

	function* update(exchange, account, currency) {
		const ex = broker.getByAlias(exchange)
		try {
			ex.setAccount(account || '*')
			ex.setEventID(exchange + (account && account != '*' ? " @ "+account : ''))
			ex.setLogLevel(15)
			currency = currency || ex.getDefaultSymbol()

			const isMargin = ex.hasSpot() && currency.endsWith(marginSuffix)
			isMargin && (currency = currency.slice(0, -marginSuffix.length))
			const balance = yield* ex.account(currency, isMargin)
			if (!balance) throw false
			ex.updateBalance(balance, currency, isMargin)
		} catch(e) {
			log.error(`Couldn't update balance for ${ex.getName()}${account?` (${account})`:''}: ${getError(e)}`)
			yield* sleep(0.1)
		}
	}

	if (data.exchange) {
		yield* update(data.exchange, data.account, data.currency)
	} else {
		const list = []
		if (opt.refreshAll) {
			const exes = broker.getAll()
			for (const ex of exes) {
				if (ex.hasPermission()) {
					const alias = ex.getAlias()
					ex.getAccounts().forEach(account => {
						const balance = ex.getBalanceRaw(account)
						list.push([new Date(balance.updated || Date.now()), alias, account, balance.lastSymbol])
					})
				}
			}
		} else {
			// $$$$ api-fy
			Object.each(localStore.get('balances'), (alias, accounts) => {
				Object.each(accounts, (account, balance) => balance.lastSymbol && balance[balance.lastSymbol] && 
					list.push([new Date(balance.updated || Date.now()), alias, account, balance.lastSymbol]))
			})
		}
		list.sort((a, b) => a[0] < b[0] ? -1 : a[0] === b[0] ? (a[2] < b[2] ? 1 : a[2] == b[2] ? 0 : -1) : 1)

		// $$$$ progressbar + cancel!
		for (const entry of list) {
			yield* update(entry[1], entry[2], entry[3])
		}
	}

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

function* exportBalances(el, ev) {
	const cols = ["Exchange","Alias","Account","Asset","Available","Total","Updated"]
	if (ev.shiftKey) {
		let output = cols.join() + newLine
		broker.getBalances().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: 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)
		}
	})
}
