<script lang="ts">
  import {
    CharEmotion,
    SizeStore,
    selectedCharID,
    settingsOpen,
    sideBarStore,
  } from "../../ts/stores";
  import { DataBase, setDatabase, type folder } from "../../ts/database";
  import BarIcon from "./BarIcon.svelte";
  import SidebarIndicator from "./SidebarIndicator.svelte";
  import {
    Plus,
    User,
    X,
    Settings,
    Users,
    Edit3Icon,
    ArrowUp,
    ArrowDown,
    ListIcon,
    LayoutGridIcon,
    PlusIcon,
    FolderIcon,
    FolderOpenIcon,
  } from "lucide-svelte";
  import {
    characterFormatUpdate,
    createNewCharacter,
    createNewGroup,
    getCharImage,
  } from "../../ts/characters";
  import { importCharacter } from "src/ts/characterCards";
  import CharConfig from "./CharConfig.svelte";
  import { language } from "../../lang";
  import Botpreset from "../Setting/botpreset.svelte";
  import { onDestroy } from "svelte";
  import { isEqual } from "lodash";
  import SidebarAvatar from "./SidebarAvatar.svelte";
  import BaseRoundedButton from "../UI/BaseRoundedButton.svelte";
  import { get } from "svelte/store";
  import { findCharacterIndexbyId, findCharacterbyId, getCharacterIndexObject } from "src/ts/util";
  import { v4 } from "uuid";
  import { checkCharOrder } from "src/ts/globalApi";
  let openPresetList = false;
  let sideBarMode = 0;
  let editMode = false;
  let menuMode = 0;
  export let openGrid = () => {};

  function createScratch() {
    reseter();
    const cid = createNewCharacter();
    selectedCharID.set(-1);
  }
  function createGroup() {
    reseter();
    const cid = createNewGroup();
    selectedCharID.set(-1);
  }
  async function createImport() {
    reseter();
    await importCharacter();
    selectedCharID.set(-1);
  }

  function changeChar(index: number) {
    reseter();
    characterFormatUpdate(index);
    selectedCharID.set(index);
  }

  function reseter() {
    menuMode = 0;
    sideBarMode = 0;
    editMode = false;
    settingsOpen.set(false);
    CharEmotion.set({});
  }

  type sortTypeNormal = { type:'normal',img: string; index: number; }
  type sortType =  sortTypeNormal|{type:'folder',folder:sortTypeNormal[],id:string}
  let charImages: sortType[] = [];
  let IconRounded = false
  let openFolders:string[] = []

  const unsub = DataBase.subscribe((db) => {
    let newCharImages: sortType[] = [];
    const idObject = getCharacterIndexObject()
    for (const id of db.characterOrder) {
      if(typeof(id) === 'string'){
        const index = idObject[id] ?? -1
        if(index !== -1){
          const cha = db.characters[index]
          newCharImages.push({
            img:cha.image ?? "",
            index:index,
            type: "normal"
          });
        }
      }
      else{
        const folder = id
        let folderCharImages: sortTypeNormal[] = []
        for(const id of folder.data){
          const index = idObject[id] ?? -1
          if(index !== -1){
            const cha = db.characters[index]
            folderCharImages.push({
              img:cha.image ?? "",
              index:index,
              type: "normal"
            });
          }
        }
        newCharImages.push({
          folder: folderCharImages,
          type: "folder",
          id: folder.id
        });
      }
    }
    if (!isEqual(charImages, newCharImages)) {
      charImages = newCharImages;
    }
    if(IconRounded !== db.roundIcons){
      IconRounded = db.roundIcons
    }
  });

  const inserter = (mainIndex:DragData, targetIndex:DragData) => {
    if(mainIndex.index === targetIndex.index && mainIndex.folder === targetIndex.folder){
      return
    }
    let db = get(DataBase)
    let mainFolderIndex = mainIndex.folder ? getFolderIndex(mainIndex.folder) : null
    let targetFolderIndex = targetIndex.folder ? getFolderIndex(targetIndex.folder) : null
    let mainFolderId = mainIndex.folder ? (db.characterOrder[mainFolderIndex] as folder).id : ''
    let mainId = ''
    if(mainIndex.folder){
      mainId = (db.characterOrder[mainFolderIndex] as folder).data[mainIndex.index]
    }
    else{
      const da = db.characterOrder[mainIndex.index]
      if(typeof(da) !== 'string'){
        return
      }
      mainId = da
    }
    if(targetIndex.folder){
        const folder = db.characterOrder[targetFolderIndex] as folder
        folder.data.splice(targetIndex.index,0,mainId)
        db.characterOrder[targetFolderIndex] = folder
    }
    else{
        db.characterOrder.splice(targetIndex.index,0,mainId)
    }
    if(mainIndex.folder){
      mainFolderIndex = -1
      for(let i=0;i<db.characterOrder.length;i++){
        const a =db.characterOrder[i]
        if(typeof(a) !== 'string'){
          if(a.id === mainFolderId){
            mainFolderIndex = i
            break
          }
        }
      }
      if(mainFolderIndex !== -1){
        const folder:folder = db.characterOrder[mainFolderIndex] as folder
        const ind = mainIndex.index > targetIndex.index ? folder.data.lastIndexOf(mainId) : folder.data.indexOf(mainId) 
        if(ind !== -1){
          folder.data.splice(ind, 1)
        }
        db.characterOrder[mainFolderIndex] = folder
      }
      else{
        console.log('folder not found')
      }
    }
    else{
      const ind = mainIndex.index > targetIndex.index ? db.characterOrder.lastIndexOf(mainId) : db.characterOrder.indexOf(mainId) 
      if(ind !== -1){
        db.characterOrder.splice(ind, 1)
      }
    }

    setDatabase(db)
    checkCharOrder()
  }

  function getFolderIndex(id:string){
    let db = get(DataBase)
    for(let i=0;i<db.characterOrder.length;i++){
      const data = db.characterOrder[i]
      if(typeof(data) !== 'string' && data.id === id){
        return i
      }
    }
    return -1
  }

  const createFolder = (mainIndex:DragData, targetIndex:DragData) => {
    if(mainIndex.index === targetIndex.index && mainIndex.folder === targetIndex.folder){
      return
    }
    let db = get(DataBase)
    let mainFolderIndex = mainIndex.folder ? getFolderIndex(mainIndex.folder) : null
    let mainFolder = db.characterOrder[mainFolderIndex] as folder
    if(targetIndex.folder){
      return
    }
    const main = mainIndex.folder ? mainFolder.data[mainIndex.index] : db.characterOrder[mainIndex.index]
    const target = db.characterOrder[targetIndex.index]
    if(typeof(main) !== 'string'){
      return
    }
    if(typeof (target) === 'string'){
      const newFolder:folder = {
        name: "New Folder",
        data: [main, target],
        color: "",
        id: v4()
      }
      db.characterOrder[targetIndex.index] = newFolder
      if(mainIndex.folder){
        mainFolder.data.splice(mainIndex.index, 1)
        db.characterOrder[mainFolderIndex] = mainFolder
      }
      else{
        db.characterOrder.splice(mainIndex.index, 1)
      }
    }
    else{
      target.data.push(main)
      if(mainIndex.folder){
        mainFolder.data.splice(mainIndex.index, 1)
        db.characterOrder[mainFolderIndex] = mainFolder
      }
      else{
        db.characterOrder.splice(mainIndex.index, 1)
      }
    }
    setDatabase(db)
  }

  type DragEv = DragEvent & {
    currentTarget: EventTarget & HTMLDivElement;
  }
  type DragData = {
    index:number,
    folder?:string
  }
  const avatarDragStart = (ind:DragData, e:DragEv) => {
    e.dataTransfer.setData("application/json", JSON.stringify({
      type: "risuDrag",
      index: ind
    }))
    const avatar = e.currentTarget.querySelector('.avatar')
      if(avatar){
        e.dataTransfer.setDragImage(avatar, 10, 10);
      }
  }

  const avatarDragOver = (e:DragEv) => {
    e.preventDefault()
    e.dataTransfer.dropEffect = 'move'
  }

  const avatarDrop = (ind:DragData, e:DragEv) => {
    e.preventDefault()
    try {
      const da = JSON.parse(e.dataTransfer.getData("application/json"))
      if(da.type === "risuDrag"){
        createFolder(da.index,ind)
      }
    } catch (error) {}
  }

  onDestroy(unsub);
