<template>
  <div
    :class="{
      'rounded border border-divider': saveRequest,
      'bg-primaryDark':
        draggingToRoot && currentReorderingStatus.type !== 'request',
    }"
    class="flex-1"
    @drop.prevent="dropToRoot"
    @dragover.prevent="draggingToRoot = true"
    @dragend="draggingToRoot = false"
  >
    <div
      class="sticky z-10 flex flex-col flex-shrink-0 overflow-x-auto border-b bg-primary border-dividerLight"
      :class="{ 'rounded-t': saveRequest }"
      :style="
        saveRequest ? 'top: calc(-1 * var(--line-height-body))' : 'top: 0'
      "
    >
      <WorkspaceCurrent :section="t('tab.collections')" />

      <HoppSmartInput
        v-model="filterTexts"
        :placeholder="t('action.search')"
        input-styles="py-2 pl-4 pr-2 bg-transparent !border-0"
        type="search"
        :autofocus="false"
        :disabled="collectionsType.type === 'team-collections'"
      />
    </div>
    <CollectionsMyCollections
      v-if="collectionsType.type === 'my-collections'"
      :collections-type="collectionsType"
      :filtered-collections="filteredCollections"
      :filter-text="filterTexts"
      :save-request="saveRequest"
      :picked="picked"
      @add-folder="addFolder"
      @add-request="addRequest"
      @edit-collection="editCollection"
      @edit-folder="editFolder"
      @export-data="exportData"
      @remove-collection="removeCollection"
      @remove-folder="removeFolder"
      @drop-collection="dropCollection"
      @update-request-order="updateRequestOrder"
      @update-collection-order="updateCollectionOrder"
      @edit-request="editRequest"
      @duplicate-request="duplicateRequest"
      @remove-request="removeRequest"
      @select-request="selectRequest"
      @select="selectPicked"
      @drop-request="dropRequest"
      @display-modal-add="displayModalAdd(true)"
      @display-modal-import-export="displayModalImportExport(true)"
    />
    <CollectionsTeamCollections
      v-else
      :collections-type="collectionsType"
      :team-collection-list="teamCollectionList"
      :team-loading-collections="teamLoadingCollections"
      :export-loading="exportLoading"
      :duplicate-loading="duplicateLoading"
      :save-request="saveRequest"
      :picked="picked"
      :collection-move-loading="collectionMoveLoading"
      :request-move-loading="requestMoveLoading"
      @add-request="addRequest"
      @add-folder="addFolder"
      @edit-collection="editCollection"
      @edit-folder="editFolder"
      @export-data="exportData"
      @remove-collection="removeCollection"
      @remove-folder="removeFolder"
      @edit-request="editRequest"
      @duplicate-request="duplicateRequest"
      @remove-request="removeRequest"
      @select-request="selectRequest"
      @select="selectPicked"
      @drop-request="dropRequest"
      @drop-collection="dropCollection"
      @update-request-order="updateRequestOrder"
      @update-collection-order="updateCollectionOrder"
      @expand-team-collection="expandTeamCollection"
      @display-modal-add="displayModalAdd(true)"
      @display-modal-import-export="displayModalImportExport(true)"
    />
    <div
      class="hidden bg-primaryDark flex-col flex-1 items-center py-15 justify-center px-4 text-secondaryLight"
      :class="{
        '!flex': draggingToRoot && currentReorderingStatus.type !== 'request',
      }"
    >
      <icon-lucide-list-end class="svg-icons !w-8 !h-8" />
    </div>
    <CollectionsAdd
      :show="showModalAdd"
      :loading-state="modalLoadingState"
      @submit="addNewRootCollection"
      @hide-modal="displayModalAdd(false)"
    />
    <CollectionsAddRequest
      :show="showModalAddRequest"
      :loading-state="modalLoadingState"
      @add-request="onAddRequest"
      @hide-modal="displayModalAddRequest(false)"
    />
    <CollectionsAddFolder
      :show="showModalAddFolder"
      :loading-state="modalLoadingState"
      @add-folder="onAddFolder"
      @hide-modal="displayModalAddFolder(false)"
    />
    <CollectionsEdit
      :show="showModalEditCollection"
      :editing-collection-name="editingCollectionName ?? ''"
      :loading-state="modalLoadingState"
      @hide-modal="displayModalEditCollection(false)"
      @submit="updateEditingCollection"
    />
    <CollectionsEditFolder
      :show="showModalEditFolder"
      :editing-folder-name="editingFolderName ?? ''"
      :loading-state="modalLoadingState"
      @submit="updateEditingFolder"
      @hide-modal="displayModalEditFolder(false)"
    />
    <CollectionsEditRequest
      v-model="editingRequestName"
      :show="showModalEditRequest"
      :loading-state="modalLoadingState"
      @submit="updateEditingRequest"
      @hide-modal="displayModalEditRequest(false)"
    />
    <HoppSmartConfirmModal
      :show="showConfirmModal"
      :title="confirmModalTitle"
      :loading-state="modalLoadingState"
      @hide-modal="showConfirmModal = false"
      @resolve="resolveConfirmModal"
    />
    <CollectionsImportExport
      :show="showModalImportExport"
      :collections-type="collectionsType.type"
      :exporting-team-collections="exportingTeamCollections"
      :creating-gist-collection="creatingGistCollection"
      :importing-my-collections="importingMyCollections"
      @export-json-collection="exportJSONCollection"
      @create-collection-gist="createCollectionGist"
      @import-to-teams="importToTeams"
      @hide-modal="displayModalImportExport(false)"
    />
    <TeamsAdd
      :show="showTeamModalAdd"
      @hide-modal="displayTeamModalAdd(false)"
    />
  </div>
</template>

<script setup lang="ts">
import { computed, nextTick, PropType, ref, watch } from "vue"
import { useToast } from "@composables/toast"
import { useI18n } from "@composables/i18n"
import { Picked } from "~/helpers/types/HoppPicked"
import { useReadonlyStream } from "~/composables/stream"
import { useLocalState } from "~/newstore/localstate"
import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
import { pipe } from "fp-ts/function"
import * as TE from "fp-ts/TaskEither"
import {
  addRESTCollection,
  addRESTFolder,
  editRESTCollection,
  editRESTFolder,
  editRESTRequest,
  moveRESTRequest,
  removeRESTCollection,
  removeRESTFolder,
  removeRESTRequest,
  restCollections$,
  saveRESTRequestAs,
  updateRESTRequestOrder,
  updateRESTCollectionOrder,
  moveRESTFolder,
  navigateToFolderWithIndexPath,
  restCollectionStore,
} from "~/newstore/collections"
import TeamCollectionAdapter from "~/helpers/teams/TeamCollectionAdapter"
import {
  HoppCollection,
  HoppRESTRequest,
  makeCollection,
} from "@hoppscotch/data"
import { cloneDeep, isEqual } from "lodash-es"
import { GQLError } from "~/helpers/backend/GQLClient"
import {
  createNewRootCollection,
  createChildCollection,
  renameCollection,
  deleteCollection,
  importJSONToTeam,
  moveRESTTeamCollection,
  updateOrderRESTTeamCollection,
} from "~/helpers/backend/mutations/TeamCollection"
import {
  updateTeamRequest,
  createRequestInCollection,
  deleteTeamRequest,
  moveRESTTeamRequest,
  updateOrderRESTTeamRequest,
} from "~/helpers/backend/mutations/TeamRequest"
import { TeamCollection } from "~/helpers/teams/TeamCollection"
import { Collection as NodeCollection } from "./MyCollections.vue"
import {
  getCompleteCollectionTree,
  getTeamCollectionJSON,
  teamCollToHoppRESTColl,
} from "~/helpers/backend/helpers"
import * as E from "fp-ts/Either"
import { platform } from "~/platform"
import { createCollectionGists } from "~/helpers/gist"
import {
  getRequestsByPath,
  resolveSaveContextOnRequestReorder,
} from "~/helpers/collection/request"
import {
  getFoldersByPath,
  resolveSaveContextOnCollectionReorder,
  updateSaveContextForAffectedRequests,
  resetTeamRequestsContext,
} from "~/helpers/collection/collection"
import { currentReorderingStatus$ } from "~/newstore/reordering"
import { defineActionHandler } from "~/helpers/actions"
import { WorkspaceService } from "~/services/workspace.service"
import { useService } from "dioc/vue"
import { RESTTabService } from "~/services/tab/rest"

