<style scoped></style>

<template>
	<ThreeColumn>
		<template #header>
			<div class="col">
				<h3 class="mb-1">Communication Workbench</h3>
				<div class="text-muted small">
					Communications from various sources come in at the left, are
					investigated in the center, and labeled as valuable signal or invalid
					on the right.
				</div>
			</div>
		</template>
		<template #left>
			<CommunicationColumnList
				:all="visibleCommunications"
				@deselect="handleDeselect"
				:loading="storeResource.communication.loadingAll()"
				:selectedID="selectedId"
				@select="handleSelect"
			/>
		</template>
		<template #center>
			<CommunicationColumnDetail
				:loading="storePublicReport.loading || loadingSelectedCommunication"
				:mapBounds="mapBounds || undefined"
				:mapMarkers="mapMarkers"
				:selectedCommunication="selectedCommunication"
				:selectedReport="selectedReport"
				@viewImage="openImageViewer"
			/>
		</template>
		<template #right>
			<CommunicationColumnAction
				:isLoading="storePublicReport.loading || loadingSelectedCommunication"
				@markInvalid="markInvalid"
				@markPendingResponse="markPendingResponse"
				@markPossibleIssue="markPossibleIssue"
				@markPossibleResolved="markPossibleResolved"
				@sendMessage="sendMessage"
				:selectedCommunication="selectedCommunication"
				:selectedReport="selectedReport"
			/>
		</template>
	</ThreeColumn>
	<ImageViewerModal
		@close="showImageModal = false"
		@imageNext="imageNext()"
		@imagePrevious="imagePrevious()"
		:images="currentImages"
		:currentImageIndex="currentImageIndex"
		:show="showImageModal"
	/>
	<ToastNotification
		:message="toastMessage"
		:show="toastShow"
		:title="toastTitle"
	/>
</template>

<script setup lang="ts">
import { computed, onMounted, ref, watch } from "vue";
import { computedAsync } from "@vueuse/core";
import maplibregl from "maplibre-gl";

import { apiClient } from "@/client";
import CommunicationColumnAction from "@/components/CommunicationColumnAction.vue";
import CommunicationColumnDetail from "@/components/CommunicationColumnDetail.vue";
import CommunicationColumnList from "@/components/CommunicationColumnList.vue";
import ImageViewerModal from "@/components/ImageViewerModal.vue";
import ThreeColumn from "@/components/layout/ThreeColumn.vue";
import ToastNotification from "@/components/ToastNotification.vue";
import { useQueryParam } from "@/composable/use-query-param";
import { log } from "@/log";
import { SSEManager } from "@/SSEManager";
import { useStoreResource } from "@/store/resource";
import { useSessionStore } from "@/store/session";
import type { Marker } from "@/types";
import { Bounds, type Communication, PublicReport } from "@/type/api";
import type { LngLatBounds } from "@/map/Map.vue";
import { boundsForServiceArea, boundsWithPadding } from "@/map/util";
import { useStorePublicReport } from "@/store/publicreport";

const session = useSessionStore();

// Refs
const currentImageIndex = ref<number>(0);
const paramCommunication = useQueryParam("communication");
const selectedId = ref<string | undefined>(undefined);
const showImageModal = ref(false);
const storePublicReport = useStorePublicReport();
const storeResource = useStoreResource();
const toastMessage = ref("");
const toastShow = ref(false);
const toastTitle = ref("");

