<script>
    import { onMount }      from 'svelte';
    import { useTinyRouter } from 'svelte-tiny-router';
    import { api, drop, get, patch } from '../../urls.svelte';
    import { error, yikes } from '../../warn.svelte';
    import { t }            from '../../translations.svelte';

    import ItemList         from './ItemList.svelte';
    import ItemProps        from './ItemProps.svelte';
    import LineEditor       from '../../Components/LineEditor.svelte';
    import Locations        from './Locations.svelte';
    import MarkdownEditor   from '../../Components/MarkdownEditor.svelte';
    import Notes            from '../notes/RelatedNotes.svelte';
    import Tags             from '../tags/TagList.svelte';

    let loc_data        = $derived.by(loadLocation);
    let item            = $state(null);
    let location        = $state(null);
    let draggedItem     = $state(null)
    let draggedLocation = $state(null)
    let { item_id, location_id, owner, owner_id, owner_number } = $props();
    let skip_location = false; // disable effect on setting location within loadItem()
    let router = useTinyRouter();

    $effect(() => {
        // This effect runs whenever `location` changes
        if (!skip_location && location !== null) {
            item = null;
            setLocationUrl();
        }
    });

    $effect(() => {
        // This effect runs whenever `item` changes
        if (item !== null) {
            setItemUrl();
            skip_location = false;
        }
    });
    let properties = $state(null);
    let top_level = $state(null);

    async function deleteLocation(loc){
        if (!confirm(t('confirm_delete',{element:loc.name}))) return;
        const url = api(`stock/location/${loc.id}`);
        const res = await drop(url);
        if (res.ok){
            yikes();
            unlistLocation(loc);
        } else error(res);
    }

    function drag_item(item){
        draggedLocation = null;
        draggedItem = item;
    }

    function drag_location(loc){
        draggedItem = null;
        draggedLocation = loc;
    }

    function dropNestedLocation(locations,loc){
        for (let [idx,entry] of locations.entries()){
            if (entry.id == loc.id){
                locations.splice(idx,1);
                return true;
            }
            if (entry.locations && dropNestedLocation(entry.locations,loc)) return true;
        }
        return false;
    }

    async function move_dragged_to(new_loc){
        const data = draggedItem ? { item : draggedItem.id, target: new_loc.id } : { parent_location_id: new_loc.id }
        const url = api(draggedItem ? 'stock/move_item' : `stock/location/${draggedLocation.id}`);
        const res = await patch(url,data);
        if (res.ok){
            yikes();
            location        = new_loc;
            if (!draggedItem) unlistLocation(draggedLocation);
            draggedItem     = null;
            draggedLocation = null;
        } else {
            error(res);
        }
    }

    async function loadLocation(){
        if (!location) return null;
        const url = api(`stock/location/${location.id}`);
        const res = await get(url);
        if (res.ok){
            yikes();
            return res.json();
        } else {
            error(res);
            return null;
        }
    }

    async function loadItem(){
        if (!owner_number) return;
        const url = api(`stock/${owner}/${owner_id}/item/${owner_number}`);
        const res = await get(url);
        if (res.ok){
            yikes();
            const json = await res.json();
            const path = json.path;
            for (let owner of top_level){
                for (let loc of owner.locations){
                    if (loc.id == path.id) {
                        loc.locations = path.locations;
                        skip_location = true;
                        location = json.location;
                        break;
                    }
                }
            }
            for (let i of json.items){
                if (i.owner_number == +owner_number) item = i;
            }
        } else {
            error(res);
            return null;
        }
    }

    async function loadPath(){
        if (!location_id) return;
        const url = api(`stock/location/${location_id}`);
        const res = await get(url);
        if (res.ok){
            yikes();
            const json = await res.json();
            const path = json.path;
            for (let owner of top_level){
                for (let loc of owner.locations){
                    if (loc.id == path.id) {
                        loc.locations = path.locations;
                        location = json.location;
                        break;
                    }
                }
            }
        } else {
            error(res);
            return null;
        }
    }

    async function loadProperties(){
        const url = api('stock/properties')
        const res = await get(url);
        if (res.ok){
            var json = await res.json();
            var dict = {}
            for (var entry of json.sort((a,b) => b.id - a.id)) dict[entry.name+'.'+entry.unit] = entry;
            properties = null;
            properties = Object.values(dict).sort((a,b) => a.name.localeCompare(b.name));
            yikes();
        } else error(res);
    }

    async function loadUserLocations(){
        const url = api('stock/locations/of_user')
        const res = await get(url);
        if (res.ok){
            top_level = await res.json();
            yikes();
        } else error(res);
    }

    async function load(){
        await preload();
        await loadUserLocations();
        await loadPath();
        await loadProperties();
        await loadItem();
    }

    async function preload(){
        if (item_id) {
            let url = api(`stock/item/${item_id}`);
            const res = await get(url);
            if (res.ok){
                const json   = await res.json();
                owner        = json.owner.type;
                owner_id     = json.owner.id;
                owner_number = json.owner_number;
                location_id  = json.location.id;
            }
        }
    }

    function moveToTop(loc){
        if (patchLocation(location,'parent_location_id',0)){
            loc.parent_location_id = 0;
            for (var owner of top_level){
                if (owner.locations && dropNestedLocation(owner.locations,loc)) {
                    owner.locations.push(loc);
                    break;
                };
            }
        }
    }

    async function patchLocation(location,field,newValue){
        const data = {};
        data[field] = newValue;
        const url = api(`stock/location/${location.id}`);
        const res = await patch(url,data);
        if (res.ok){
            yikes();
            return true;
        } else {
            error(res);
            return false;
        }
    }

    function setItemUrl(){
        var owner = `/${item.owner.type}/${item.owner.id}`
        var code = `/item/${item.owner_number}`
        let url = window.location.origin + '/stock' + owner + code;
        window.history.replaceState(window.history.state, '', url);
    }

    function setLocationUrl(){
       let url = window.location.origin + '/stock/location/' + location.id;
       window.history.replaceState(window.history.state, '', url);
    }

    function unlistLocation(loc){
        for (var owner of top_level){
            if (owner.locations && dropNestedLocation(owner.locations,loc)) break;
        }
    }

    onMount(load);