const t = useI18n()
const toast = useToast()
const tabs = useService(RESTTabService)

const props = defineProps({
  saveRequest: {
    type: Boolean,
    default: false,
    required: false,
  },
  picked: {
    type: Object as PropType<Picked | null>,
    default: null,
    required: false,
  },
})

const emit = defineEmits<{
  (event: "select", payload: Picked | null): void
  (event: "update-team", team: SelectedTeam): void
  (event: "update-collection-type", type: CollectionType["type"]): void
}>()

type SelectedTeam = GetMyTeamsQuery["myTeams"][number] | undefined

type CollectionType =
  | {
      type: "team-collections"
      selectedTeam: SelectedTeam
    }
  | { type: "my-collections"; selectedTeam: undefined }

const collectionsType = ref<CollectionType>({
  type: "my-collections",
  selectedTeam: undefined,
})

// Collection Data
const editingCollection = ref<
  HoppCollection<HoppRESTRequest> | TeamCollection | null
>(null)
const editingCollectionName = ref<string | null>(null)
const editingCollectionIndex = ref<number | null>(null)
const editingCollectionID = ref<string | null>(null)
const editingFolder = ref<
  HoppCollection<HoppRESTRequest> | TeamCollection | null
>(null)
const editingFolderName = ref<string | null>(null)
const editingFolderPath = ref<string | null>(null)
const editingRequest = ref<HoppRESTRequest | null>(null)
const editingRequestName = ref("")
const editingRequestIndex = ref<number | null>(null)
const editingRequestID = ref<string | null>(null)

const confirmModalTitle = ref<string | null>(null)

const filterTexts = ref("")

const currentUser = useReadonlyStream(
  platform.auth.getCurrentUserStream(),
  platform.auth.getCurrentUser()
)
const myCollections = useReadonlyStream(restCollections$, [], "deep")

// Draging
const draggingToRoot = ref(false)
const collectionMoveLoading = ref<string[]>([])
const requestMoveLoading = ref<string[]>([])

// Export - Import refs
const collectionJSON = ref("")
const exportingTeamCollections = ref(false)
const creatingGistCollection = ref(false)
const importingMyCollections = ref(false)

// TeamList-Adapter
const workspaceService = useService(WorkspaceService)
const teamListAdapter = workspaceService.acquireTeamListAdapter(null)
const myTeams = useReadonlyStream(teamListAdapter.teamList$, null)
const REMEMBERED_TEAM_ID = useLocalState("REMEMBERED_TEAM_ID")
const teamListFetched = ref(false)

// Team Collection Adapter
const teamCollectionAdapter = new TeamCollectionAdapter(null)
const teamCollectionList = useReadonlyStream(
  teamCollectionAdapter.collections$,
  []
)
const teamLoadingCollections = useReadonlyStream(
  teamCollectionAdapter.loadingCollections$,
  []
)

watch(
  () => myTeams.value,
  (newTeams) => {
    if (newTeams && !teamListFetched.value) {
      teamListFetched.value = true
      if (REMEMBERED_TEAM_ID.value && currentUser.value) {
        const team = newTeams.find((t) => t.id === REMEMBERED_TEAM_ID.value)
        if (team) updateSelectedTeam(team)
      }
    }
  }
)

watch(
  () => collectionsType.value.selectedTeam,
  (newTeam) => {
    if (newTeam) {
      teamCollectionAdapter.changeTeamID(newTeam.id)
    }
  }
)

const switchToMyCollections = () => {
  collectionsType.value.type = "my-collections"
  collectionsType.value.selectedTeam = undefined
  teamCollectionAdapter.changeTeamID(null)
}

const expandTeamCollection = (collectionID: string) => {
  teamCollectionAdapter.expandCollection(collectionID)
}

const updateSelectedTeam = (team: SelectedTeam) => {
  if (team) {
    collectionsType.value.type = "team-collections"
    collectionsType.value.selectedTeam = team
    REMEMBERED_TEAM_ID.value = team.id
    emit("update-team", team)
    emit("update-collection-type", "team-collections")
  }
}

const workspace = workspaceService.currentWorkspace

// Used to switch collection type and team when user switch workspace in the global workspace switcher
// Check if there is a teamID in the workspace, if yes, switch to team collections and select the team
// If there is no teamID, switch to my collections
watch(
  () => {
    const space = workspace.value
    return space.type === "personal" ? undefined : space.teamID
  },
  (teamID) => {
    if (teamID) {
      const team = myTeams.value?.find((t) => t.id === teamID)
      if (team) {
        updateSelectedTeam(team)
      }
      return
    }

    return switchToMyCollections()
  },
  {
    immediate: true,
  }
)

// Switch to my-collections and reset the team collection when user logout
watch(
  () => currentUser.value,
  (user) => {
    if (!user) {
      switchToMyCollections()
    }
  }
)

const currentReorderingStatus = useReadonlyStream(currentReorderingStatus$, {
  type: "collection",
  id: "",
  parentID: "",
})

const hasTeamWriteAccess = computed(() => {
  if (!collectionsType.value.selectedTeam) return false

  if (
    collectionsType.value.type === "team-collections" &&
    collectionsType.value.selectedTeam.myRole !== "VIEWER"
  )
    return true
  else return false
})

const filteredCollections = computed(() => {
  const collections =
    collectionsType.value.type === "my-collections" ? myCollections.value : []

  if (filterTexts.value === "") return collections

  if (collectionsType.value.type === "team-collections") return []

  const filterText = filterTexts.value.toLowerCase()
  const filteredCollections = []

  const isMatch = (text: string) => text.toLowerCase().includes(filterText)

  for (const collection of collections) {
    const filteredRequests = []
    const filteredFolders = []
    for (const request of collection.requests) {
      if (isMatch(request.name)) filteredRequests.push(request)
    }
    for (const folder of collection.folders) {
      if (isMatch(folder.name)) filteredFolders.push(folder)
      const filteredFolderRequests = []
      for (const request of folder.requests) {
        if (isMatch(request.name)) filteredFolderRequests.push(request)
      }
      if (filteredFolderRequests.length > 0) {
        const filteredFolder = Object.assign({}, folder)
        filteredFolder.requests = filteredFolderRequests
        filteredFolders.push(filteredFolder)
      }
    }

    if (
      filteredRequests.length + filteredFolders.length > 0 ||
      isMatch(collection.name)
    ) {
      const filteredCollection = Object.assign({}, collection)
      filteredCollection.requests = filteredRequests
      filteredCollection.folders = filteredFolders
      filteredCollections.push(filteredCollection)
    }
  }

  return filteredCollections
})