const currentImage = computed(() => {
	const comm = selectedCommunication.value;
	return selectedReport.value?.images[currentImageIndex.value] ?? null;
});
const currentImages = computed(() => {
	const comm = selectedCommunication.value;
	if (comm == null) {
		return [];
	}
	return selectedReport.value?.images ?? [];
});
const loadingSelectedCommunication = computed<boolean>((): boolean => {
	return !!selectedCommunication.value;
});
const mapBounds = computed<LngLatBounds | null>((): LngLatBounds | null => {
	let bounds = new Bounds();
	const loc = selectedReport.value?.location;
	log.info("updating for loc", loc);
	if (loc && loc.latitude != 0 && loc.longitude != 0) {
		bounds.addLocation(loc);
	}
	const address_loc = selectedReport.value?.address.location;
	if (address_loc && address_loc.latitude != 0 && address_loc.longitude != 0) {
		bounds.addLocation(address_loc);
	}

	for (const [i, image] of (selectedReport.value?.images ?? []).entries()) {
		if (
			image.location != null &&
			image.location.latitude != 0 &&
			image.location.longitude != 0
		) {
			bounds.addLocation(image.location);
		}
	}
	if (bounds.isEmpty()) {
		return boundsForServiceArea();
	}
	return boundsWithPadding(bounds.min, bounds.max, 0.01);
});
const mapMarkers = computed<Marker[]>((): Marker[] => {
	const loc = selectedReport.value?.location;
	let markers: Marker[] = [];
	if (loc && loc.latitude != 0 && loc.longitude != 0) {
		markers.push({
			color: "#0000FF",
			draggable: false,
			id: "reporter",
			location: loc,
		});
	}
	const address_loc = selectedReport.value?.address.location;
	if (address_loc && address_loc.latitude != 0 && address_loc.longitude != 0) {
		markers.push({
			color: "#FF0000",
			draggable: false,
			id: "address",
			location: address_loc,
		});
	}

	for (const [i, image] of (selectedReport.value?.images ?? []).entries()) {
		if (
			image.location != null &&
			image.location.latitude != 0 &&
			image.location.longitude != 0
		) {
			markers.push({
				color: "#00FF00",
				draggable: false,
				id: `image-${i}`,
				location: image.location,
			});
		}
	}
	return markers;
});
const selectedCommunication = computed((): Communication | undefined => {
	log.info("get selectedCommunication", selectedId.value);
	if (!selectedId.value) {
		return undefined;
	}
	const all = Array.from(storeResource.communication.byURI.values());
	const result = all.find((c: Communication) => c.id == selectedId.value);
	log.info("selectedCommunication", selectedId.value, result);
	return result;
});
const selectedReport = computedAsync(
	async (): Promise<PublicReport | undefined> => {
		if (
			!(
				selectedCommunication.value &&
				selectedCommunication.value.type != "publicreport"
			)
		)
			return;
		return await storePublicReport.byURI(selectedCommunication.value.source);
	},
);
const visibleCommunications = computed((): Communication[] | undefined => {
	log.info("get visibleCommunications", storeResource.communication.byURI);
	if (!storeResource.communication.byURI) {
		return undefined;
	}
	const all: Communication[] = Array.from(
		storeResource.communication.byURI.values(),
	);
	const result = all.filter((c: Communication) => {
		return c.status == "new" || c.status == "opened";
	});
	log.info("visibleCommunications:", result);
	return result;
});
const handleDeselect = (id: string) => {
	selectedId.value = undefined;
};
const handleSelect = (id: string) => {
	selectedId.value = id;
	paramCommunication.setValue(id);
};
function imageNext() {
	currentImageIndex.value = Math.min(
		currentImages.value.length - 1,
		currentImageIndex.value + 1,
	);
}
function imagePrevious() {
	currentImageIndex.value = Math.max(0, currentImageIndex.value - 1);
}

function openImageViewer(index: number) {
	currentImageIndex.value = index;
	showImageModal.value = true;
}

async function markInvalid() {
	markReport("Invalid", "invalid");
}
async function markPendingResponse() {
	markReport("Pending Response", "pending-response");
}
async function markPossibleIssue() {
	markReport("Possible Issue", "possible-issue");
}
async function markPossibleResolved() {
	markReport("Possibly Resolved", "possible-resolved");
}
async function markReport(title: string, status: string) {
	if (selectedCommunication.value == null) {
		return;
	}
	const url = `${selectedCommunication.value.uri}/mark/${status}`;
	const result = apiClient.JSONPost(url, {});

	showNotification(
		`Report Marked ${title}`,
		`Report #${selectedCommunication.value.id} has been updated`,
	);
	await storeResource.communication.fetchAll();
}

async function sendMessage(message: string) {
	if (!message.trim()) return;
	if (selectedCommunication.value == null) return;
	if (selectedReport.value == null) return;
	if (session.urls == null) return;
	log.info("Sending message reporter:", message);

	const payload = {
		message: message,
		reportID: selectedCommunication.value.id,
	};
	const response = await fetch(session.urls?.api.publicreport_message, {
		method: "POST",
		headers: {
			"Content-Type": "application/json",
		},
		body: JSON.stringify(payload),
	});

	if (!response.ok) {
		throw new Error(`HTTP error! status: ${response.status}`);
	}

	showNotification(
		"Message Sent",
		`Message successfully sent to ${selectedReport.value.reporter.name}`,
	);
}
function showNotification(title: string, message: string) {
	toastTitle.value = title;
	toastMessage.value = message;
	toastShow.value = true;

	setTimeout(() => {
		toastShow.value = false;
	}, 3000);
}

// Lifecycle hooks
onMounted(async () => {
	await storeResource.communication.fetchAll();
});
watch(
	paramCommunication.value,
	(communication_id) => {
		if (communication_id) {
			handleSelect(communication_id);
		}
	},
	{ immediate: true },
);
</script>