</script>

<h2>{t('Stock')}</h2>
<div class="stock grid3">
    <div class="locations">
        {#if top_level}
        {#each top_level as realm,idx}
        <h3>{realm.name}</h3>
        {#if realm.locations}
        <Locations
                locations={realm.locations}
                parent={realm.parent}
                bind:selected={location}
                {move_dragged_to}
                drag_start={drag_location} />
        {/if}
        {/each}
        {/if}
    </div>
    {#await loc_data}
    <span>loading…</span>
    {:then data}
    <div class="items">
        {#if location}
        <h3>
            <LineEditor editable={true} bind:value={location.name} wrapper="span" onSet={newName => patchLocation(location,'name',newName)} />
            <button class="symbol" title={t('delete_object',{object:t('location')})} onclick={e => deleteLocation(location)}></button>
            {#if location.parent_location_id}
            <button class="symbol" title={t('move_to_top')} onclick={e => moveToTop(location)}></button>
            {/if}
        </h3>
        <MarkdownEditor editable={true} value={location.description} type="div" onSet={newDesc => patchLocation(location,'description',newDesc)} />
        {/if}
        <ItemList {location} items={data?.items.sort((a,b) => a.code.localeCompare(b.code))} bind:selected={item} drag_start={drag_item} />
    </div>
    <div class="properties">
        <ItemProps {item} {properties} />
    </div>
    {#if item && data && data.users}
    <div class="tags">
        <h4>{t('tags')}</h4>
        <Tags module="stock" id={item.id} user_list={data.users} />
    </div>
    <div class="notes">
        <h4>{t('notes')}</h4>
        <Notes module="stock" entity_id={item.id} />
    </div>
    {/if}
    {/await}
</div>