const isSelected = ({
  collectionIndex,
  folderPath,
  requestIndex,
  collectionID,
  folderID,
  requestID,
}: {
  collectionIndex?: number | undefined
  folderPath?: string | undefined
  requestIndex?: number | undefined
  collectionID?: string | undefined
  folderID?: string | undefined
  requestID?: string | undefined
}) => {
  if (collectionIndex !== undefined) {
    return (
      props.picked &&
      props.picked.pickedType === "my-collection" &&
      props.picked.collectionIndex === collectionIndex
    )
  } else if (requestIndex !== undefined && folderPath !== undefined) {
    return (
      props.picked &&
      props.picked.pickedType === "my-request" &&
      props.picked.folderPath === folderPath &&
      props.picked.requestIndex === requestIndex
    )
  } else if (folderPath !== undefined) {
    return (
      props.picked &&
      props.picked.pickedType === "my-folder" &&
      props.picked.folderPath === folderPath
    )
  } else if (collectionID !== undefined) {
    return (
      props.picked &&
      props.picked.pickedType === "teams-collection" &&
      props.picked.collectionID === collectionID
    )
  } else if (requestID !== undefined) {
    return (
      props.picked &&
      props.picked.pickedType === "teams-request" &&
      props.picked.requestID === requestID
    )
  } else if (folderID !== undefined) {
    return (
      props.picked &&
      props.picked.pickedType === "teams-folder" &&
      props.picked.folderID === folderID
    )
  }
}

const modalLoadingState = ref(false)
const exportLoading = ref(false)
const duplicateLoading = ref(false)

const showModalAdd = ref(false)
const showModalAddRequest = ref(false)
const showModalAddFolder = ref(false)
const showModalEditCollection = ref(false)
const showModalEditFolder = ref(false)
const showModalEditRequest = ref(false)
const showModalImportExport = ref(false)
const showConfirmModal = ref(false)
const showTeamModalAdd = ref(false)

const displayModalAdd = (show: boolean) => {
  showModalAdd.value = show

  if (!show) resetSelectedData()
}

const displayModalAddRequest = (show: boolean) => {
  showModalAddRequest.value = show

  if (!show) resetSelectedData()
}

const displayModalAddFolder = (show: boolean) => {
  showModalAddFolder.value = show

  if (!show) resetSelectedData()
}

const displayModalEditCollection = (show: boolean) => {
  showModalEditCollection.value = show

  if (!show) resetSelectedData()
}

const displayModalEditFolder = (show: boolean) => {
  showModalEditFolder.value = show

  if (!show) resetSelectedData()
}

const displayModalEditRequest = (show: boolean) => {
  showModalEditRequest.value = show

  if (!show) resetSelectedData()
}

const displayModalImportExport = (show: boolean) => {
  showModalImportExport.value = show

  if (!show) resetSelectedData()
}

const displayConfirmModal = (show: boolean) => {
  showConfirmModal.value = show

  if (!show) resetSelectedData()
}

const displayTeamModalAdd = (show: boolean) => {
  showTeamModalAdd.value = show

  teamListAdapter.fetchList()
}

const addNewRootCollection = (name: string) => {
  if (collectionsType.value.type === "my-collections") {
    addRESTCollection(
      makeCollection({
        name,
        folders: [],
        requests: [],
      })
    )

    platform.analytics?.logEvent({
      type: "HOPP_CREATE_COLLECTION",
      platform: "rest",
      workspaceType: "personal",
      isRootCollection: true,
    })

    displayModalAdd(false)
  } else if (hasTeamWriteAccess.value) {
    if (!collectionsType.value.selectedTeam) return
    modalLoadingState.value = true

    platform.analytics?.logEvent({
      type: "HOPP_CREATE_COLLECTION",
      platform: "rest",
      workspaceType: "team",
      isRootCollection: true,
    })

    pipe(
      createNewRootCollection(name, collectionsType.value.selectedTeam.id),
      TE.match(
        (err: GQLError<string>) => {
          toast.error(`${getErrorMessage(err)}`)
          modalLoadingState.value = false
        },
        () => {
          modalLoadingState.value = false
          toast.success(t("collection.created"))
          displayModalAdd(false)
        }
      )
    )()
  }
}

const addRequest = (payload: {
  path: string
  folder: HoppCollection<HoppRESTRequest> | TeamCollection
}) => {
  const { path, folder } = payload
  editingFolder.value = folder
  editingFolderPath.value = path
  displayModalAddRequest(true)
}

const onAddRequest = (requestName: string) => {
  const newRequest = {
    ...cloneDeep(tabs.currentActiveTab.value.document.request),
    name: requestName,
  }

  if (collectionsType.value.type === "my-collections") {
    const path = editingFolderPath.value
    if (!path) return
    const insertionIndex = saveRESTRequestAs(path, newRequest)

    tabs.createNewTab({
      request: newRequest,
      isDirty: false,
      saveContext: {
        originLocation: "user-collection",
        folderPath: path,
        requestIndex: insertionIndex,
      },
    })

    platform.analytics?.logEvent({
      type: "HOPP_SAVE_REQUEST",
      workspaceType: "personal",
      createdNow: true,
      platform: "rest",
    })

    displayModalAddRequest(false)
  } else if (hasTeamWriteAccess.value) {
    const folder = editingFolder.value

    if (!folder || !collectionsType.value.selectedTeam) return
    if (!folder.id) return

    modalLoadingState.value = true

    const data = {
      request: JSON.stringify(newRequest),
      teamID: collectionsType.value.selectedTeam.id,
      title: requestName,
    }

    platform.analytics?.logEvent({
      type: "HOPP_SAVE_REQUEST",
      workspaceType: "team",
      platform: "rest",
      createdNow: true,
    })

    pipe(
      createRequestInCollection(folder.id, data),
      TE.match(
        (err: GQLError<string>) => {
          toast.error(`${getErrorMessage(err)}`)
          modalLoadingState.value = false
        },
        (result) => {
          const { createRequestInCollection } = result

          tabs.createNewTab({
            request: newRequest,
            isDirty: false,
            saveContext: {
              originLocation: "team-collection",
              requestID: createRequestInCollection.id,
              collectionID: createRequestInCollection.collection.id,
              teamID: createRequestInCollection.collection.team.id,
            },
          })

          modalLoadingState.value = false
          displayModalAddRequest(false)
        }
      )
    )()
  }
}

const addFolder = (payload: {
  path: string
  folder: HoppCollection<HoppRESTRequest> | TeamCollection
}) => {
  const { path, folder } = payload
  editingFolder.value = folder
  editingFolderPath.value = path
  displayModalAddFolder(true)
}

