import "@logseq/libs"
import { setup, t } from "logseq-l10n"
import { render } from "preact"
import { throttle } from "rambdax"
import FavList from "./comps/FavList"
import { allExpansionKeys, removeExpansionState } from "./libs/storage"
import { hash, queryForSubItems, setLanguage, waitForEl, queryPropertyAndSort } from "./libs/utils"
import zhCN from "./translations/zh-CN.json"

const CLEAN_WAIT = 3000

let dragHandle: HTMLElement | null = null

async function main() {
  const { preferredLanguage: lang } = await logseq.App.getUserConfigs()
  setLanguage(logseq.settings?.sortingLocale || lang)

  await setup({ builtinTranslations: { "zh-CN": zhCN } })

  provideStyles()

  logseq.useSettingsSchema([
    {
      key: "hierarchyProperty",
      title: "",
      type: "string",
      default: "tags",
      description: t(
        "It controls which property is used to decide a tag's hierarchy.",
      ),
    },
    {
      key: "sortByProperty",
      title: "",
      type: "string",
      default: "index",
      description: t(
        "It controls which property is used to sort favorite items.",
      ),
    },
    {
      key: "filterIcon",
      title: "",
      type: "string",
      default: "🔍",
      description: t("Define an icon for quick filters."),
    },
    {
      key: "hoverArrow",
      title: "",
      type: "boolean",
      default: false,
      description: t("Show arrows only when hovered."),
    },
    {
      key: "taggedPageLimit",
      title: "",
      type: "number",
      default: 30,
      description: t(
        "Maximum number of tagged pages to display on each level for favorites.",
      ),
    },
    {
      key: "sortingLocale",
      title: "",
      type: "string",
      default: "",
      description: t(
        "Locale used in sorting hierarchical favorites. E.g, zh-CN. Keep it empty to use Logseq's language setting.",
      ),
    },
  ])

  const favoritesObserver = new MutationObserver(async (mutationList) => {
    const mutation = mutationList[0]
    if (mutation?.target == null) return
    const target = mutation.target as HTMLElement

    if (
      target.classList?.contains("nav-content-item-inner") ||
      target.classList?.contains("favorites")
    ) {
      await processFavorites()
    }
  })
  const favoritesEl = await waitForEl("#left-sidebar .favorites", 300)
  if (favoritesEl != null) {
    favoritesObserver.observe(favoritesEl, { childList: true, subtree: true })
  }

  const transactionOff = logseq.DB.onChanged(onTransaction)

  const graphOff = logseq.App.onCurrentGraphChanged(async () => {
    ;(window as any).storage = logseq.Assets.makeSandboxStorage()
    await adjustLeftBarWidth()
  })

  await waitForEl("#left-sidebar .favorite-item", 1000)
  ;(window as any).storage = logseq.Assets.makeSandboxStorage()

  const readKeys = new Set<string>()
  await processFavorites(readKeys)
  setTimeout(async () => {
    const keys = await allExpansionKeys()
    const notReadKeys = keys.filter((key: any) => !readKeys.has(key))
    for (const key of notReadKeys) {
      await removeExpansionState(key)
    }
  }, CLEAN_WAIT)

  await adjustLeftBarWidth()

  logseq.provideUI({
    key: "kef-ft-drag-handle",
    path: "#left-sidebar",
    template: `<div class="kef-ft-drag-handle"></div>`,
  })
  setTimeout(() => {
    dragHandle = parent.document.querySelector(
      "#left-sidebar .kef-ft-drag-handle",
    )!
    dragHandle.addEventListener("pointerdown", onPointerDown)
  }, 0)

  logseq.beforeunload(async () => {
    graphOff()
    transactionOff()
    favoritesObserver.disconnect()
    dragHandle?.removeEventListener("pointerdown", onPointerDown)
  })

  console.log("#favorite-tree loaded")
}

function provideStyles() {
  logseq.provideStyle({
    key: "kef-ft-fav",
    style: `
      .kef-ft-fav-list {
        padding-left: 24px;
        display: none;
      }
      .kef-ft-fav-expanded {
        display: block;
      }
      .kef-ft-fav-arrow {
        flex: 0 0 auto;
        padding: 4px 20px 4px 10px;
        margin-right: -20px;
        opacity: ${logseq.settings?.hoverArrow ? 0 : 1};
        transition: opacity 0.3s;
      }
      :is(.favorite-item, .recent-item):hover > a > .kef-ft-fav-arrow,
      .kef-ft-fav-item:hover > .kef-ft-fav-arrow {
        opacity: 1;
      }
      .kef-ft-fav-arrow svg {
        transform: rotate(90deg) scale(0.8);
        transition: transform 0.04s linear;
      }
      .kef-ft-fav-arrow-expanded svg {
        transform: rotate(0deg) scale(0.8);
      }
      .kef-ft-fav-item {
        display: flex;
        align-items: center;
        padding: 0 24px;
        line-height: 28px;
        color: var(--ls-header-button-background);
        cursor: pointer;
      }
      .kef-ft-fav-item:hover {
        background-color: var(--ls-quaternary-background-color);
      }
      .kef-ft-fav-item-icon {
        flex: 0 0 auto;
        margin-right: 5px;
        width: 16px;
        text-align: center;
      }
      .kef-ft-fav-item-name {
        flex: 1 1 auto;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
      }

      .kef-ft-drag-handle {
        content: "";
        position: absolute;
        top: 0;
        bottom: 0;
        right: 0;
        width: 4px;
        z-index: 10;
      }
      .kef-ft-drag-handle:hover,
      .kef-ft-dragging .kef-ft-drag-handle {
        cursor: col-resize;
        background: var(--ls-active-primary-color);
      }
      .kef-ft-dragging {
        cursor: col-resize;
      }
      .kef-ft-dragging :is(#left-sidebar, #main-content-container) {
        pointer-events: none;
      }
    `,
  })
}

