<script lang="ts">
    import { goto } from "$app/navigation";
    import Text from "$lib/components/inputs/Text.svelte";
    import Textarea from "$lib/components/inputs/Textarea.svelte";
    import Navbar from "$lib/components/Navbar.svelte";
    import crossUrl from "$lib/assets/cross.svg";
    import Button from "$lib/components/Button.svelte";
    import Images from "$lib/components/inputs/Images.svelte";
    import OpeningTimesDay from "$lib/components/inputs/OpeningTimesDay.svelte";
    import {
        availableStudySpaceTags,
        wifiTags,
        powerOutletTags,
        volumeTags,
        gmapsLoader,
        daysOfWeek,
        timeToMins,
        collectTimings
    } from "$lib";
    import { onMount } from "svelte";
    import type { Json } from "$lib/database.js";

    const { data } = $props();
    const { supabase } = $derived(data);

    const { space, images } = $derived(data);

    interface OpeningTime {
        day_of_week: number;
        opens_at: string;
        closes_at: string;
        open_today_status: boolean | null;
    }
    const studySpaceData = $state({
        opening_times: [] as OpeningTime[],
        ...space
    });

    $effect(() => {
        if (!space) return;
        Object.assign(studySpaceData, space);
        studySpaceData.opening_times = space.opening_times ?? [];
    });

    let scrollPosition = $state(0);
    const existingImages = $derived(
        Promise.all(
            images?.map(async ({ image_path }) => {
                const { data, error } = await supabase.storage
                    .from("files_bucket")
                    .download(image_path);
                if (error) {
                    console.error(`Error downloading image ${image_path}:`, error);
                    return null;
                }
                return { data, name: image_path.split("/").pop() || "image", type: data.type };
            }) || []
        )
    );
    let spaceImgs = $state<FileList>();
    let uploading = $state(false);
    function checkTimings() {
        let cannotExist = [] as number[];

        let hasAllDays = Object.values(collectTimings(studySpaceData.opening_times)).every(
            (item) => !(Array.isArray(item) && item.length === 0)
        );

        if (
            (allDays.closes_at === "" || allDays.opens_at === "") &&
            allDays.open_today_status === null &&
            studySpaceData.opening_times.length > 0 &&
            !hasAllDays
        ) {
            alert(`No opening time provided for all other days.`);
            return false;
        }

        const opensAtMinsAll = timeToMins(allDays.opens_at);
        const closesAtMinsAll = timeToMins(allDays.closes_at);
        if (opensAtMinsAll >= closesAtMinsAll) {
            alert(`Opening time for all days is after closing time.`);
            return false;
        }

        for (const entry of studySpaceData.opening_times) {
            if (cannotExist.includes(entry.day_of_week)) {
                alert(
                    "You marked a day as either closed or open all day, and then provided another timing."
                );
                return false;
            }
            if (entry.open_today_status != null) {
                cannotExist.push(entry.day_of_week);
            }
            const opensAtMins = timeToMins(entry.opens_at);
            const closesAtMins = timeToMins(entry.closes_at);
            if (opensAtMins >= closesAtMins) {
                alert(`Opening time for ${daysOfWeek[entry.day_of_week]} is after closing time.`);
                return false;
            }
        }
        return true;
    }

    function genTimings(studySpaceId: string) {
        const fullDayOfWeek = [0, 1, 2, 3, 4, 5, 6];
        // all day only
        if (
            studySpaceData.opening_times.length === 0 &&
            ((allDays.closes_at != "" && allDays.opens_at != "") ||
                allDays.open_today_status != null)
        ) {
            return fullDayOfWeek.map((day) => ({
                study_space_id: studySpaceId,
                day_of_week: day,
                opens_at: allDays.open_today_status === null ? allDays.opens_at : "00:00",
                closes_at: allDays.open_today_status === null ? allDays.closes_at : "00:01",
                open_today_status: allDays.open_today_status
            }));
        }
        // some days specified
        const nonDefinedDays = fullDayOfWeek.filter(
            (day) => !new Set(studySpaceData.opening_times.map((h) => h.day_of_week)).has(day)
        );
        return studySpaceData.opening_times
            .map((h) => ({
                study_space_id: studySpaceId,
                day_of_week: h.day_of_week,
                opens_at: h.open_today_status === null ? h.opens_at : "00:00",
                closes_at: h.open_today_status === null ? h.closes_at : "00:01",
                open_today_status: h.open_today_status
            }))
            .concat(
                nonDefinedDays.map((day) => ({
                    study_space_id: studySpaceId,
                    day_of_week: day,
                    opens_at: allDays.open_today_status === null ? allDays.opens_at : "00:00",
                    closes_at: allDays.open_today_status === null ? allDays.closes_at : "00:01",
                    open_today_status: allDays.open_today_status
                }))
            );
    }

    async function uploadStudySpace() {
        if (!checkTimings()) return;
        if (!spaceImgs || spaceImgs.length < 1) return alert("Please select an image file.");
        if (!studySpaceData.building_location) return alert("Please select a building location.");

        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        const { opening_times: _, ...spacePayload } = studySpaceData;

        const { data: studySpaceInsert, error: studySpaceError } = await supabase
            .from("study_spaces")
            .upsert(
                {
                    ...spacePayload,
                    building_location: studySpaceData.building_location as Json
                },
                {
                    onConflict: "id"
                }
            )
            .select()
            .single();
        if (studySpaceError)
            return alert(`Error uploading study space: ${studySpaceError.message}`);

        const imgUploads = await Promise.all(
            Array(spaceImgs.length)
                .keys()
                .map(async (i) => {
                    const imageFile = spaceImgs![i];
                    const resp = await supabase.storage
                        .from("files_bucket")
                        .upload(
                            `public/${studySpaceInsert.id}-${crypto.randomUUID()}-${imageFile.name}`,
                            imageFile,
                            {
                                contentType: imageFile.type
                            }
                        );
                    return resp;
                })
        );

        const imageError = imgUploads.find(({ error }) => error)?.error;
        if (imageError) return alert(`Error uploading image: ${imageError.message}`);
        if (space.id) {
            const { error: imageOverwriteError } = await supabase
                .from("study_space_images")
                .delete()
                .eq("study_space_id", space.id);
            if (imageOverwriteError)
                return alert(`Error overwriting existing images: ${imageOverwriteError.message}`);
        }

        const { error: imageInsertError } = await supabase
            .from("study_space_images")
            .insert(
                imgUploads.map(({ data }) => ({
                    study_space_id: studySpaceInsert.id,
                    image_path: data!.path
                }))
            )
            .select();
        if (imageInsertError) return alert(`Error creating image: ${imageInsertError.message}`);

        const { error: deleteErr } = await supabase
            .from("study_space_hours")
            .delete()
            .eq("study_space_id", studySpaceInsert.id);
        if (deleteErr) return alert(`Error clearing old hours: ${deleteErr.message}`);

        // Nothing is provided
        if (
            (allDays.closes_at != "" && allDays.opens_at != "") ||
            studySpaceData.opening_times.length === 7 ||
            allDays.open_today_status != null
        ) {
            const { error: hoursErr } = await supabase
                .from("study_space_hours")
                .insert(genTimings(studySpaceInsert.id));
            if (hoursErr) return alert(`Error saving opening times: ${hoursErr.message}`);
        }
        await supabase.channel("study_space_updates").send({
            type: "broadcast",
            event: "study_space_updated",
            payload: {
                study_space_id: studySpaceInsert.id
            }
        });
        alert("Thank you for your contribution!");
        // Redirect to the new study space page
        await goto(`/space/${studySpaceInsert.id}`, {
            invalidate: ["db:study_spaces"]
        });
    }

    // Tag
    let tagFilter = $state("");
    let tagFilterElem = $state<HTMLInputElement>();
    let filteredTags = $derived(
        availableStudySpaceTags
            .filter((tag) => tag.toLowerCase().includes(tagFilter.toLowerCase()))
            .filter((tag) => !studySpaceData.tags.includes(tag))
    );
    let dropdownVisible = $state(false);

    function deleteTag(tagName: string) {
        return () => {
            studySpaceData.tags = studySpaceData.tags.filter((tag) => tag !== tagName);
        };
    }

    function addTag(tagName: string) {
        return () => {
            if (!studySpaceData.tags.includes(tagName)) {
                studySpaceData.tags.push(tagName);
            }
            tagFilter = "";
        };
    }

    let addressInput = $state<HTMLInputElement>();
    onMount(async () => {
        const loader = await gmapsLoader();
        const places = await loader.importLibrary("places");
        if (!addressInput) return console.error("Address input element not found");
        addressInput.value = studySpaceData.building_location?.formatted_address || "";
        if (studySpaceData.building_location?.name) {
            addressInput.value = `${studySpaceData.building_location.name}, ${addressInput.value}`;
        }
        const placeAutocomplete = new places.Autocomplete(addressInput, {
            componentRestrictions: { country: "gb" }
        });
        placeAutocomplete.addListener("place_changed", () => {
            studySpaceData.building_location = placeAutocomplete.getPlace();
        });
    });
    onMount(async () => {
        const images = await existingImages;
        const dt = new DataTransfer();
        images.forEach((response) => {
            if (response) {
                const file = new File([response.data], response.name, { type: response.type });
                dt.items.add(file);
            }
        });
        spaceImgs = dt.files;
    });
    // Opening times
    let allDays = $state({
        opens_at: "",
        closes_at: "",
        open_today_status: null
    });