const onAddFolder = (folderName: string) => {
  const path = editingFolderPath.value

  if (collectionsType.value.type === "my-collections") {
    if (!path) return
    addRESTFolder(folderName, path)

    platform.analytics?.logEvent({
      type: "HOPP_CREATE_COLLECTION",
      workspaceType: "personal",
      isRootCollection: false,
      platform: "rest",
    })

    displayModalAddFolder(false)
  } else if (hasTeamWriteAccess.value) {
    const folder = editingFolder.value
    if (!folder || !folder.id) return

    modalLoadingState.value = true

    platform.analytics?.logEvent({
      type: "HOPP_CREATE_COLLECTION",
      workspaceType: "personal",
      isRootCollection: false,
      platform: "rest",
    })

    pipe(
      createChildCollection(folderName, folder.id),
      TE.match(
        (err: GQLError<string>) => {
          if (err.error === "team_coll/short_title") {
            toast.error(t("folder.name_length_insufficient"))
          } else {
            toast.error(`${getErrorMessage(err)}`)
          }
          modalLoadingState.value = false
        },
        () => {
          toast.success(t("folder.created"))
          modalLoadingState.value = false
          displayModalAddFolder(false)
        }
      )
    )()
  }
}

const editCollection = (payload: {
  collectionIndex: string
  collection: HoppCollection<HoppRESTRequest> | TeamCollection
}) => {
  const { collectionIndex, collection } = payload
  editingCollection.value = collection
  if (collectionsType.value.type === "my-collections") {
    editingCollectionIndex.value = parseInt(collectionIndex)
    editingCollectionName.value = (
      collection as HoppCollection<HoppRESTRequest>
    ).name
  } else {
    editingCollectionName.value = (collection as TeamCollection).title
  }

  displayModalEditCollection(true)
}

const updateEditingCollection = (newName: string) => {
  if (!editingCollection.value) return

  if (!newName) {
    toast.error(t("collection.invalid_name"))
    return
  }

  if (collectionsType.value.type === "my-collections") {
    const collectionIndex = editingCollectionIndex.value
    if (collectionIndex === null) return

    const collectionUpdated = {
      ...editingCollection.value,
      name: newName,
    }

    editRESTCollection(
      collectionIndex,
      collectionUpdated as NodeCollection["data"]["data"]
    )
    displayModalEditCollection(false)
  } else if (hasTeamWriteAccess.value) {
    if (!editingCollection.value.id) return
    modalLoadingState.value = true

    pipe(
      renameCollection(editingCollection.value.id, newName),
      TE.match(
        (err: GQLError<string>) => {
          toast.error(`${getErrorMessage(err)}`)
          modalLoadingState.value = false
        },
        () => {
          modalLoadingState.value = false
          toast.success(t("collection.renamed"))
          displayModalEditCollection(false)
        }
      )
    )()
  }
}

const editFolder = (payload: {
  folderPath: string | undefined
  folder: HoppCollection<HoppRESTRequest> | TeamCollection
}) => {
  const { folderPath, folder } = payload
  editingFolder.value = folder
  if (collectionsType.value.type === "my-collections" && folderPath) {
    editingFolderPath.value = folderPath
    editingFolderName.value = (folder as HoppCollection<HoppRESTRequest>).name
  } else {
    editingFolderName.value = (folder as TeamCollection).title
  }
  displayModalEditFolder(true)
}

const updateEditingFolder = (newName: string) => {
  if (!editingFolder.value) return

  if (collectionsType.value.type === "my-collections") {
    if (!editingFolderPath.value) return

    editRESTFolder(editingFolderPath.value, {
      ...(editingFolder.value as HoppCollection<HoppRESTRequest>),
      name: newName,
    })
    displayModalEditFolder(false)
  } else if (hasTeamWriteAccess.value) {
    if (!editingFolder.value.id) return
    modalLoadingState.value = true

    /* renameCollection can be used to rename both collections and folders
     since folder is treated as collection in the BE. */
    pipe(
      renameCollection(editingFolder.value.id, newName),
      TE.match(
        (err: GQLError<string>) => {
          if (err.error === "team_coll/short_title") {
            toast.error(t("folder.name_length_insufficient"))
          } else {
            toast.error(`${getErrorMessage(err)}`)
          }
          modalLoadingState.value = false
        },
        () => {
          modalLoadingState.value = false
          toast.success(t("folder.renamed"))
          displayModalEditFolder(false)
        }
      )
    )()
  }
}

const editRequest = (payload: {
  folderPath: string | undefined
  requestIndex: string
  request: HoppRESTRequest
}) => {
  const { folderPath, requestIndex, request } = payload
  editingRequest.value = request
  editingRequestName.value = request.name ?? ""
  if (collectionsType.value.type === "my-collections" && folderPath) {
    editingFolderPath.value = folderPath
    editingRequestIndex.value = parseInt(requestIndex)
  } else {
    editingRequestID.value = requestIndex
  }
  displayModalEditRequest(true)
}

const updateEditingRequest = (newName: string) => {
  const request = editingRequest.value
  if (!request) return

  const requestUpdated = {
    ...request,
    name: newName || request.name,
  }
  if (collectionsType.value.type === "my-collections") {
    const folderPath = editingFolderPath.value
    const requestIndex = editingRequestIndex.value

    if (folderPath === null || requestIndex === null) return

    const possibleActiveTab = tabs.getTabRefWithSaveContext({
      originLocation: "user-collection",
      requestIndex,
      folderPath,
    })

    editRESTRequest(folderPath, requestIndex, requestUpdated)

    if (possibleActiveTab) {
      possibleActiveTab.value.document.request.name = requestUpdated.name
      nextTick(() => {
        possibleActiveTab.value.document.isDirty = false
      })
    }

    displayModalEditRequest(false)
  } else if (hasTeamWriteAccess.value) {
    modalLoadingState.value = true

    const requestID = editingRequestID.value
    const requestName = newName || request.name

    if (!requestID) return

    const data = {
      request: JSON.stringify(requestUpdated),
      title: requestName,
    }

    pipe(
      updateTeamRequest(requestID, data),
      TE.match(
        (err: GQLError<string>) => {
          toast.error(`${getErrorMessage(err)}`)
          modalLoadingState.value = false
        },
        () => {
          modalLoadingState.value = false
          toast.success(t("request.renamed"))
          displayModalEditRequest(false)
        }
      )
    )()

    const possibleTab = tabs.getTabRefWithSaveContext({
      originLocation: "team-collection",
      requestID,
    })

    if (possibleTab) {
      possibleTab.value.document.request.name = requestName
      nextTick(() => {
        possibleTab.value.document.isDirty = false
      })
    }
  }
}

const duplicateRequest = (payload: {
  folderPath: string
  request: HoppRESTRequest
}) => {
  const { folderPath, request } = payload
  if (!folderPath) return

  const newRequest = {
    ...cloneDeep(request),
    name: `${request.name} - ${t("action.duplicate")}`,
  }

  if (collectionsType.value.type === "my-collections") {
    saveRESTRequestAs(folderPath, newRequest)
    toast.success(t("request.duplicated"))
  } else if (hasTeamWriteAccess.value) {
    duplicateLoading.value = true

    if (!collectionsType.value.selectedTeam) return

    const data = {
      request: JSON.stringify(newRequest),
      teamID: collectionsType.value.selectedTeam.id,
      title: `${request.name} - ${t("action.duplicate")}`,
    }

    pipe(
      createRequestInCollection(folderPath, data),
      TE.match(
        (err: GQLError<string>) => {
          toast.error(`${getErrorMessage(err)}`)
          duplicateLoading.value = false
        },
        () => {
          duplicateLoading.value = false
          toast.success(t("request.duplicated"))
          displayModalAddRequest(false)
        }
      )
    )()
  }
}

