<svelte:options immutable={true} />

<script lang="ts">
    import { onMount, onDestroy } from 'svelte';
    import { openModal, closeModal } from 'svelte-modals';
    import { slide } from 'svelte/transition';
    import { cubicOut } from 'svelte/easing';
    import { user } from '$lib/stores/user';
    import { page } from '$app/stores';
    import { notifications } from '$lib/components/toasts/notifications';
    import DragDropList, { VerticalDropZone, reorder, type DropEvent } from 'svelte-dnd-list';
    import SettingsCard from '$lib/components/SettingsCard.svelte';
    import { PasswordInput } from '$lib/components/input';
    import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
    import ScanNetworks from './Scan.svelte';
    import Spinner from '$lib/components/Spinner.svelte';
    import InfoDialog from '$lib/components/InfoDialog.svelte';
    import type { KnownNetworkItem, WifiSettings, WifiStatus } from '$lib/types/models';
    import { socket, useFeatureFlags } from '$lib/stores';
    import { api } from '$lib/api';
    import {
        Cancel,
        Delete,
        Check,
        Router,
        AP,
        SSID,
        Home,
        WiFi,
        Down,
        MAC,
        Channel,
        Gateway,
        Subnet,
        DNS,
        Add,
        Scan,
        Edit
    } from '$lib/components/icons';

    const features = useFeatureFlags();

    let networkEditable: KnownNetworkItem = {
        ssid: '',
        password: '',
        static_ip_config: false,
        local_ip: undefined,
        subnet_mask: undefined,
        gateway_ip: undefined,
        dns_ip_1: undefined,
        dns_ip_2: undefined
    };

    let static_ip_config = false;

    let newNetwork: boolean = true;
    let showNetworkEditor: boolean = false;

    let wifiStatus: WifiStatus;
    let wifiSettings: WifiSettings;

    let dndNetworkList: KnownNetworkItem[] = [];

    let showWifiDetails = false;

    let formField: any;

    let formErrors = {
        ssid: false,
        local_ip: false,
        gateway_ip: false,
        subnet_mask: false,
        dns_1: false,
        dns_2: false
    };

    let formErrorhostname = false;

    async function getWifiStatus() {
        const result = await api.get<WifiStatus>('/api/wifi/status');
        if (result.isErr()) {
            console.error(`Error occurred while fetching: `, result.inner);
            return;
        }
        wifiStatus = result.inner;
        return wifiStatus;
    }

    async function getWifiSettings() {
        const result = await api.get<WifiSettings>('/api/wifi/settings');
        if (result.isErr()) {
            console.error(`Error occurred while fetching: `, result.inner);
            return;
        }
        wifiSettings = result.inner;
        dndNetworkList = wifiSettings.wifi_networks;
        return wifiSettings;
    }

    onDestroy(() => socket.off('WiFiSettings'));

    onMount(() => {
        socket.on<WifiSettings>('WiFiSettings', data => {
            wifiSettings = data;
            dndNetworkList = wifiSettings.wifi_networks;
        });
    });

    async function postWiFiSettings(data: WifiSettings) {
        const result = await api.post<WifiSettings>('/api/wifi/settings', data);
        if (result.isErr()) {
            console.error(`Error occurred while fetching: `, result.inner);
            notifications.error('User not authorized.', 3000);
            return;
        }
        wifiSettings = result.inner;
        notifications.success('Wi-Fi settings updated.', 3000);
    }

    function validateHostName() {
        if (wifiSettings.hostname.length < 3 || wifiSettings.hostname.length > 32) {
            formErrorhostname = true;
        } else {
            formErrorhostname = false;
            // Update global wifiSettings object
            wifiSettings.wifi_networks = dndNetworkList;
            // Post to REST API
            postWiFiSettings(wifiSettings);
            console.log(wifiSettings);
        }
    }

    function validateWiFiForm() {
        let valid = true;

        // Validate SSID
        if (networkEditable.ssid.length < 3 || networkEditable.ssid.length > 32) {
            valid = false;
            formErrors.ssid = true;
        } else {
            formErrors.ssid = false;
        }

        networkEditable.static_ip_config = static_ip_config;

        if (networkEditable.static_ip_config) {
            // RegEx for IPv4
            const regexExp =
                /\b(?:(?:2(?:[0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9])\.){3}(?:(?:2([0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9]))\b/;

            // Validate gateway IP
            if (!regexExp.test(networkEditable.gateway_ip!)) {
                valid = false;
                formErrors.gateway_ip = true;
            } else {
                formErrors.gateway_ip = false;
            }

            // Validate Subnet Mask
            if (!regexExp.test(networkEditable.subnet_mask!)) {
                valid = false;
                formErrors.subnet_mask = true;
            } else {
                formErrors.subnet_mask = false;
            }

            // Validate local IP
            if (!regexExp.test(networkEditable.local_ip!)) {
                valid = false;
                formErrors.local_ip = true;
            } else {
                formErrors.local_ip = false;
            }

            // Validate DNS 1
            if (!regexExp.test(networkEditable.dns_ip_1!)) {
                valid = false;
                formErrors.dns_1 = true;
            } else {
                formErrors.dns_1 = false;
            }

            // Validate DNS 2
            if (!regexExp.test(networkEditable.dns_ip_2!)) {
                valid = false;
                formErrors.dns_2 = true;
            } else {
                formErrors.dns_2 = false;
            }
        } else {
            formErrors.local_ip = false;
            formErrors.subnet_mask = false;
            formErrors.gateway_ip = false;
            formErrors.dns_1 = false;
            formErrors.dns_2 = false;
        }
        // Submit JSON to REST API
        if (valid) {
            if (newNetwork) {
                dndNetworkList.push(networkEditable);
            } else {
                dndNetworkList.splice(dndNetworkList.indexOf(networkEditable), 1, networkEditable);
            }
            addNetwork();
            dndNetworkList = [...dndNetworkList]; //Trigger reactivity
            showNetworkEditor = false;
        }
    }

    function scanForNetworks() {
        openModal(ScanNetworks, {
            storeNetwork: (network: string) => {
                addNetwork();
                networkEditable.ssid = network;
                showNetworkEditor = true;
                closeModal();
            }
        });
    }

    function addNetwork() {
        newNetwork = true;
        networkEditable = {
            ssid: '',
            password: '',
            static_ip_config: false,
            local_ip: undefined,
            subnet_mask: undefined,
            gateway_ip: undefined,
            dns_ip_1: undefined,
            dns_ip_2: undefined
        };
    }

    function handleEdit(index: number) {
        newNetwork = false;
        showNetworkEditor = true;
        networkEditable = dndNetworkList[index];
    }

    function confirmDelete(index: number) {
        openModal(ConfirmDialog, {
            title: 'Delete Network',
            message: 'Are you sure you want to delete this network?',
            labels: {
                cancel: { label: 'Cancel', icon: Cancel },
                confirm: { label: 'Delete', icon: Delete }
            },
            onConfirm: () => {
                // Check if network is currently been edited and delete as well
                if (dndNetworkList[index].ssid === networkEditable.ssid) {
                    addNetwork();
                }
                // Remove network from array
                dndNetworkList.splice(index, 1);
                dndNetworkList = [...dndNetworkList]; //Trigger reactivity
                showNetworkEditor = false;
                closeModal();
            }
        });
    }

    function checkNetworkList() {
        if (dndNetworkList.length >= 5) {
            openModal(InfoDialog, {
                title: 'Reached Maximum Networks',
                message:
                    'You have reached the maximum number of networks. Please delete one to add another.',
                dismiss: { label: 'OK', icon: Check },
                onDismiss: () => {
                    closeModal();
                }
            });
            return false;
        } else {
            return true;
        }
    }

    function onDrop({ detail: { from, to } }: CustomEvent<DropEvent>) {
        if (!to || from === to) {
            return;
        }

        dndNetworkList = reorder(dndNetworkList, from.index, to.index);
        console.log(dndNetworkList);
    }
</script>

<SettingsCard collapsible={false}>
    <Router slot="icon" class="lex-shrink-0 mr-2 h-6 w-6 self-end" />
    <span slot="title">WiFi Connection</span>
    <div class="w-full overflow-x-auto">
        {#await getWifiStatus()}
            <Spinner />
        {:then nothing}
            <div
                class="flex w-full flex-col space-y-1"
                transition:slide|local={{ duration: 300, easing: cubicOut }}
            >
                <div class="rounded-box bg-base-100 flex items-center space-x-3 px-4 py-2">
                    <div
                        class="mask mask-hexagon h-auto w-10 {wifiStatus.status === 3 ?
                            'bg-success'
                        :   'bg-error'}"
                    >
                        <AP
                            class="h-auto w-full scale-75 {wifiStatus.status === 3 ?
                                'text-success-content'
                            :   'text-error-content'}"
                        />
                    </div>
                    <div>
                        <div class="font-bold">Status</div>
                        <div class="text-sm opacity-75">
                            {wifiStatus.status === 3 ? 'Connected' : 'Inactive'}
                        </div>
                    </div>
                </div>
                {#if wifiStatus.status === 3}
                    <div class="rounded-box bg-base-100 flex items-center space-x-3 px-4 py-2">
                        <div class="mask mask-hexagon bg-primary h-auto w-10">
                            <SSID class="text-primary-content h-auto w-full scale-75" />
                        </div>
                        <div>
                            <div class="font-bold">SSID</div>
                            <div class="text-sm opacity-75">
                                {wifiStatus.ssid}
                            </div>
                        </div>
                    </div>

                    <div class="rounded-box bg-base-100 flex items-center space-x-3 px-4 py-2">
                        <div class="mask mask-hexagon bg-primary h-auto w-10">
                            <Home class="text-primary-content h-auto w-full scale-75" />
                        </div>
                        <div>
                            <div class="font-bold">IP Address</div>
                            <div class="text-sm opacity-75">
                                {wifiStatus.local_ip}
                            </div>
                        </div>
                    </div>

                    <div class="rounded-box bg-base-100 flex items-center space-x-3 px-4 py-2">
                        <div class="mask mask-hexagon bg-primary h-auto w-10">
                            <WiFi class="text-primary-content h-auto w-full scale-75" />
                        </div>
                        <div>
                            <div class="font-bold">RSSI</div>
                            <div class="text-sm opacity-75">
                                {wifiStatus.rssi} dBm
                            </div>
                        </div>
                        <div class="grow" />
                        <button
                            class="btn btn-circle btn-ghost btn-sm modal-button"
                            on:click={() => {
                                showWifiDetails = !showWifiDetails;
                            }}
                        >
                            <Down
                                class="text-base-content h-auto w-6 transition-transform duration-300 ease-in-out {(
                                    showWifiDetails
                                ) ?
                                    'rotate-180'
                                :   ''}"
                            />
                        </button>
                    </div>
                {/if}
            </div>

            <!-- Folds open -->
            {#if showWifiDetails}
                <div
                    class="flex w-full flex-col space-y-1 pt-1"
                    transition:slide|local={{ duration: 300, easing: cubicOut }}
                >
                    <div class="rounded-box bg-base-100 flex items-center space-x-3 px-4 py-2">
                        <div class="mask mask-hexagon bg-primary h-auto w-10">
                            <MAC class="text-primary-content h-auto w-full scale-75" />
                        </div>
                        <div>
                            <div class="font-bold">MAC Address</div>
                            <div class="text-sm opacity-75">
                                {wifiStatus.mac_address}
                            </div>
                        </div>
                    </div>

                    <div class="rounded-box bg-base-100 flex items-center space-x-3 px-4 py-2">
                        <div class="mask mask-hexagon bg-primary h-auto w-10">
                            <Channel class="text-primary-content h-auto w-full scale-75" />
                        </div>
                        <div>
                            <div class="font-bold">Channel</div>
                            <div class="text-sm opacity-75">
                                {wifiStatus.channel}
                            </div>
                        </div>
                    </div>

                    <div class="rounded-box bg-base-100 flex items-center space-x-3 px-4 py-2">
                        <div class="mask mask-hexagon bg-primary h-auto w-10">
                            <Gateway class="text-primary-content h-auto w-full scale-75" />
                        </div>
                        <div>
                            <div class="font-bold">Gateway IP</div>
                            <div class="text-sm opacity-75">
                                {wifiStatus.gateway_ip}
                            </div>
                        </div>
                    </div>

                    <div class="rounded-box bg-base-100 flex items-center space-x-3 px-4 py-2">
                        <div class="mask mask-hexagon bg-primary h-auto w-10">
                            <Subnet class="text-primary-content h-auto w-full scale-75" />
                        </div>
                        <div>
                            <div class="font-bold">Subnet Mask</div>
                            <div class="text-sm opacity-75">
                                {wifiStatus.subnet_mask}
                            </div>
                        </div>
                    </div>

                    <div class="rounded-box bg-base-100 flex items-center space-x-3 px-4 py-2">
                        <div class="mask mask-hexagon bg-primary h-auto w-10">
                            <DNS class="text-primary-content h-auto w-full scale-75" />
                        </div>
                        <div>
                            <div class="font-bold">DNS</div>
                            <div class="text-sm opacity-75">
                                {wifiStatus.dns_ip_1}
                            </div>
                        </div>
                    </div>
                </div>
            {/if}
        {/await}
    </div>

    {#if !$features.security || $user.admin}
        <div class="bg-base-200 relative grid w-full max-w-2xl self-center overflow-hidden">
            <div
                class="min-h-16 flex w-full items-center justify-between space-x-3 p-0 text-xl font-medium"
            >
                Saved Networks
            </div>
            {#await getWifiSettings()}
                <Spinner />
            {:then nothing}
                <div class="relative w-full overflow-visible">
                    <button
                        class="btn btn-primary text-primary-content btn-md absolute -top-14 right-16"
                        on:click={() => {
                            if (checkNetworkList()) {
                                addNetwork();
                                showNetworkEditor = true;
                            }
                        }}
                    >
                        <Add class="h-6 w-6" /></button
                    >
                    <button
                        class="btn btn-primary text-primary-content btn-md absolute -top-14 right-0"
                        on:click={() => {
                            if (checkNetworkList()) {
                                scanForNetworks();
                                showNetworkEditor = true;
                            }
                        }}
                    >
                        <Scan class="h-6 w-6" /></button
                    >

                    <div
                        class="overflow-x-auto space-y-1"
                        transition:slide|local={{ duration: 300, easing: cubicOut }}
                    >
                        <DragDropList
                            id="networks"
                            type={VerticalDropZone}
                            itemSize={60}
                            itemCount={dndNetworkList.length}
                            on:drop={onDrop}
                            let:index
                        >
                            <!-- svelte-ignore a11y-click-events-have-key-events -->
                            <div
                                class="rounded-box bg-base-100 flex items-center space-x-3 px-4 py-2"
                            >
                                <div class="mask mask-hexagon bg-primary h-auto w-10 shrink-0">
                                    <Router class="text-primary-content h-auto w-full scale-75" />
                                </div>
                                <div>
                                    <div class="font-bold">{dndNetworkList[index].ssid}</div>
                                </div>
                                {#if !$features.security || $user.admin}
                                    <div class="flex-grow" />
                                    <div class="space-x-0 px-0 mx-0">
                                        <button
                                            class="btn btn-ghost btn-sm"
                                            on:click={() => {
                                                handleEdit(index);
                                            }}
                                        >
                                            <Edit class="h-6 w-6" /></button
                                        >
                                        <button
                                            class="btn btn-ghost btn-sm"
                                            on:click={() => {
                                                confirmDelete(index);
                                            }}
                                        >
                                            <Delete class="text-error h-6 w-6" />
                                        </button>
                                    </div>
                                {/if}
                            </div>
                        </DragDropList>
                    </div>
                </div>

                <div class="divider mb-0" />
                <div
                    class="flex flex-col gap-2 p-0"
                    transition:slide|local={{ duration: 300, easing: cubicOut }}
                >
                    <form
                        class=""
                        on:submit|preventDefault={validateWiFiForm}
                        novalidate
                        bind:this={formField}
                    >
                        <div
                            class="grid w-full grid-cols-1 content-center gap-x-4 px-4 sm:grid-cols-2"
                        >
                            <div>
                                <label class="label" for="channel">
                                    <span class="label-text text-md">Host Name</span>
                                </label>
                                <input
                                    type="text"
                                    min="1"
                                    max="32"
                                    class="input input-bordered invalid:border-error w-full invalid:border-2 {(
                                        formErrorhostname
                                    ) ?
                                        'border-error border-2'
                                    :   ''}"
                                    bind:value={wifiSettings.hostname}
                                    id="channel"
                                    required
                                />
                                <label class="label" for="channel">
                                    <span
                                        class="label-text-alt text-error {formErrorhostname ? '' : (
                                            'hidden'
                                        )}">Host name must be between 2 and 32 characters long</span
                                    >
                                </label>
                            </div>
                            <label
                                class="label inline-flex cursor-pointer content-end justify-start gap-4"
                            >
                                <input
                                    type="checkbox"
                                    bind:checked={wifiSettings.priority_RSSI}
                                    class="checkbox checkbox-primary sm:-mb-5"
                                />
                                <span class="sm:-mb-5">Connect to strongest WiFi</span>
                            </label>
                        </div>

                        {#if showNetworkEditor}
                            <div class="divider my-0" />
                            <div
                                class="grid w-full grid-cols-1 content-center gap-x-4 px-4 sm:grid-cols-2"
                                transition:slide|local={{ duration: 300, easing: cubicOut }}
                            >
                                <div>
                                    <label class="label" for="ssid">
                                        <span class="label-text text-md">SSID</span>
                                    </label>
                                    <input
                                        type="text"
                                        class="input input-bordered invalid:border-error w-full invalid:border-2 {(
                                            formErrors.ssid
                                        ) ?
                                            'border-error border-2'
                                        :   ''}"
                                        bind:value={networkEditable.ssid}
                                        id="ssid"
                                        min="2"
                                        max="32"
                                        required
                                    />
                                    <label class="label" for="ssid">
                                        <span
                                            class="label-text-alt text-error {formErrors.ssid ? ''
                                            :   'hidden'}"
                                            >SSID must be between 3 and 32 characters long</span
                                        >
                                    </label>
                                </div>
                                <div>
                                    <label class="label" for="pwd">
                                        <span class="label-text text-md">Password</span>
                                    </label>
                                    <PasswordInput bind:value={networkEditable.password} id="pwd" />
                                </div>
                                <label
                                    class="label inline-flex cursor-pointer content-end justify-start gap-4 mt-2 sm:mb-4"
                                >
                                    <input
                                        type="checkbox"
                                        bind:checked={static_ip_config}
                                        class="checkbox checkbox-primary sm:-mb-5"
                                    />
                                    <span class="sm:-mb-5">Static IP Config?</span>
                                </label>
                            </div>
                            {#if static_ip_config}
                                <div
                                    class="grid w-full grid-cols-1 content-center gap-x-4 px-4 sm:grid-cols-2"
                                    transition:slide|local={{ duration: 300, easing: cubicOut }}
                                >
                                    <div>
                                        <label class="label" for="localIP">
                                            <span class="label-text text-md">Local IP</span>
                                        </label>
                                        <input
                                            type="text"
                                            class="input input-bordered w-full {(
                                                formErrors.local_ip
                                            ) ?
                                                'border-error border-2'
                                            :   ''}"
                                            minlength="7"
                                            maxlength="15"
                                            size="15"
                                            bind:value={networkEditable.local_ip}
                                            id="localIP"
                                            required
                                        />
                                        <label class="label" for="localIP">
                                            <span
                                                class="label-text-alt text-error {(
                                                    formErrors.local_ip
                                                ) ?
                                                    ''
                                                :   'hidden'}">Must be a valid IPv4 address</span
                                            >
                                        </label>
                                    </div>

                                    <div>
                                        <label class="label" for="gateway">
                                            <span class="label-text text-md">Gateway IP</span>
                                        </label>
                                        <input
                                            type="text"
                                            class="input input-bordered w-full {(
                                                formErrors.gateway_ip
                                            ) ?
                                                'border-error border-2'
                                            :   ''}"
                                            minlength="7"
                                            maxlength="15"
                                            size="15"
                                            bind:value={networkEditable.gateway_ip}
                                            required
                                        />
                                        <label class="label" for="gateway">
                                            <span
                                                class="label-text-alt text-error {(
                                                    formErrors.gateway_ip
                                                ) ?
                                                    ''
                                                :   'hidden'}">Must be a valid IPv4 address</span
                                            >
                                        </label>
                                    </div>
                                    <div>
                                        <label class="label" for="subnet">
                                            <span class="label-text text-md">Subnet Mask</span>
                                        </label>
                                        <input
                                            type="text"
                                            class="input input-bordered w-full {(
                                                formErrors.subnet_mask
                                            ) ?
                                                'border-error border-2'
                                            :   ''}"
                                            minlength="7"
                                            maxlength="15"
                                            size="15"
                                            bind:value={networkEditable.subnet_mask}
                                            required
                                        />
                                        <label class="label" for="subnet">
                                            <span
                                                class="label-text-alt text-error {(
                                                    formErrors.subnet_mask
                                                ) ?
                                                    ''
                                                :   'hidden'}">Must be a valid IPv4 address</span
                                            >
                                        </label>
                                    </div>
                                    <div>
                                        <label class="label" for="gateway">
                                            <span class="label-text text-md">DNS 1</span>
                                        </label>
                                        <input
                                            type="text"
                                            class="input input-bordered w-full {formErrors.dns_1 ?
                                                'border-error border-2'
                                            :   ''}"
                                            minlength="7"
                                            maxlength="15"
                                            size="15"
                                            bind:value={networkEditable.dns_ip_1}
                                            required
                                        />
                                        <label class="label" for="gateway">
                                            <span
                                                class="label-text-alt text-error {formErrors.dns_1 ?
                                                    ''
                                                :   'hidden'}">Must be a valid IPv4 address</span
                                            >
                                        </label>
                                    </div>
                                    <div>
                                        <label class="label" for="subnet">
                                            <span class="label-text text-md">DNS 2</span>
                                        </label>
                                        <input
                                            type="text"
                                            class="input input-bordered w-full {formErrors.dns_2 ?
                                                'border-error border-2'
                                            :   ''}"
                                            minlength="7"
                                            maxlength="15"
                                            size="15"
                                            bind:value={networkEditable.dns_ip_2}
                                            required
                                        />
                                        <label class="label" for="subnet">
                                            <span
                                                class="label-text-alt text-error {formErrors.dns_2 ?
                                                    ''
                                                :   'hidden'}">Must be a valid IPv4 address</span
                                            >
                                        </label>
                                    </div>
                                </div>
                            {/if}
                        {/if}

                        <div class="divider mb-2 mt-0" />
                        <div class="mx-4 flex flex-wrap justify-end gap-2">
                            <button
                                class="btn btn-primary"
                                type="submit"
                                disabled={!showNetworkEditor}
                                >{newNetwork ? 'Add Network' : 'Update Network'}</button
                            >
                            <button
                                class="btn btn-primary"
                                type="button"
                                on:click={validateHostName}>Apply Settings</button
                            >
                        </div>
                    </form>
                </div>
            {/await}
        </div>
    {/if}
</SettingsCard>