</script>

<Navbar>
    <a href="/">
        <img src={crossUrl} alt="close" />
    </a>
</Navbar>

<form
    onsubmit={async (event) => {
        event.preventDefault();
        uploading = true;
        await uploadStudySpace();
        uploading = false;
    }}
>
    <Images
        name="study-space-image"
        minHeight="16rem"
        bind:files={spaceImgs}
        bind:scrollPosition
        required
    />
    {#if spaceImgs?.length || 0 > 0}
        <label class="additionalImages" for="additionalImages">
            Add more images
            <input
                type="file"
                name="additionalImages"
                id="additionalImages"
                multiple
                accept=".png, .jpg, .jpeg, .svg"
                onchange={function () {
                    const dt = new DataTransfer();
                    if (spaceImgs) {
                        for (let i = 0; i < spaceImgs.length; i++) {
                            dt.items.add(spaceImgs[i]);
                        }
                    }
                    if (this.files) {
                        for (let i = 0; i < this.files.length; i++) {
                            dt.items.add(this.files[i]);
                        }
                    }
                    spaceImgs = dt.files;
                    scrollPosition = dt.files.length - 1;
                }}
            />
        </label>
    {/if}

    <label for="location">Enter the name:</label>
    <Text
        name="location"
        bind:value={studySpaceData.location}
        placeholder="Huxeley Labs 225"
        maxlength={35}
        required
    />

    {#if (studySpaceData.location ?? "").length > 25}
        <p class="lengthPopup">
            Try to keep the name succinct—for example, building + room name. Put any further
            information like floor number in the description.
        </p>
    {/if}

    <div class="compulsoryTags">
        <div class="compulsoryContainer">
            <label for="volume">Sound level:</label>
            <select bind:value={studySpaceData.volume} name="volume" class="compulsoryTagSelect">
                <option value="" disabled selected>How noisy is it?</option>
                {#each volumeTags as volumeTag (volumeTag)}
                    <option value={volumeTag}>{volumeTag}</option>
                {/each}
            </select>
        </div>
        <div class="compulsoryContainer">
            <label for="powerOutlets">Power outlets:</label>
            <select
                bind:value={studySpaceData.power}
                name="poweOutlets"
                class="compulsoryTagSelect"
            >
                <option value="" disabled selected>Power outlets?</option>
                {#each powerOutletTags as powerOutletTag (powerOutletTag)}
                    <option value={powerOutletTag}>{powerOutletTag}</option>
                {/each}
            </select>
        </div>
        <div class="compulsoryContainer">
            <label for="wifi">Wifi:</label>
            <select bind:value={studySpaceData.wifi} name="wifi" class="compulsoryTagSelect">
                <option value="" disabled selected>How's the wifi?</option>
                {#each wifiTags as wifiTag (wifiTag)}
                    <option value={wifiTag}>{wifiTag}</option>
                {/each}
            </select>
        </div>
    </div>
    <label for="openingTimes">Opening times (Optional):</label>
    <div class="allDaysTiming">
        {#each studySpaceData.opening_times as opening_time, index (opening_time)}
            <OpeningTimesDay
                {index}
                bind:openingValue={opening_time.opens_at}
                bind:closingValue={opening_time.closes_at}
                bind:openTodayStatus={opening_time.open_today_status}
                bind:day={opening_time.day_of_week}
                onHide={() => {
                    studySpaceData.opening_times.splice(index, 1);
                }}
            />
            <hr />
        {/each}
        <OpeningTimesDay
            index={-1}
            bind:openingValue={allDays.opens_at}
            bind:closingValue={allDays.closes_at}
            bind:openTodayStatus={allDays.open_today_status}
            day={studySpaceData.opening_times.length === 0 ? 7 : 8}
        />
    </div>
    <Button
        style="normal"
        type="button"
        onclick={() => {
            studySpaceData.opening_times.push({
                day_of_week: 0,
                opens_at: "09:00",
                closes_at: "17:00",
                open_today_status: null
            });
        }}>Add new day</Button
    >

    <label for="tags">Additional tags (Optional):</label>
    <div class="tagDisplay">
        {#each studySpaceData.tags as tagName (tagName)}
            <button class="tag" onclick={deleteTag(tagName)} type="button">
                {tagName}
                <img src={crossUrl} alt="delete" /></button
            >
        {/each}
        <input
            type="text"
            name="tagInput"
            class="tagInput"
            bind:value={tagFilter}
            bind:this={tagFilterElem}
            onfocus={() => {
                dropdownVisible = true;
            }}
            onblur={() => {
                dropdownVisible = false;
            }}
            onkeypress={(event) => {
                if (event.key === "Enter") {
                    event.preventDefault();
                    const tag = filteredTags[0];
                    if (tag) addTag(tag)();
                }
            }}
            placeholder="Add tags..."
        />
        {#if dropdownVisible}
            <div class="tagDropdown">
                {#each filteredTags as avaliableTag (avaliableTag)}
                    <button
                        class="avaliableTag"
                        onclick={addTag(avaliableTag)}
                        onmousedown={(e) => {
                            // Keep input focused
                            e.preventDefault();
                            e.stopPropagation();
                        }}
                        type="button"
                    >
                        {avaliableTag}
                    </button>
                {/each}
            </div>
        {/if}
    </div>

    <label for="description">Brief description (Optional):</label>
    <Textarea
        name="description"
        bind:value={studySpaceData.description}
        placeholder="A quiet room with a lovely view of the park."
        rows={2}
    />

    <label for="directions">Give directions:</label>
    <Textarea
        name="directions"
        bind:value={studySpaceData.directions}
        placeholder="Turn left once you enter Huxley and walk straight."
        rows={2}
    />

    <label for="building-location">Add the building location:</label>
    <Text
        name="building-location"
        bind:inputElem={addressInput}
        placeholder="Huxley Building, Imperial South Kensington Campus"
        required
    />

    <div class="submit">
        <Button
            type="submit"
            disabled={(spaceImgs?.length || 0) === 0 ||
                !studySpaceData.location ||
                !studySpaceData.wifi ||
                !studySpaceData.volume ||
                !studySpaceData.power ||
                !studySpaceData.building_location ||
                uploading}
        >
            Share this study space!
        </Button>
    </div>
</form>

<style>
    form {
        display: flex;
        flex-direction: column;
        padding: 1.5rem;
        gap: 0.5rem;
        max-width: 32rem;
        margin: 0 auto;
    }

    label {
        color: #ffffff;
        margin-top: 0.5rem;
    }
    .submit {
        position: sticky;
        display: flex;
        flex-direction: column;
        margin-top: 0.5rem;
        bottom: 0;
        margin-left: -0.5rem;
        width: calc(100% + 1rem);
    }
    .tagDisplay {
        display: flex;
        gap: 0.4rem;
        flex-wrap: wrap;
        align-items: left;
        justify-content: left;
        position: relative;
        width: 100%;
        height: auto;
        padding: 0.5rem;
        border-radius: 0.5rem;
        border: 2px solid #eaffeb;
        background: none;
        color: #eaffeb;
        font-size: 1rem;
    }
    .tagInput {
        width: 100%;
        height: 100%;
        background: none;
        color: #eaffeb;
        font-size: 1rem;
        border: none;
        outline: none;
    }
    ::placeholder {
        color: #859a90;
        opacity: 1;
    }
    .tag {
        display: flex;
        align-items: center;
        border-radius: 0.25rem;
        background-color: #2e4653;
        color: #eaffeb;
        font-size: 0.9rem;
        cursor: pointer;
        border-width: 0rem;
    }
    .tag img {
        width: 1rem;
        height: 1rem;
        margin-left: 0.2rem;
    }

    .tagDropdown {
        width: 100%;
        display: flex;
        gap: 0.4rem;
        flex-wrap: wrap;
        position: absolute;
        background-color: #2e4653;
        box-shadow: 1px 1px 0.5rem rgba(0, 0, 0, 0.5);
        border-radius: 0.5rem;
        overflow-y: auto;
        max-height: 10rem;
        top: 100%;
        left: 50%;
        transform: translateX(-50%);
    }

    .avaliableTag {
        width: 100%;
        text-align: left;
        background: none;
        border: none;
        color: #eaffeb;
        font-size: 0.9rem;
        margin: 0%;
        padding: 0 0.8rem 0.4rem;
    }
    .avaliableTag:first-child {
        padding-top: 0.6rem;
        background-color: hsl(201, 26%, 60%);
    }
    .avaliableTag:last-child {
        padding-bottom: 0.6rem;
    }

    .compulsoryTags {
        display: grid;
        gap: 0.4rem;
        border-radius: 0.5rem;
        background-color: none;
        width: 100%;
        font-size: 1rem;
        grid-template-columns: repeat(3, 1fr);
    }

    .compulsoryContainer {
        display: flex;
        flex-direction: column;
        align-items: left;
        justify-content: top;
        border-radius: 0.5rem;
        background-color: none;
    }

    .compulsoryTagSelect {
        width: 100%;
        height: 100%;
        padding: 0.5rem;
        border-radius: 0.5rem;
        border: 2px solid #eaffeb;
        background: none;
        color: #eaffeb;
        font-size: 0.9rem;
        text-align: left;
    }

    .compulsoryTagSelect option {
        background-color: #2e4653;
        color: #eaffeb;
    }
    .additionalImages {
        padding: 0.5rem 1rem;
        border-radius: 0.5rem;
        background: linear-gradient(-83deg, #3fb095, #189f5e);
        box-shadow: 0rem 0rem 0.5rem #182125;
        color: #eaffeb;
        border: none;
        cursor: pointer;
        text-align: center;
    }
    .additionalImages:focus {
        outline: 2px solid #007bff;
    }
    .additionalImages input {
        display: none;
    }

    /* Opening times layout and inputs styling */
    .allDaysTiming {
        border-radius: 0.5rem;
        background: none;
        display: flex;
        flex-direction: column;
        flex-wrap: wrap;
        gap: 0.5rem;
    }

    hr {
        margin: 0%;
        padding: 0;
        width: 100%;
        height: 2px;
        border: none;
        background-color: #eaffeb;
        border-radius: 5rem;
    }

    .lengthPopup {
        background-color: #2e4653;
        border-radius: 0.5rem;
        padding: 0.5rem;
    }
</style>