const removeCollection = (id: string) => {
  if (collectionsType.value.type === "my-collections")
    editingCollectionIndex.value = parseInt(id)
  else editingCollectionID.value = id

  confirmModalTitle.value = `${t("confirm.remove_collection")}`
  displayConfirmModal(true)
}

/**
 * Used to delete both collections and folders
 * since folder is treated as collection in the BE.
 * @param collectionID - ID of the collection or folder to be deleted.
 */
const removeTeamCollectionOrFolder = async (collectionID: string) => {
  modalLoadingState.value = true

  await pipe(
    deleteCollection(collectionID),
    TE.match(
      (err: GQLError<string>) => {
        toast.error(`${getErrorMessage(err)}`)
        modalLoadingState.value = false
      },
      () => {
        modalLoadingState.value = false
        toast.success(t("state.deleted"))
        displayConfirmModal(false)
      }
    )
  )()
}

const onRemoveCollection = () => {
  if (collectionsType.value.type === "my-collections") {
    const collectionIndex = editingCollectionIndex.value

    const collectionToRemove =
      collectionIndex || collectionIndex == 0
        ? navigateToFolderWithIndexPath(restCollectionStore.value.state, [
            collectionIndex,
          ])
        : undefined

    if (collectionIndex === null) return

    if (
      isSelected({
        collectionIndex,
      })
    ) {
      emit("select", null)
    }

    removeRESTCollection(
      collectionIndex,
      collectionToRemove ? collectionToRemove.id : undefined
    )

    resolveSaveContextOnCollectionReorder({
      lastIndex: collectionIndex,
      newIndex: -1,
      folderPath: "", // root folder
      length: myCollections.value.length,
    })

    toast.success(t("state.deleted"))
    displayConfirmModal(false)
  } else if (hasTeamWriteAccess.value) {
    const collectionID = editingCollectionID.value

    if (!collectionID) return

    if (
      isSelected({
        collectionID,
      })
    ) {
      emit("select", null)
    }

    removeTeamCollectionOrFolder(collectionID).then(() => {
      resetTeamRequestsContext()
    })
  }
}

const removeFolder = (id: string) => {
  if (collectionsType.value.type === "my-collections")
    editingFolderPath.value = id
  else editingCollectionID.value = id

  confirmModalTitle.value = `${t("confirm.remove_folder")}`
  displayConfirmModal(true)
}

const onRemoveFolder = () => {
  if (collectionsType.value.type === "my-collections") {
    const folderPath = editingFolderPath.value

    if (!folderPath) return

    if (
      isSelected({
        folderPath,
      })
    ) {
      emit("select", null)
    }

    const folderToRemove = folderPath
      ? navigateToFolderWithIndexPath(
          restCollectionStore.value.state,
          folderPath.split("/").map((i) => parseInt(i))
        )
      : undefined

    removeRESTFolder(folderPath, folderToRemove ? folderToRemove.id : undefined)

    const parentFolder = folderPath.split("/").slice(0, -1).join("/") // remove last folder to get parent folder
    resolveSaveContextOnCollectionReorder({
      lastIndex: pathToLastIndex(folderPath),
      newIndex: -1,
      folderPath: parentFolder,
      length: getFoldersByPath(myCollections.value, parentFolder).length,
    })

    toast.success(t("state.deleted"))
    displayConfirmModal(false)
  } else if (hasTeamWriteAccess.value) {
    const collectionID = editingCollectionID.value

    if (!collectionID) return

    if (
      isSelected({
        collectionID,
      })
    ) {
      emit("select", null)
    }

    removeTeamCollectionOrFolder(collectionID).then(() => {
      resetTeamRequestsContext()
    })
  }
}

const removeRequest = (payload: {
  folderPath: string | null
  requestIndex: string
}) => {
  const { folderPath, requestIndex } = payload
  if (collectionsType.value.type === "my-collections" && folderPath) {
    editingFolderPath.value = folderPath
    editingRequestIndex.value = parseInt(requestIndex)
  } else {
    editingRequestID.value = requestIndex
  }
  confirmModalTitle.value = `${t("confirm.remove_request")}`
  displayConfirmModal(true)
}

const onRemoveRequest = () => {
  if (collectionsType.value.type === "my-collections") {
    const folderPath = editingFolderPath.value
    const requestIndex = editingRequestIndex.value

    if (folderPath === null || requestIndex === null) return

    if (
      isSelected({
        folderPath,
        requestIndex,
      })
    ) {
      emit("select", null)
    }

    const possibleTab = tabs.getTabRefWithSaveContext({
      originLocation: "user-collection",
      folderPath,
      requestIndex,
    })

    // If there is a tab attached to this request, dissociate its state and mark it dirty
    if (possibleTab) {
      possibleTab.value.document.saveContext = null
      possibleTab.value.document.isDirty = true
    }

    const requestToRemove = navigateToFolderWithIndexPath(
      restCollectionStore.value.state,
      folderPath.split("/").map((i) => parseInt(i))
    )?.requests[requestIndex]

    removeRESTRequest(folderPath, requestIndex, requestToRemove?.id)

    // the same function is used to reorder requests since after removing, it's basically doing reorder
    resolveSaveContextOnRequestReorder({
      lastIndex: requestIndex,
      newIndex: -1,
      folderPath,
      length: getRequestsByPath(myCollections.value, folderPath).length,
    })

    toast.success(t("state.deleted"))
    displayConfirmModal(false)
  } else if (hasTeamWriteAccess.value) {
    const requestID = editingRequestID.value

    if (!requestID) return

    if (
      isSelected({
        requestID,
      })
    ) {
      emit("select", null)
    }

    modalLoadingState.value = true

    pipe(
      deleteTeamRequest(requestID),
      TE.match(
        (err: GQLError<string>) => {
          toast.error(`${getErrorMessage(err)}`)
          modalLoadingState.value = false
        },
        () => {
          modalLoadingState.value = false
          toast.success(t("state.deleted"))
          displayConfirmModal(false)
        }
      )
    )()

    // If there is a tab attached to this request, dissociate its state and mark it dirty
    const possibleTab = tabs.getTabRefWithSaveContext({
      originLocation: "team-collection",
      requestID,
    })

    if (possibleTab) {
      possibleTab.value.document.saveContext = null
      possibleTab.value.document.isDirty = true
    }
  }
}

// The request is picked in the save request as modal
const selectPicked = (payload: Picked | null) => {
  emit("select", payload)
}

/**
 * This function is called when the user clicks on a request
 * @param selectedRequest The request that the user clicked on emited from the collection tree
 */
