<template>
	<div v-if="schedule.deleted">
		Error: Unexpected deleted schedule.
	</div>
	<div v-else>
		<table>
			<thead>
				<tr>
					<th>id</th>
					<th>name</th>
					<th>role</th>
					<th>s</th>
					<th>description</th>
					<th v-if="edit"></th>
				</tr>
			</thead>
			<tbody>
				<template v-if="edit">
					<tr
						v-for="shift in shifts?.filter(s => !s.deleted)"
						:key="shift.id"
						:class="{ removed: removed.has(shift.id) }"
					>
						<td>{{ shift.id }}</td>
						<td>
							<input
								type="text"
								:value="shift.name"
								@input="editShift(shift, { name: ($event as any).target.value })"
							>
						</td>
						<td>
							<select
								:value="shift.roleId"
								@change="editShift(shift, { roleId: ($event as any).target.value })"
							>
								<option
									v-for="role in schedule.roles?.filter(r => !r.deleted)"
									:key="role.id"
									:value="role.id"
									:selected="shift.roleId === role.id"
								>{{ role.name }}</option>
							</select>
						</td>
						<td>{{ shift.slots.length ? shift.slots.length : "" }}</td>
						<td>
							<input
								type="text"
								:value="shift.description"
								@input="editShift(shift, { description: ($event as any).target.value })"
							>
						</td>
						<td>
							<button
								type="button"
								:disabled="removed.has(shift.id)"
								@click="delShift(shift.id)"
							>Delete</button>
							<button
								v-if="changes.some(c => c.id === shift.id)"
								type="button"
								@click="revertShift(shift.id)"
							>Revert</button>
						</td>
					</tr>
					<tr>
						<td>{{ newShiftId }}</td>
						<td>
							<input
								type="text"
								v-model="newShiftName"
							>
						</td>
						<td>
							<select v-model="newShiftRole">
								<option
									v-for="role in schedule.roles?.filter(r => !r.deleted)"
									:key="role.id"
									:value="role.id"
									:selected="role.id === newShiftRole"
								>{{ role.name }}</option>
							</select>
						</td>
						<td></td>
						<td>
							<input
								type="text"
								v-model="newShiftDescription"
							>
						</td>
						<td>
							<button
								v-if="shiftExists(newShiftName)"
								disabled
							>Shift already exists</button>
							<button
								v-else
								type="button"
								@click="newShift"
							>Add Shift</button>
						</td>
					</tr>
				</template>
				<template v-else>
					<tr
						v-for="shift in shifts?.filter(s => !s.deleted)"
						:key="shift.id"
					>
						<td>{{ shift.id }}</td>
						<td>{{ shift.name }}</td>
						<td>{{ shift.roleId }}</td>
						<td>{{ shift.slots.length ? shift.slots.length : "" }}</td>
						<td>{{ shift.description }}</td>
					</tr>
				</template>
			</tbody>
		</table>
		<p v-if="changes.length">
			Changes are not saved yet.
			<button
				type="button"
				@click="saveShifts"
			>Save Changes</button>
		</p>
		<details>
			<summary>Debug</summary>
			<ol>
				<li v-for="change in changes">
					<pre><code>{{ JSON.stringify(change, undefined, 2) }}</code></pre>
				</li>
			</ol>
		</details>
	</div>
</template>

<script lang="ts" setup>
import type { ApiSchedule, ApiScheduleShift } from '~/shared/types/api';
import { toId } from '~/shared/utils/functions';
import { applyUpdatesToArray } from '~/shared/utils/update';

const props = defineProps<{
	edit?: boolean,
	roleId?: number,
}>();

const schedule = await useSchedule();

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

const newShiftName = ref("");
const newShiftId = computed(() => {
	if (schedule.value.deleted) {
		throw new Error("Unexpected deleted schedule");
	}
	return Math.max(
		1,
		...schedule.value.shifts?.map(r => r.id) ?? [],
		...changes.value.map(c => c.id)
	) + 1;
});
const newShiftRole = ref(props.roleId);
watch(() => props.roleId, () => {
	newShiftRole.value = props.roleId;
});
const newShiftDescription = ref("");
function editShift(
	shift: Extract<ApiScheduleShift, { deleted?: false }>,
	edits: { name?: string, description?: string, roleId?: number }
) {
	const copy = { ...shift };
	if (edits.name !== undefined) {
		copy.name = edits.name;
	}
	if (edits.description !== undefined) {
		copy.description = edits.description || undefined;
	}
	if (edits.roleId !== undefined) {
		copy.roleId = edits.roleId;
	}
	changes.value = replaceChange(copy, changes.value);
}
function delShift(id: number) {
	const change = { id, updatedAt: "", deleted: true as const };
	changes.value = replaceChange(change, changes.value);
}
function revertShift(id: number) {
	changes.value = revertChange(id, changes.value);
}
function shiftExists(name: string) {
	if (schedule.value.deleted) {
		throw new Error("Unexpected deleted schedule");
	}
	name = toId(name);
	return (
		schedule.value.shifts?.some(s => !s.deleted && toId(s.name) === name)
		|| changes.value.some(c => !c.deleted && c.name === name)
	);
}
function newShift() {
	if (shiftExists(newShiftName.value)) {
		alert(`Shift ${newShiftName.value} already exists`);
		return;
	}
	if (schedule.value.deleted) {
		throw new Error("Unexpected deleted schedule");
	}
	if (!newShiftRole.value) {
		alert(`Invalid role`);
		return;
	}
	const change = {
		id: newShiftId.value,
		updatedAt: "",
		name: newShiftName.value,
		roleId: newShiftRole.value,
		description: newShiftDescription.value || undefined,
		slots: [],
	};
	changes.value = replaceChange(change, changes.value);
	newShiftName.value = "";
	newShiftDescription.value = "";
}
async function saveShifts() {
	try {
		await $fetch("/api/schedule", {
			method: "PATCH",
			body: {
				id: 111,
				updatedAt: "",
				shifts: changes.value
			} satisfies ApiSchedule,
		});
		changes.value = [];
	} catch (err: any) {
		console.error(err);
		alert(err?.data?.message ?? err.message);
	}
}

const shifts = computed(() => {
	if (schedule.value.deleted) {
		throw new Error("Unexpected deleted schedule");
	}
	const data = [...schedule.value.shifts ?? []].filter(shift => !shift.deleted && (!props.roleId || shift.roleId === props.roleId));
	applyUpdatesToArray(changes.value.filter(change => !change.deleted), 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>