async function processFavorites(readKeys?: Set<string>) {
  const favorites = parent.document.querySelectorAll<HTMLElement>(
    `#left-sidebar .favorite-item`,
  )
  //console.log("Processing favorites...")
  //console.log(favorites)
  // We do not want, to sort the base level
  for (const fav of favorites) {
    /*
    items: [{displayName: "Eng",
      name: "school/eng"
      original-name: "School/Eng"
      uuid: "68a2f289-0043-4718-be51-7b7687abbc2 }]
    */
    const items = await queryForSubItems(fav.dataset.ref!)
    //console.log(items)
    if (items?.length > 0) {
      injectList(fav, items, readKeys)
    }
  }
}

async function injectList(
  el: HTMLElement,
  items: any[],
  readKeys?: Set<string>,
) {
  const key = `kef-ft-f-${await hash(el.dataset.ref!)}`

  const arrowContainer = el.querySelector("a")!
  const arrow = arrowContainer.querySelector(".kef-ft-fav-arrow")
  if (arrow != null) {
    arrow.remove()
  }

  if (parent.document.getElementById(key) == null) {
    logseq.provideUI({
      key,
      path: `.favorite-item[data-ref="${el.dataset.ref}"]`,
      template: `<div id="${key}"></div>`,
    })
  }

  setTimeout(() => {
    renderList(key, items, arrowContainer, el.dataset.ref!, readKeys)
  }, 0)
}

function renderList(
  key: string,
  items: any[],
  arrowContainer: HTMLElement,
  name: string,
  readKeys?: Set<string>,
) {
  const el = parent.document.getElementById(key)!
  render(
    <FavList
      items={items}
      arrowContainer={arrowContainer}
      name={name}
      readKeys={readKeys}
    />,
    el,
  )
}

async function onTransaction({ blocks, txData, txMeta }: any) {
  if (needsProcessing(txData)) {
    await processFavorites()
  }
}

async function adjustLeftBarWidth() {
  const graph = (await logseq.App.getCurrentGraph())!
  const storedWidth = parent.localStorage.getItem(`kef-ft-lsw-${graph.name}`)
  if (storedWidth) {
    parent.document.documentElement.style.setProperty(
      "--ls-left-sidebar-width",
      `${+storedWidth}px`,
    )
  }
}

function needsProcessing(txData: any[]) {
  const hierarchyProperty = logseq.settings?.hierarchyProperty ?? "tags"
  let oldProperty, newProperty
  let oldQuickFilters, newQuickFilters
  for (const [_e, attr, val, _tx, added] of txData) {
    if (attr === "originalName") return true
    if (hierarchyProperty === "tags" && attr === "tags") return true
    if (attr === "properties") {
      if (val[hierarchyProperty]) {
        if (added) {
          newProperty = val[hierarchyProperty]
        } else {
          oldProperty = val[hierarchyProperty]
        }
      }
      if (val.quickFilters) {
        if (added) {
          newQuickFilters = val.quickFilters
        } else {
          oldQuickFilters = val.quickFilters
        }
      }
    }
  }
  if (
    (!oldProperty && !newProperty && !oldQuickFilters && !newQuickFilters) ||
    (oldProperty?.toString() === newProperty?.toString() &&
      oldQuickFilters === newQuickFilters)
  )
    return false
  return true
}

function onPointerDown(e: Event) {
  e.preventDefault()
  parent.document.documentElement.classList.add("kef-ft-dragging")
  parent.document.addEventListener("pointermove", onPointerMove)
  parent.document.addEventListener("pointerup", onPointerUp)
  parent.document.addEventListener("pointercancel", onPointerUp)
}

function onPointerUp(e: MouseEvent) {
  e.preventDefault()
  parent.document.removeEventListener("pointermove", onPointerMove)
  parent.document.removeEventListener("pointerup", onPointerUp)
  parent.document.removeEventListener("pointercancel", onPointerUp)
  parent.document.documentElement.classList.remove("kef-ft-dragging")

  const pos = Math.max(150, e.clientX)
  parent.document.documentElement.style.setProperty(
    "--ls-left-sidebar-width",
    `${pos}px`,
  )
  ;(async () => {
    const graph = (await logseq.App.getCurrentGraph())!
    parent.localStorage.setItem(`kef-ft-lsw-${graph.name}`, `${pos}`)
  })()
}

function onPointerMove(e: MouseEvent) {
  e.preventDefault()
  move(Math.max(150, e.clientX))
}

const move = throttle((pos) => {
  parent.document.documentElement.style.setProperty(
    "--ls-left-sidebar-width",
    `${pos}px`,
  )
}, 12)

logseq.ready(main).catch(console.error)