</script>

<div
  class="flex h-full w-20 min-w-20 flex-col items-center overflow-x-hidden overflow-y-scroll bg-bgcolor text-white shadow-lg"
  class:editMode
>
  <button
    class="absolute top-0 flex h-8 w-14 min-w-14 cursor-pointer items-center justify-center rounded-b-md bg-gray-500 transition-colors hover:bg-green-500"
    on:click={() => {
      menuMode = 1 - menuMode;
    }}><ListIcon /></button
  >
  <div class="h-8 min-h-8 w-14 min-w-14 bg-transparent" />
  <div class="h-3 min-h-3 w-14" on:dragover={(e) => {
    e.preventDefault()
    e.dataTransfer.dropEffect = 'move'
    e.currentTarget.classList.add('bg-green-500')
  }} on:dragleave={(e) => {
    e.currentTarget.classList.remove('bg-green-500')
  }} on:drop={(e) => {
    e.preventDefault()
    e.currentTarget.classList.remove('bg-green-500')
    const da = JSON.parse(e.dataTransfer.getData("application/json"))
    if(da.type === "risuDrag"){
      inserter(da.index,{index:0})
    }
  }} on:dragenter|preventDefault />
  {#if menuMode === 0}
    {#each charImages as char, ind}
      <div class="group relative flex items-center px-2"
        draggable="true"
        on:dragstart={(e) => {avatarDragStart({index:ind}, e)}}
        on:dragover={avatarDragOver}
        on:drop={(e) => {avatarDrop({index:ind}, e)}}
        on:dragenter|preventDefault
      >
        <SidebarIndicator
          isActive={char.type === 'normal' && $selectedCharID === char.index && sideBarMode !== 1}
        />
        <!-- svelte-ignore a11y-no-noninteractive-tabindex -->
        <div
            on:click={() => {
              if(char.type === "normal"){
                changeChar(char.index);
              }
            }}
            on:keydown={(e) => {
              if (e.key === "Enter") {
                if(char.type === "normal"){
                  changeChar(char.index);
                }
              }
            }}
            tabindex="0"
          >
          {#if char.type === 'normal'}
            <SidebarAvatar src={char.img ? getCharImage(char.img, "plain") : "/none.webp"} size="56" rounded={IconRounded} />
          {:else if char.type === "folder"}
            <SidebarAvatar src="slot" size="56" rounded={IconRounded} onClick={() => {
              if(char.type !== 'folder'){
                return
              }
              if(openFolders.includes(char.id)){
                openFolders.splice(openFolders.indexOf(char.id), 1)
              }
              else{
                openFolders.push(char.id)
              }
              openFolders = openFolders
            }}>
              {#if openFolders.includes(char.id)}
                <FolderOpenIcon />
              {:else}
                <FolderIcon />
              {/if}
            </SidebarAvatar>
          {/if}
        </div>
      </div>
      {#if char.type === 'folder' && openFolders.includes(char.id)}
        <div class="w-full flex flex-col items-center py-1 mt-1 rounded-lg relative">
          <div class="absolute top-0 left-1 bg-darkbg w-full h-full rounded-lg z-0"></div>
          <div class="h-3 min-h-3 w-14 relative z-10" on:dragover={(e) => {
            e.preventDefault()
            e.dataTransfer.dropEffect = 'move'
            e.currentTarget.classList.add('bg-green-500')
          }} on:dragleave={(e) => {
            e.currentTarget.classList.remove('bg-green-500')
          }} on:drop={(e) => {
            e.preventDefault()
            e.currentTarget.classList.remove('bg-green-500')
            const da = JSON.parse(e.dataTransfer.getData("application/json"))
            if(da.type === "risuDrag", char.type === 'folder'){
              inserter(da.index,{index:0,folder:char.id})
            }
          }} on:dragenter|preventDefault/>
          {#each char.folder as char2, ind}
              <div class="group relative flex items-center px-2 z-10"
              draggable="true"
              on:dragstart={(e) => {if(char.type === 'folder'){avatarDragStart({index: ind, folder:char.id}, e)}}}
              on:dragover={avatarDragOver}
              on:drop={(e) => {if(char.type === 'folder'){avatarDrop({index: ind, folder:char.id}, e)}}}
              on:dragenter|preventDefault
            >
              <SidebarIndicator
                isActive={$selectedCharID === char2.index && sideBarMode !== 1}
              />
              <!-- svelte-ignore a11y-no-noninteractive-tabindex -->
              <div
                  on:click={() => {
                    if(char2.type === "normal"){
                      changeChar(char2.index);
                    }
                  }}
                  on:keydown={(e) => {
                    if (e.key === "Enter") {
                      if(char2.type === "normal"){
                        changeChar(char2.index);
                      }
                    }
                  }}
                  tabindex="0"
                >
                <SidebarAvatar src={char2.img ? getCharImage(char2.img, "plain") : "/none.webp"} size="56" rounded={IconRounded} />
              </div>
            </div>
            <div class="h-3 min-h-3 w-14 relative z-10" on:dragover={(e) => {
              e.preventDefault()
              e.dataTransfer.dropEffect = 'move'
              e.currentTarget.classList.add('bg-green-500')
            }} on:dragleave={(e) => {
              e.currentTarget.classList.remove('bg-green-500')
            }} on:drop={(e) => {
              e.preventDefault()
              e.currentTarget.classList.remove('bg-green-500')
              const da = JSON.parse(e.dataTransfer.getData("application/json"))
              if(da.type === "risuDrag" && char.type === 'folder'){
                inserter(da.index,{index:ind+1,folder:char.id})
              }
            }} on:dragenter|preventDefault/>
          {/each}
        </div>
      {/if}
      <div class="h-3 min-h-3 w-14" on:dragover={(e) => {
        e.preventDefault()
        e.dataTransfer.dropEffect = 'move'
        e.currentTarget.classList.add('bg-green-500')
      }} on:dragleave={(e) => {
        e.currentTarget.classList.remove('bg-green-500')
      }} on:drop={(e) => {
        e.preventDefault()
        e.currentTarget.classList.remove('bg-green-500')
        const da = JSON.parse(e.dataTransfer.getData("application/json"))
        if(da.type === "risuDrag"){
          inserter(da.index,{index:ind+1})
        }
      }} on:dragenter|preventDefault />
    {/each}
    <div class="flex flex-col items-center space-y-2 px-2">
      <BaseRoundedButton
        onClick={() => {
          if (sideBarMode === 1) {
            reseter();
            sideBarMode = 0;
          } else {
            reseter();
            sideBarMode = 1;
          }
        }}
        ><svg viewBox="0 0 24 24" width="1.2em" height="1.2em"
          ><path
            fill="none"
            stroke="currentColor"
            stroke-linecap="round"
            stroke-linejoin="round"
            stroke-width="2"
            d="M12 6v6m0 0v6m0-6h6m-6 0H6"
          /></svg
        ></BaseRoundedButton
      >
    </div>
  {:else}
    <BarIcon
      onClick={() => {
        if ($settingsOpen) {
          reseter();
          settingsOpen.set(false);
        } else {
          reseter();
          settingsOpen.set(true);
        }
      }}><Settings /></BarIcon
    >
    <BarIcon
      onClick={() => {
        reseter();
        openGrid();
      }}><LayoutGridIcon /></BarIcon
    >
  {/if}
</div>
<div
  class="setting-area flex w-96 flex-col overflow-y-auto overflow-x-hidden bg-darkbg p-6 text-gray-200"
  class:flex-grow={$SizeStore.w <= 1000}
  class:minw96={$SizeStore.w > 1000}
>
  <button
    class="flex w-full justify-end text-gray-200"
    on:click={() => {
      sideBarStore.set(false);
    }}
  >
    <button class="border-none bg-transparent p-0 text-gray-200"><X /></button>
  </button>
  {#if sideBarMode === 0}
    {#if $selectedCharID < 0 || $settingsOpen}
      <div>
        <h1 class="text-xl">Welcome to RisuAI!</h1>
        <span class="text-xs text-gray-400">Select a bot to start chating</span>
      </div>
    {:else}
      <CharConfig />
    {/if}
  {:else if sideBarMode === 1}
    <h2 class="title mt-2 text-xl font-bold">Create</h2>
    <button
      on:click={createScratch}
      class="ml-2 mr-2 mt-2 flex items-center justify-center border-1 border-solid border-borderc p-5 text-lg drop-shadow-lg hover:bg-selected"
    >
      {language.createfromScratch}
    </button>
    <button
      on:click={createImport}
      class="ml-2 mr-2 mt-2 flex items-center justify-center border-1 border-solid border-borderc p-5 text-lg drop-shadow-lg hover:bg-selected"
    >
      {language.importCharacter}
    </button>
    <button
      on:click={createGroup}
      class="ml-2 mr-2 mt-2 flex items-center justify-center border-1 border-solid border-borderc p-3 drop-shadow-lg hover:bg-selected"
    >
      {language.createGroup}
    </button>
    <h2 class="title mt-4 text-xl font-bold">Edit</h2>
    <button
      on:click={() => {
        editMode = !editMode;
        $selectedCharID = -1;
      }}
      class="ml-2 mr-2 mt-2 flex items-center justify-center border-1 border-solid border-borderc p-3 drop-shadow-lg hover:bg-selected"
    >
      {language.editOrder}
    </button>
  {/if}
</div>

{#if openPresetList}
  <Botpreset
    close={() => {
      openPresetList = false;
    }}
  />
{/if}

<style>
  .minw96 {
    min-width: 24rem; /* 384px */
  }
  .title {
    margin-bottom: 0.5rem;
  }
  .editMode {
    min-width: 6rem;
  }
</style>
