import { isNavigationFailure } from 'vue-router'

async function waitFor<T>(
  obtain: () => T | Promise<T>,
  tries = 30,
  delay = 100
): Promise<T | undefined> {
  for (let i = 0; i < tries; i++) {
    const result = await obtain()

    if (result !== undefined) {
      return result
    }

    await new Promise((resolve) => setTimeout(resolve, delay))
  }

  return undefined
}

export default defineNuxtPlugin((app) => {
  app.hook('app:mounted', () => {
    const router = useRouter()

    router.options.scrollBehavior = (to, from, savedPosition) => {
      if (to.query.freeze) {
        return false
      }

      if (to.hash && to.path === from.path) {
        return false
      }

      return { top: 0 }
    }

    router.afterEach(async (to, from, failure) => {
      if (isNavigationFailure(failure)) {
        console.warn(`Failed navigation: ${JSON.stringify(failure)}`)
      } else if (to.hash) {
        const [header, element, atTop] = await Promise.all([
          waitFor(() => document.querySelector('#scaffold-header') ?? undefined),
          waitFor(() => document.querySelector(to.hash) ?? undefined),
          waitFor(() => (to.path === from.path || window.scrollY < 10 ? true : undefined)),
        ])

        if (element && atTop) {
          const offset = header ? header.offsetHeight : 0
          const position = element.getBoundingClientRect().top

          window.scrollTo({ top: position - offset + window.scrollY })
        }
      }
    })
  })
})
