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 TfIdPickerElement extends LitElement {
	static get properties() {
		return {
			ids: {type: Array},
			selected: {type: String},
		};
	}

	constructor() {
		super();
		this.load();
	}

	async load() {
		this.selected = await tfrpc.rpc.localStorageGet('whoami');
		this.ids = (await tfrpc.rpc.getIdentities()) || [];
	}

	changed(event) {
		this.selected = event.srcElement.value;
		tfrpc.rpc.localStorageSet('whoami', this.selected);
	}

	render() {
		if (this.ids) {
			return html`
				<select @change=${this.changed} style="max-width: 100%">
					${(this.ids).map(id => html`<option ?selected=${id == this.selected} value=${id}>${id}</option>`)}
				</select>
			`;
		} else {
			return html`<div>Loading...</div>`;
		}
	}
}
customElements.define('tf-id-picker', TfIdPickerElement);

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.* 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.* 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.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 = this.shadowRoot.getElementById('picker').selected;
			await tfrpc.rpc.appendMessage(whoami, {
				type: 'issue-edit',
				issues: [
					{
						link: id,
						open: open,
					},
				],
			});
			await this.load();
		}
	}

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

	async reply_to_issue(event) {
		let whoami = this.shadowRoot.getElementById('picker').selected;
		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>
			<tf-id-picker id="picker"></tf-id-picker>
		`;
		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);