const selectRequest = (selectedRequest: {
  request: HoppRESTRequest
  folderPath: string | undefined
  requestIndex: string
  isActive: boolean
}) => {
  const { request, folderPath, requestIndex } = selectedRequest

  // If there is a request with this save context, switch into it
  let possibleTab = null

  if (collectionsType.value.type === "team-collections") {
    possibleTab = tabs.getTabRefWithSaveContext({
      originLocation: "team-collection",
      requestID: requestIndex,
    })
    if (possibleTab) {
      tabs.setActiveTab(possibleTab.value.id)
    } else {
      tabs.createNewTab({
        request: cloneDeep(request),
        isDirty: false,
        saveContext: {
          originLocation: "team-collection",
          requestID: requestIndex,
        },
      })
    }
  } else {
    possibleTab = tabs.getTabRefWithSaveContext({
      originLocation: "user-collection",
      requestIndex: parseInt(requestIndex),
      folderPath: folderPath!,
    })
    if (possibleTab) {
      tabs.setActiveTab(possibleTab.value.id)
    } else {
      // If not, open the request in a new tab
      tabs.createNewTab({
        request: cloneDeep(request),
        isDirty: false,
        saveContext: {
          originLocation: "user-collection",
          folderPath: folderPath!,
          requestIndex: parseInt(requestIndex),
        },
      })
    }
  }
}

/**
 * Used to get the index of the request from the path
 * @param path The path of the request
 * @returns The index of the request
 */
const pathToLastIndex = (path: string) => {
  const pathArr = path.split("/")
  return parseInt(pathArr[pathArr.length - 1])
}

/**
 * This function is called when the user drops the request inside a collection
 * @param payload Object that contains the folder path, request index and the destination collection index
 */
const dropRequest = (payload: {
  folderPath?: string | undefined
  requestIndex: string
  destinationCollectionIndex: string
}) => {
  const { folderPath, requestIndex, destinationCollectionIndex } = payload

  if (!requestIndex || !destinationCollectionIndex) return

  if (collectionsType.value.type === "my-collections" && folderPath) {
    moveRESTRequest(
      folderPath,
      pathToLastIndex(requestIndex),
      destinationCollectionIndex
    )

    const possibleTab = tabs.getTabRefWithSaveContext({
      originLocation: "user-collection",
      folderPath,
      requestIndex: pathToLastIndex(requestIndex),
    })

    // If there is a tab attached to this request, change save its save context
    if (possibleTab) {
      possibleTab.value.document.saveContext = {
        originLocation: "user-collection",
        folderPath: destinationCollectionIndex,
        requestIndex: getRequestsByPath(
          myCollections.value,
          destinationCollectionIndex
        ).length,
      }
    }

    // When it's drop it's basically getting deleted from last folder. reordering last folder accordingly
    resolveSaveContextOnRequestReorder({
      lastIndex: pathToLastIndex(requestIndex),
      newIndex: -1, // being deleted from last folder
      folderPath,
      length: getRequestsByPath(myCollections.value, folderPath).length,
    })

    toast.success(`${t("request.moved")}`)
    draggingToRoot.value = false
  } else if (hasTeamWriteAccess.value) {
    // add the request index to the loading array
    requestMoveLoading.value.push(requestIndex)

    pipe(
      moveRESTTeamRequest(destinationCollectionIndex, requestIndex),
      TE.match(
        (err: GQLError<string>) => {
          toast.error(`${getErrorMessage(err)}`)
          requestMoveLoading.value.splice(
            requestMoveLoading.value.indexOf(requestIndex),
            1
          )
        },
        () => {
          // remove the request index from the loading array
          requestMoveLoading.value.splice(
            requestMoveLoading.value.indexOf(requestIndex),
            1
          )

          const possibleTab = tabs.getTabRefWithSaveContext({
            originLocation: "team-collection",
            requestID: requestIndex,
          })

          if (possibleTab) {
            possibleTab.value.document.saveContext = {
              originLocation: "team-collection",
              requestID: requestIndex,
            }
          }
          toast.success(`${t("request.moved")}`)
        }
      )
    )()
  }
}

/**
 * @param path The path of the collection or request
 * @returns The index of the collection or request
 */
const pathToIndex = (path: string) => {
  const pathArr = path.split("/")
  return pathArr
}

/**
 * Used to check if the collection exist as the parent of the childrens
 * @param collectionIndexDragged The index of the collection dragged
 * @param destinationCollectionIndex The index of the destination collection
 * @returns True if the collection exist as the parent of the childrens
 */
const checkIfCollectionIsAParentOfTheChildren = (
  collectionIndexDragged: string,
  destinationCollectionIndex: string
) => {
  const collectionDraggedPath = pathToIndex(collectionIndexDragged)
  const destinationCollectionPath = pathToIndex(destinationCollectionIndex)

  if (collectionDraggedPath.length < destinationCollectionPath.length) {
    const slicedDestinationCollectionPath = destinationCollectionPath.slice(
      0,
      collectionDraggedPath.length
    )
    if (isEqual(slicedDestinationCollectionPath, collectionDraggedPath)) {
      return true
    } else {
      return false
    }
  }

  return false
}

const isMoveToSameLocation = (
  draggedItemPath: string,
  destinationPath: string
) => {
  const draggedItemPathArr = pathToIndex(draggedItemPath)
  const destinationPathArr = pathToIndex(destinationPath)

  if (draggedItemPathArr.length > 0) {
    const draggedItemParentPathArr = draggedItemPathArr.slice(
      0,
      draggedItemPathArr.length - 1
    )

    if (isEqual(draggedItemParentPathArr, destinationPathArr)) {
      return true
    } else {
      return false
    }
  }
}

/**
 * This function is called when the user moves the collection
 * to a different collection or folder
 * @param payload - object containing the collection index dragged and the destination collection index
 */
const dropCollection = (payload: {
  collectionIndexDragged: string
  destinationCollectionIndex: string
}) => {
  const { collectionIndexDragged, destinationCollectionIndex } = payload
  if (!collectionIndexDragged || !destinationCollectionIndex) return
  if (collectionIndexDragged === destinationCollectionIndex) return

  if (collectionsType.value.type === "my-collections") {
    if (
      checkIfCollectionIsAParentOfTheChildren(
        collectionIndexDragged,
        destinationCollectionIndex
      )
    ) {
      toast.error(`${t("team.parent_coll_move")}`)
      return
    }

    //check if the collection is being moved to its own parent
    if (
      isMoveToSameLocation(collectionIndexDragged, destinationCollectionIndex)
    ) {
      return
    }

    const parentFolder = collectionIndexDragged
      .split("/")
      .slice(0, -1)
      .join("/") // remove last folder to get parent folder
    const totalFoldersOfDestinationCollection =
      getFoldersByPath(myCollections.value, destinationCollectionIndex).length -
      (parentFolder === destinationCollectionIndex ? 1 : 0)

    moveRESTFolder(collectionIndexDragged, destinationCollectionIndex)

    resolveSaveContextOnCollectionReorder(
      {
        lastIndex: pathToLastIndex(collectionIndexDragged),
        newIndex: -1,
        folderPath: parentFolder,
        length: getFoldersByPath(myCollections.value, parentFolder).length,
      },
      "drop"
    )

    updateSaveContextForAffectedRequests(
      collectionIndexDragged,
      `${destinationCollectionIndex}/${totalFoldersOfDestinationCollection}`
    )

    draggingToRoot.value = false
    toast.success(`${t("collection.moved")}`)
  } else if (hasTeamWriteAccess.value) {
    // add the collection index to the loading array
    collectionMoveLoading.value.push(collectionIndexDragged)
    pipe(
      moveRESTTeamCollection(
        collectionIndexDragged,
        destinationCollectionIndex
      ),
      TE.match(
        (err: GQLError<string>) => {
          toast.error(`${getErrorMessage(err)}`)
          collectionMoveLoading.value.splice(
            collectionMoveLoading.value.indexOf(collectionIndexDragged),
            1
          )
        },
        () => {
          toast.success(`${t("collection.moved")}`)
          // remove the collection index from the loading array
          collectionMoveLoading.value.splice(
            collectionMoveLoading.value.indexOf(collectionIndexDragged),
            1
          )
        }
      )
    )()
  }
}

