import { CustomHTMLElement } from './lib/custom_html_element.mjs'
import { Node } from 'node_store'
import { MarkedPosition } from './marked_position.mjs'


export class N2PageHistory extends CustomHTMLElement {
	static PAGESIZE = 15
	static {// {{{
		this.tmpl = document.createElement('template')
		this.tmpl.innerHTML = `
			<style>
				n2-pagehistory {
					margin-top: 32px;
				}
			</style>

			<div class="back">
				<img data-el="back-image" src="/images/${_VERSION}/icon_back.svg" class="colorize">
				<div data-el="back-text">Back to node</div>
			</div>

			<div class="node-name">
				<img src="/images/${_VERSION}/icon_history.svg" class="colorize">
				<h1 data-el="node-name"></h1>
			</div>

			<div class="column-1">
				<div class="group-label">Actions</div>
				<div class="group">
					<button data-el="download-history">Fetch all history from server</button>
					<div data-el="fetch-history-progress"></div>
				</div>


				<div class="group-label">History</div>
				<div class="group">
					<div data-el="stats">
						<div>History on server:</div>
						<div data-el="stats-on-server"></div>

						<div>History on client:</div>
						<div data-el="stats-on-client"></div>
					</div>

					<div data-el="nodes"></div>

					<div data-el="pagination">
						<div data-el="prev">&lt;</div>
						<div data-el="page"></div>
						<div data-el="next">&gt;</div>
					</div>
				</div>
			</div>

			<div class="column-2">
				<div class="group-label">Document</div>
				<div class="group">
					<div data-el="node-markdown"></div>
				</div>
			</div>
		`
	}// }}}

	constructor() {// {{{
		super()
		this.selectedNode = null

		this.setAttribute('tabindex', '-1')
		this.addEventListener('keydown', event => this.keyHandler(event))

		// Connect back icon and text to give the user a way back to the node.
		this.elBackImage.addEventListener('click', () => _mbus.dispatch('SHOW_PAGE', { page: 'node' }))
		this.elBackText.addEventListener('click', () => _mbus.dispatch('SHOW_PAGE', { page: 'node' }))
		this.elPrev.addEventListener('click', () => this.prevPage())
		this.elNext.addEventListener('click', () => this.nextPage())
		this.elDownloadHistory.addEventListener('click', async () => {
			await this.downloadHistory()
			await this.useNode(this.node)
			this.render(true)

		})

		_mbus.subscribe('SHOW_PAGE', async (event) => {
			if(event.detail.data.page != 'history')
				return

			await this.useNode(_app.nodeUI.node)
			this.render()
		})

		_mbus.subscribe('HISTORY_NODE_SELECTED', (event) => {
			this.selectedNode = event.detail.data.historyNode

			// Any selected history node is rendered with markdown.
			const marked = new MarkedPosition()
			this.elNodeMarkdown.innerHTML = marked.parse(this.selectedNode?.node.content())
		})
	}// }}}
	async render(keepFetchHistoryProgress) {// {{{
		this.elNodeName.innerText = this.node.get('Name')
		this.elPage.innerText = `${this.page} / ${this.pages}`
		this.elStatsOnClient.innerText = `${this.nodesTotal}`
		this.elStatsOnServer.innerText = `${this.historyOnServerTotal}`

		if (this.nodesTotal <= N2PageHistory.PAGESIZE)
			this.elPagination.style.display = 'none'
		else
			this.elPagination.style.display = ''

		let nodes = await nodeStore.nodesHistory.retrievePage(this.node.UUID, N2PageHistory.PAGESIZE, this.page)
		let i = 0
		let divs = nodes.map(n => {
			i++
			const index = 1 + this.nodesTotal - (N2PageHistory.PAGESIZE * (this.page - 1) + i)
			const div = new N2PageHistoryNode(n, index)
			div.render()
			return div
		})
		this.elNodes.replaceChildren(...divs)

		if (!keepFetchHistoryProgress)
			this.elFetchHistoryProgress.innerText = ''

		// Select the first node.
		if (!this.selectedNode) {
			this.elNodes.firstElementChild?.select()
		}
	}// }}}

