import {LitElement, html, unsafeHTML} from './lit-all.min.js';
import * as tfrpc from '/static/tfrpc.js';
import * as tfutils from './tf-utils.js';

const k_project = '%Hr+4xEVtjplidSKBlRWi4Aw/0Tfw7B+1OR9BzlDKmOI=.sha256';

class TfComposeElement extends LitElement {
	static get properties() {
		return {
			value: {type: String},
		};
	}

	input() {
		let input = this.renderRoot.getElementById('input');
		let preview = this.renderRoot.getElementById('preview');
		if (input && preview) {
			preview.innerHTML = tfutils.markdown(input.value);
		}
	}

	submit() {
		this.dispatchEvent(
			new CustomEvent('tf-submit', {
				bubbles: true,
				composed: true,
				detail: {
					value: this.renderRoot.getElementById('input').value,
				},
			})
		);
		this.renderRoot.getElementById('input').value = '';
		this.input();
	}

	render() {
		return html`
			<div style="display: flex; flex-direction: row">
				<textarea id="input" @input=${this.input} style="flex: 1 1">${this.value}</textarea>
				<div id="preview" style="flex: 1 1"></div>
			</div>
			<input type="submit" value="Submit" @click=${this.submit}></input>
		`;
	}
}
customElements.define('tf-compose', TfComposeElement);

class TfIssuesAppElement extends LitElement {
	static get properties() {
		return {
			issues: {type: Array},
			selected: {type: Object},
		};
	}

	constructor() {
		super();
		this.issues = [];
		this.load();
	}

	async load() {
		let issues = {};
		let messages = await tfrpc.rpc.query(
			`
			WITH issues AS (SELECT messages.id, json(messages.content) AS content, messages.author, messages.timestamp FROM messages_refs JOIN messages ON
				messages.id = messages_refs.message
				WHERE messages_refs.ref = ? AND json_extract(messages.content, '$.type') = 'issue'),
			edits AS (SELECT messages.id, json(messages.content) AS content, messages.author, messages.timestamp FROM issues JOIN messages_refs ON
				issues.id = messages_refs.ref JOIN messages ON
				messages.id = messages_refs.message
				WHERE json_extract(messages.content, '$.type') IN ('issue-edit', 'post'))
			SELECT * FROM issues
			UNION
			SELECT * FROM edits ORDER BY timestamp
		`,
			[k_project]
		);
		for (let message of messages) {
			let content = JSON.parse(message.content);
			switch (content.type) {
				case 'issue':
					issues[message.id] = {
						id: message.id,
						author: message.author,
						text: content.text,
						updates: [],
						created: message.timestamp,
						open: true,
					};
					break;
				case 'issue-edit':
				case 'post':
					for (let issue of content.issues || []) {
						if (issues[issue.link]) {
							if (issue.open !== undefined) {
								issues[issue.link].open = issue.open;
								message.open = issue.open;
							}
							issues[issue.link].updates.push(message);
							issues[issue.link].updated = message.timestamp;
						}
					}
					break;
			}
		}
		this.issues = Object.values(issues).sort(
			(x, y) => y.open - x.open || y.created - x.created
		);
		if (this.selected) {
			for (let issue of this.issues) {
				if (issue.id == this.selected.id) {
					this.selected = issue;
				}
			}
		}
	}

	render_issue_table_row(issue) {
		return html`
			<tr>
				<td>${issue.open ? '☐ open' : '☑ closed'}</td>
				<td
					style="max-width: 8em; overflow: hidden; white-space: nowrap; text-overflow: ellipsis"
				>
					${issue.author}
				</td>
				<td
					style="max-width: 40em; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; cursor: pointer"
					@click=${() => (this.selected = issue)}
				>
					${issue.text.split('\n')?.[0]}
				</td>
				<td>
					${new Date(issue.updated ?? issue.created).toLocaleDateString()}
				</td>
			</tr>
		`;
	}

	render_update(update) {
		let content = JSON.parse(update.content);
		let message;
		if (content.text) {
			message = unsafeHTML(tfutils.markdown(content.text));
		}
		return html`
			<div style="border-left: 2px solid #fff; padding-left: 8px">
				<div>${new Date(update.timestamp).toLocaleString()}</div>
				<div>${update.author}</div>
				<div>${message}</div>
				<div>
					${update.open !== undefined
						? update.open
							? 'issue opened'
							: 'issue closed'
						: undefined}
				</div>
			</div>
		`;
	}

	async set_open(id, open) {
		if (
			confirm(`Are you sure you want to ${open ? 'open' : 'close'} this issue?`)
		) {
			let whoami = await tfrpc.rpc.getActiveIdentity();
			await tfrpc.rpc.appendMessage(whoami, {
				type: 'issue-edit',
				issues: [
					{
						link: id,
						open: open,
					},
				],
			});
			await this.load();
		}
	}

	async create_issue(event) {
		let whoami = await tfrpc.rpc.getActiveIdentity();
		await tfrpc.rpc.appendMessage(whoami, {
			type: 'issue',
			project: k_project,
			text: event.detail.value,
		});
		await this.load();
	}

	async reply_to_issue(event) {
		let whoami = await tfrpc.rpc.getActiveIdentity();
		await tfrpc.rpc.appendMessage(whoami, {
			type: 'post',
			text: event.detail.value,
			root: this.selected.id,
			branch: this.selected.updates.length
				? this.selected.updates[this.selected.updates.length - 1].id
				: this.selected.id,
			issues: [
				{
					link: this.selected.id,
				},
			],
		});
		await this.load();
	}

	render() {
		let header = html` <h1>Tilde Friends Issues</h1> `;
		if (this.selected) {
			return html`
				${header}
				<div>
					<input type="button" value="Back" @click=${() => (this.selected = undefined)}></input>
					${
						this.selected.open
							? html`<input type="button" value="Close Issue" @click=${() => this.set_open(this.selected.id, false)}></input>`
							: html`<input type="button" value="Reopen Issue" @click=${() => this.set_open(this.selected.id, true)}></input>`
					}
				</div>
				<div>${new Date(this.selected.created).toLocaleString()}</div>
				<div>${this.selected.author}</div>
				<div>${this.selected.id}</div>
				<div>${unsafeHTML(tfutils.markdown(this.selected.text))}</div>
				${this.selected.updates.map((x) => this.render_update(x))}
				<tf-compose @tf-submit=${this.reply_to_issue}></tf-compose>
			`;
		} else {
			return html`
				${header}
				<h2>New Issue</h2>
				<tf-compose @tf-submit=${this.create_issue}></tf-compose>
				<table>
					<tr>
						<th>Status</th>
						<th>Author</th>
						<th>Title</th>
						<th>Date</th>
					</tr>
					${this.issues.map((x) => this.render_issue_table_row(x))}
				</table>
			`;
		}
	}
}

customElements.define('tf-issues-app', TfIssuesAppElement);