/**
 * Checks if the collection is already in the root
 * @param id - path of the collection
 * @returns boolean - true if the collection is already in the root
 */
const isAlreadyInRoot = (id: string) => {
  const indexPath = pathToIndex(id)
  return indexPath.length === 1
}

/**
 * This function is called when the user drops the collection
 * to the root
 * @param payload - object containing the collection index dragged
 */
const dropToRoot = ({ dataTransfer }: DragEvent) => {
  if (dataTransfer) {
    const collectionIndexDragged = dataTransfer.getData("collectionIndex")
    if (!collectionIndexDragged) return
    if (collectionsType.value.type === "my-collections") {
      // check if the collection is already in the root
      if (isAlreadyInRoot(collectionIndexDragged)) {
        toast.error(`${t("collection.invalid_root_move")}`)
      } else {
        moveRESTFolder(collectionIndexDragged, null)
        toast.success(`${t("collection.moved")}`)
      }

      draggingToRoot.value = false
    } else if (hasTeamWriteAccess.value) {
      // add the collection index to the loading array
      collectionMoveLoading.value.push(collectionIndexDragged)

      // destination collection index is null since we are moving to root
      pipe(
        moveRESTTeamCollection(collectionIndexDragged, null),
        TE.match(
          (err: GQLError<string>) => {
            collectionMoveLoading.value.splice(
              collectionMoveLoading.value.indexOf(collectionIndexDragged),
              1
            )
            toast.error(`${getErrorMessage(err)}`)
          },
          () => {
            // remove the collection index from the loading array
            collectionMoveLoading.value.splice(
              collectionMoveLoading.value.indexOf(collectionIndexDragged),
              1
            )
            toast.success(`${t("collection.moved")}`)
          }
        )
      )()
    }
  }
}

/**
 * Used to check if the request/collection is being moved to the same parent since reorder is only allowed within the same parent
 * @param draggedItem - path index of the dragged request
 * @param destinationItem - path index of the destination request
 * @param destinationCollectionIndex -  index of the destination collection
 * @returns boolean - true if the request is being moved to the same parent
 */
const isSameSameParent = (
  draggedItemPath: string,
  destinationItemPath: string | null,
  destinationCollectionIndex: string | null
) => {
  const draggedItemIndex = pathToIndex(draggedItemPath)

  // if the destinationItemPath and destinationCollectionIndex is null, it means the request is being moved to the root
  if (destinationItemPath === null && destinationCollectionIndex === null) {
    return draggedItemIndex.length === 1
  } else if (
    destinationItemPath === null &&
    destinationCollectionIndex !== null &&
    draggedItemIndex.length === 1
  ) {
    return draggedItemIndex[0] === destinationCollectionIndex
  } else if (
    destinationItemPath === null &&
    draggedItemIndex.length !== 1 &&
    destinationCollectionIndex !== null
  ) {
    const dragedItemParent = draggedItemIndex.slice(0, -1)

    return dragedItemParent.join("/") === destinationCollectionIndex
  } else {
    if (destinationItemPath === null) return false
    const destinationItemIndex = pathToIndex(destinationItemPath)

    // length of 1 means the request is in the root
    if (draggedItemIndex.length === 1 && destinationItemIndex.length === 1) {
      return true
    } else if (draggedItemIndex.length === destinationItemIndex.length) {
      const dragedItemParent = draggedItemIndex.slice(0, -1)
      const destinationItemParent = destinationItemIndex.slice(0, -1)
      if (isEqual(dragedItemParent, destinationItemParent)) {
        return true
      } else {
        return false
      }
    } else {
      return false
    }
  }
}

/**
 * This function is called when the user updates the request order in a collection
 * @param payload - object containing the request index dragged and the destination request index
 *  with the destination collection index
 */
const updateRequestOrder = (payload: {
  dragedRequestIndex: string
  destinationRequestIndex: string | null
  destinationCollectionIndex: string
}) => {
  const {
    dragedRequestIndex,
    destinationRequestIndex,
    destinationCollectionIndex,
  } = payload

  if (!dragedRequestIndex || !destinationCollectionIndex) return

  if (dragedRequestIndex === destinationRequestIndex) return

  if (collectionsType.value.type === "my-collections") {
    if (
      !isSameSameParent(
        dragedRequestIndex,
        destinationRequestIndex,
        destinationCollectionIndex
      )
    ) {
      toast.error(`${t("collection.different_parent")}`)
    } else {
      updateRESTRequestOrder(
        pathToLastIndex(dragedRequestIndex),
        destinationRequestIndex
          ? pathToLastIndex(destinationRequestIndex)
          : null,
        destinationCollectionIndex
      )

      toast.success(`${t("request.order_changed")}`)
    }
  } else if (hasTeamWriteAccess.value) {
    // add the request index to the loading array
    requestMoveLoading.value.push(dragedRequestIndex)

    pipe(
      updateOrderRESTTeamRequest(
        dragedRequestIndex,
        destinationRequestIndex,
        destinationCollectionIndex
      ),
      TE.match(
        (err: GQLError<string>) => {
          toast.error(`${getErrorMessage(err)}`)
          requestMoveLoading.value.splice(
            requestMoveLoading.value.indexOf(dragedRequestIndex),
            1
          )
        },
        () => {
          toast.success(`${t("request.order_changed")}`)

          // remove the request index from the loading array
          requestMoveLoading.value.splice(
            requestMoveLoading.value.indexOf(dragedRequestIndex),
            1
          )
        }
      )
    )()
  }
}

/**
 * This function is called when the user updates the collection or folder order
 * @param payload - object containing the collection index dragged and the destination collection index
 */
const updateCollectionOrder = (payload: {
  dragedCollectionIndex: string
  destinationCollection: {
    destinationCollectionIndex: string | null
    destinationCollectionParentIndex: string | null
  }
}) => {
  const { dragedCollectionIndex, destinationCollection } = payload
  const { destinationCollectionIndex, destinationCollectionParentIndex } =
    destinationCollection
  if (!dragedCollectionIndex) return
  if (dragedCollectionIndex === destinationCollectionIndex) return

  if (collectionsType.value.type === "my-collections") {
    if (
      !isSameSameParent(
        dragedCollectionIndex,
        destinationCollectionIndex,
        destinationCollectionParentIndex
      )
    ) {
      toast.error(`${t("collection.different_parent")}`)
    } else {
      updateRESTCollectionOrder(
        dragedCollectionIndex,
        destinationCollectionIndex
      )
      resolveSaveContextOnCollectionReorder({
        lastIndex: pathToLastIndex(dragedCollectionIndex),
        newIndex: pathToLastIndex(
          destinationCollectionIndex ? destinationCollectionIndex : ""
        ),
        folderPath: dragedCollectionIndex.split("/").slice(0, -1).join("/"),
      })
      toast.success(`${t("collection.order_changed")}`)
    }
  } else if (hasTeamWriteAccess.value) {
    collectionMoveLoading.value.push(dragedCollectionIndex)
    pipe(
      updateOrderRESTTeamCollection(
        dragedCollectionIndex,
        destinationCollectionIndex
      ),
      TE.match(
        (err: GQLError<string>) => {
          toast.error(`${getErrorMessage(err)}`)
          collectionMoveLoading.value.splice(
            collectionMoveLoading.value.indexOf(dragedCollectionIndex),
            1
          )
        },
        () => {
          toast.success(`${t("collection.order_changed")}`)
          collectionMoveLoading.value.splice(
            collectionMoveLoading.value.indexOf(dragedCollectionIndex),
            1
          )
        }
      )
    )()
  }
}
// Import - Export Collection functions
/**
 * Export the whole my collection or specific team collection to JSON
 */