	async useNode(node) {// {{{
		this.node = node
		this.page = 1

		this.nodesTotal = await nodeStore.nodesHistory.count(this.node.UUID)
		this.historyOnServerTotal = await this.getServerTotal()
		this.pages = Math.ceil(this.nodesTotal / N2PageHistory.PAGESIZE)
	}// }}}
	keyHandler(event) {// {{{
		let handled = true
		switch (event.key) {
			case 'ArrowLeft':
				this.prevPage()
				break

			case 'ArrowRight':
				this.nextPage()
				break

			case 'ArrowUp':
				const prevNode = this.selectedNode?.previousElementSibling
				if (prevNode)
					prevNode.select()
				break

			case 'ArrowDown':
				const nextNode = this.selectedNode?.nextElementSibling
				if (nextNode)
					nextNode.select()
				break

			default:
				handled = false
		}

		if (handled) {
			event.stopPropagation()
			event.preventDefault()
		}
	}// }}}

	prevPage() {// {{{
		if (this.page == 1)
			return

		// Selecting a node on another page is wrong.
		this.selectedNode = null
		this.page--
		this.render()
	}// }}}
	nextPage() {// {{{
		if (this.page >= this.pages)
			return
		// Selecting a node on another page is wrong.
		this.selectedNode = null
		this.page++
		this.render()
	}// }}}

	async getServerTotal() {// {{{
		const res = await fetch(`/node/history/count/${this.node.UUID}`, {
			headers: {
				"Authorization": 'Bearer ' + localStorage.getItem('token'),
			}
		})
		const json = await res.json()

		if (!json.OK) {
			alert(json.Error)
			return
		}

		return json.Count
	}// }}}
	async downloadHistory() {// {{{
		try {
			const nodes = []
			let offset = 0
			let hasMore = true

			while (hasMore) {
				const history = await this.downloadHistoryPage(offset)
				hasMore = history.HasMore
				for (const nodeData of history.Nodes) {
					nodes.push(new Node(nodeData))
				}
				offset = nodes.length
				this.elFetchHistoryProgress.innerText = `${nodes.length} fetched.`
			}

			let num = 0
			for (const node of nodes) {
				const ok = await nodeStore.nodesHistory.hasNode(node.UUID, node.get('Updated'))
				if (ok) num++
				await nodeStore.nodesHistory.add(node)
			}

			this.elFetchHistoryProgress.innerText = `${nodes.length} fetched - all history fetched.`
		} catch (e) {
			console.error(e)
			alert(e)
		}
	}// }}}
	async downloadHistoryPage(offset) {// {{{
		const res = await fetch(`/node/history/retrieve/${this.node.UUID}/${offset}`, {
			headers: {
				"Authorization": 'Bearer ' + localStorage.getItem('token'),
			}
		})
		const json = await res.json()

		if (!json.OK) {
			alert(json.Error)
			return
		}

		return json
	}// }}}
}
customElements.define('n2-pagehistory', N2PageHistory)


class N2PageHistoryNode extends CustomHTMLElement {
	static {// {{{
		this.tmpl = document.createElement('template')
		this.tmpl.innerHTML = `
			<div data-el="index"></div>
			<div data-el="updated"><span data-el="date"></span> <span data-el="time"></span></div>
			<div data-el="size"></div>
			<div data-el="name"></div>
		`
	}// }}}
	constructor(node, index) {// {{{
		super()

		this.node = node
		this.index = index

		this.style.display = 'contents'
		this.selected = false

		this.addEventListener('click', () => this.select())

		// Another history node has been selected.
		_mbus.subscribe('HISTORY_NODE_SELECTED', (event) => {
			if (this.node.get('Updated') == event.detail.data.historyNode.node.get('Updated'))
				return
			this.selected = false
			this.render()
		})
	}// }}}

	select() {// {{{
		this.selected = true
		// Other nodes are told to unselect and rerender.
		_mbus.dispatch('HISTORY_NODE_SELECTED', { historyNode: this })
		this.render()
	}// }}}
	render() {// {{{
		const date = this.node.get('Updated').slice(0, 10)
		const time = this.node.get('Updated').slice(11, 19)

		if (this.selected)
			this.classList.add('selected')
		else
			this.classList.remove('selected')

		this.elIndex.innerText = this.index
		this.elDate.innerText = date
		this.elTime.innerText = time
		this.elSize.innerText = this.formatSize(this.node.get('Content').length)
		this.elName.innerText = this.node.get('Name')
	}// }}}
	formatSize(s) {// {{{
		let div = 1
		let unit = 'B'
		if (s >= 1048576) {
			div = 1048576
			unit = 'MB'
		} else if (s >= 1024) {
			div = 1024
			unit = 'kB'
		}

		return new Intl.NumberFormat(undefined, {
			maximumFractionDigits: 0
		}).format(Math.round(s / div)) + ' ' + unit
	}// }}}
}
customElements.define('n2-pagehistorynode', N2PageHistoryNode)
