<template>
	<div>
		<table>
			<thead>
				<tr>
					<th>id</th>
					<th>name</th>
					<th>description</th>
					<th>p</th>
					<th>s</th>
					<th v-if="edit"></th>
				</tr>
			</thead>
			<tbody>
				<template v-if="edit">
					<tr
						v-for="event in events"
						:key="event.id"
						:class="{ removed: removed.has(event.id) }"
					>
						<td>{{ event.id }}</td>
						<td>
							<input
								type="text"
								:value="event.name"
								:disabled="!canEdit(event)"
								@input="editEvent(event, { name: ($event as any).target.value })"
							>
						</td>
						<td>
							<input
								type="text"
								:disabled="!canEdit(event)"
								:value="event.description"
								@input="editEvent(event, { description: ($event as any).target.value })"
							>
						</td>
						<td>
							<input
								type="checkbox"
								:disabled="!canEditPublic"
								:value="!event.crew"
								:checked="!event.crew"
								@change="editEvent(event, { crew: !($event as any).target.value })"
							>
						</td>
						<td>{{ event.slots.length ? event.slots.length : "" }}</td>
						<td>
							<button
								type="button"
								:disabled="!canEdit(event) || removed.has(event.id)"
								@click="delEvent(event.id)"
							>Delete</button>
							<button
								v-if="changes.some(c => c.data.id === event.id)"
								type="button"
								@click="revertEvent(event.id)"
							>Revert</button>
						</td>
					</tr>
					<tr>
						<td>{{ toId(newEventName) }}</td>
						<td>
							<input
								type="text"
								v-model="newEventName"
							>
						</td>
						<td>
							<input
								type="text"
								v-model="newEventDescription"
							>
						</td>
						<td>
							<input
								type="checkbox"
								:disabled="!canEditPublic"
								v-model="newEventPublic"
							>
						</td>
						<td></td>
						<td>
							<button
								v-if="eventExists(newEventName)"
								disabled
							>Event already exists</button>
							<button
								v-else
								type="button"
								@click="newEvent"
							>Add Event</button>
						</td>
					</tr>
				</template>
				<template v-else>
					<tr
						v-for="event in events"
						:key="event.id"
					>
						<td>{{ event.id }}</td>
						<td>{{ event.name }}</td>
						<td>{{ event.description }}</td>
						<td>{{ event.crew ? "" : "Yes"}}</td>
						<td>{{ event.slots.length ? event.slots.length : "" }}</td>
					</tr>
				</template>
			</tbody>
		</table>
		<p v-if="changes.length">
			Changes are not saved yet.
			<button
				type="button"
				@click="saveEvents"
			>Save Changes</button>
		</p>
		<details>
			<summary>Debug</summary>
			<ol>
				<li v-for="change in changes">
					{{ JSON.stringify(change) }}
				</li>
			</ol>
		</details>
	</div>
</template>

<script lang="ts" setup>
import type { ChangeRecord, ScheduleEvent } from '~/shared/types/schedule';
import { applyChangeArray } from '~/shared/utils/changes';
import { toId } from '~/shared/utils/functions';

defineProps<{
	edit?: boolean,
}>();

const schedule = await useSchedule();
const { data: session } = await useAccountSession();
const canEditPublic = computed(() => session.value?.account.type === "admin");

function canEdit(event: ScheduleEvent) {
	return event.crew || canEditPublic.value;
}

const changes = ref<ChangeRecord<ScheduleEvent>[]>([]);
const removed = computed(() => new Set(changes.value.filter(c => c.op === "del").map(c => c.data.id)));
function replaceChange(
	change: ChangeRecord<ScheduleEvent>,
	changes: ChangeRecord<ScheduleEvent>[],
) {
	const index = changes.findIndex(item => (
		item.op === change.op && item.data.id === change.data.id
	));
	const copy = [...changes];
	if (index !== -1)
		copy.splice(index, 1, change);
	else
		copy.push(change);
	return copy;
}
function revertChange(id: string, changes: ChangeRecord<ScheduleEvent>[]) {
	return changes.filter(change => change.data.id !== id);
}

const newEventName = ref("");
const newEventDescription = ref("");
const newEventPublic = ref(false);
function editEvent(
	event: ScheduleEvent,
	edits: { name?: string, description?: string, crew?: boolean }
) {
	const copy = { ...event };
	if (edits.name !== undefined) {
		copy.name = edits.name;
	}
	if (edits.description !== undefined) {
		copy.description = edits.description || undefined;
	}
	if (edits.crew !== undefined) {
		copy.crew = edits.crew || undefined;
	}
	const change = { op: "set" as const, data: copy };
	changes.value = replaceChange(change, changes.value);
}
function delEvent(id: string) {
	const change = { op: "del" as const, data: { id } };
	changes.value = replaceChange(change, changes.value);
}
function revertEvent(id: string) {
	changes.value = revertChange(id, changes.value);
}
function eventExists(name: string) {
	const id = toId(name);
	return (
		schedule.value.events.some(e => e.id === id)
		|| changes.value.some(c => c.data.id === id)
	);
}
function newEvent() {
	if (eventExists(newEventName.value)) {
		alert(`Event ${newEventName.value} already exists`);
		return;
	}
	const change = {
		op: "set" as const,
		data: {
			id: toId(newEventName.value),
			name: newEventName.value,
			description: newEventDescription.value || undefined,
			crew: !newEventPublic.value || undefined,
			slots: [],
		},
	};
	changes.value = replaceChange(change, changes.value);
	newEventName.value = "";
	newEventDescription.value = "";
	newEventPublic.value = false;
}
async function saveEvents() {
	try {
		await $fetch("/api/schedule", {
			method: "PATCH",
			body: { events: changes.value },
		});
		changes.value = [];
	} catch (err: any) {
		console.error(err);
		alert(err?.data?.message ?? err.message);
	}
}

const events = computed(() => {
	const data = [...schedule.value.events];
	applyChangeArray(changes.value.filter(change => change.op === "set"), data);
	return data;
});
</script>

<style scoped>
table {
	border-spacing: 0;
}
table th {
	text-align: left;
	border-bottom: 1px solid var(--foreground);
}
table :is(th, td) + :is(th, td) {
	padding-inline-start: 0.4em;
}
.removed :is(td, input) {
	text-decoration: line-through;
}
</style>