const getJSONCollection = async () => {
  if (collectionsType.value.type === "my-collections") {
    collectionJSON.value = JSON.stringify(myCollections.value, null, 2)
  } else {
    if (!collectionsType.value.selectedTeam) return
    exportingTeamCollections.value = true
    pipe(
      await getTeamCollectionJSON(collectionsType.value.selectedTeam.id),
      E.match(
        (err) => {
          toast.error(`${getErrorMessage(err)}`)
          exportingTeamCollections.value = false
        },
        (result) => {
          const { exportCollectionsToJSON } = result
          collectionJSON.value = exportCollectionsToJSON
          exportingTeamCollections.value = false
        }
      )
    )
  }

  return collectionJSON.value
}

/**
 * Create a downloadable file from a collection and prompts the user to download it.
 * @param collectionJSON - JSON string of the collection
 * @param name - Name of the collection set as the file name
 */
const initializeDownloadCollection = (
  collectionJSON: string,
  name: string | null
) => {
  const file = new Blob([collectionJSON], { type: "application/json" })
  const a = document.createElement("a")
  const url = URL.createObjectURL(file)
  a.href = url

  if (name) {
    a.download = `${name}.json`
  } else {
    a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json`
  }

  document.body.appendChild(a)
  a.click()
  toast.success(t("state.download_started").toString())
  setTimeout(() => {
    document.body.removeChild(a)
    URL.revokeObjectURL(url)
  }, 1000)
}

/**
 * Export a specific collection or folder
 * Triggered by the export button in the tippy menu
 * @param collection - Collection or folder to be exported
 */
const exportData = async (
  collection: HoppCollection<HoppRESTRequest> | TeamCollection
) => {
  if (collectionsType.value.type === "my-collections") {
    const collectionJSON = JSON.stringify(collection)

    const name = (collection as HoppCollection<HoppRESTRequest>).name

    initializeDownloadCollection(collectionJSON, name)
  } else {
    if (!collection.id) return
    exportLoading.value = true

    pipe(
      getCompleteCollectionTree(collection.id),
      TE.match(
        (err: GQLError<string>) => {
          toast.error(`${getErrorMessage(err)}`)
          exportLoading.value = false
          return
        },
        (coll) => {
          const hoppColl = teamCollToHoppRESTColl(coll)
          const collectionJSONString = JSON.stringify(hoppColl)

          initializeDownloadCollection(collectionJSONString, hoppColl.name)
          exportLoading.value = false
        }
      )
    )()
  }
}

const exportJSONCollection = async () => {
  platform.analytics?.logEvent({
    type: "HOPP_EXPORT_COLLECTION",
    exporter: "json",
    platform: "rest",
  })

  await getJSONCollection()

  const parsedCollections = JSON.parse(collectionJSON.value)

  if (!parsedCollections.length) {
    return toast.error(t("error.no_collections_to_export"))
  }

  initializeDownloadCollection(collectionJSON.value, null)
}

const createCollectionGist = async () => {
  if (!currentUser.value || !currentUser.value.accessToken) {
    toast.error(t("profile.no_permission").toString())
    return
  }

  platform.analytics?.logEvent({
    type: "HOPP_EXPORT_COLLECTION",
    exporter: "gist",
    platform: "rest",
  })

  creatingGistCollection.value = true
  await getJSONCollection()

  pipe(
    createCollectionGists(collectionJSON.value, currentUser.value.accessToken),
    TE.match(
      (err) => {
        toast.error(t("error.something_went_wrong").toString())
        console.error(err)
        creatingGistCollection.value = false
      },
      (result) => {
        toast.success(t("export.gist_created").toString())
        creatingGistCollection.value = false
        window.open(result.data.html_url)
      }
    )
  )()
}

const importToTeams = async (collection: HoppCollection<HoppRESTRequest>[]) => {
  if (!hasTeamWriteAccess.value) {
    toast.error(t("team.no_access").toString())
    return
  }

  if (!collectionsType.value.selectedTeam) return

  importingMyCollections.value = true

  platform.analytics?.logEvent({
    type: "HOPP_EXPORT_COLLECTION",
    exporter: "import-to-teams",
    platform: "rest",
  })

  pipe(
    importJSONToTeam(
      JSON.stringify(collection),
      collectionsType.value.selectedTeam.id
    ),
    TE.match(
      (err: GQLError<string>) => {
        toast.error(`${getErrorMessage(err)}`)
        importingMyCollections.value = false
      },
      () => {
        importingMyCollections.value = false
        displayModalImportExport(false)
      }
    )
  )()
}

const resolveConfirmModal = (title: string | null) => {
  if (title === `${t("confirm.remove_collection")}`) onRemoveCollection()
  else if (title === `${t("confirm.remove_request")}`) onRemoveRequest()
  else if (title === `${t("confirm.remove_folder")}`) onRemoveFolder()
  else {
    console.error(
      `Confirm modal title ${title} is not handled by the component`
    )
    toast.error(t("error.something_went_wrong"))
    displayConfirmModal(false)
  }
}

const resetSelectedData = () => {
  editingCollection.value = null
  editingCollectionIndex.value = null
  editingCollectionID.value = null
  editingFolder.value = null
  editingFolderPath.value = null
  editingRequest.value = null
  editingRequestIndex.value = null
  editingRequestID.value = null
  confirmModalTitle.value = null
}

const getErrorMessage = (err: GQLError<string>) => {
  console.error(err)
  if (err.type === "network_error") {
    return t("error.network_error")
  } else {
    switch (err.error) {
      case "team_coll/short_title":
        return t("collection.name_length_insufficient")
      case "team/invalid_coll_id":
      case "bug/team_coll/no_coll_id":
      case "team_req/invalid_target_id":
        return t("team.invalid_coll_id")
      case "team/not_required_role":
        return t("profile.no_permission")
      case "team_req/not_required_role":
        return t("profile.no_permission")
      case "Forbidden resource":
        return t("profile.no_permission")
      case "team_req/not_found":
        return t("team.no_request_found")
      case "bug/team_req/no_req_id":
        return t("team.no_request_found")
      case "team/collection_is_parent_coll":
        return t("team.parent_coll_move")
      case "team/target_and_destination_collection_are_same":
        return t("team.same_target_destination")
      case "team/target_collection_is_already_root_collection":
        return t("collection.invalid_root_move")
      case "team_req/requests_not_from_same_collection":
        return t("request.different_collection")
      case "team/team_collections_have_different_parents":
        return t("collection.different_parent")
      default:
        return t("error.something_went_wrong")
    }
  }
}

defineActionHandler("collection.new", () => {
  displayModalAdd(true)
})
</script>
