// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
//
// SPDX-License-Identifier: AGPL-3.0-only

interface MessageWithLevel {
  level: 'info' | 'warning' | 'error'
  message: string
  help?: {
    url: string
    text: string
  }
}

interface Result {
  label?: string
  messages: Array<MessageWithLevel | string>
  debug?: Array<{
    title: string
    message: string
  }>
  next?: string | ((r: Result) => void)
  ok: boolean
  test: string
}

function launchTests (): void {
  let tokenType: string
  let accessToken: string
  let refreshToken: string

  const container = document.querySelector('div')
  if (!container) {
    throw new Error('Cant find main container')
  }
  container.innerHTML = ''

  const title = document.createElement('h1')
  title.textContent = 'Diagnostic'
  container.append(title)
  const ul = document.createElement('ul')
  container.append(ul)

  function appendMessages (result: Result): void {
    const li = document.createElement('li')

    const label = document.createElement('span')
    label.textContent = (result.label ?? result.test) + ': '
    li.append(label)

    const state = document.createElement('b')
    if (result.ok) {
      state.textContent = 'OK'
      state.style.color = 'green'
    } else {
      state.textContent = 'KO'
      state.style.color = 'red'
    }
    li.append(state)

    if (result.messages.length) {
      const messageUl = document.createElement('ul')
      for (let i = 0; i < result.messages.length; i++) {
        const messageLi = document.createElement('li')
        const message = result.messages[i]
        if (typeof message === 'string') {
          messageLi.textContent = message
        } else {
          const messageSpan = document.createElement('span')
          if (message.level === 'warning') {
            messageSpan.style.color = 'orange'
          } else if (message.level === 'error') {
            messageSpan.style.color = 'red'
          }
          messageSpan.textContent = message.message + ' ' // adding a space, in case there is a help button
          messageLi.append(messageSpan)

          if (message.help) {
            const helpButton = document.createElement('a')
            helpButton.href = message.help.url
            helpButton.textContent = message.help.text
            messageLi.append(helpButton)
          }
        }
        messageUl.append(messageLi)
      }
      li.append(messageUl)
    }

    ul.append(li)
  }

  let debugContainer: HTMLElement
  function appendDebug (result: Result): void {
    if (!result.debug?.length) { return }
    if (!debugContainer) {
      debugContainer = document.createElement('div')
      const title = document.createElement('h2')
      title.textContent = 'Additional debugging information'
      debugContainer.append(title)
      container?.append(debugContainer)
    }
    for (let i = 0; i < result.debug.length; i++) {
      const debug = result.debug[i]
      const title = document.createElement('h3')
      title.textContent = debug.title
      debugContainer.append(title)
      const message = document.createElement('div')
      message.setAttribute('style', 'white-space: pre;')
      message.textContent = debug.message
      debugContainer.append(message)
    }
  }

  function testBrowser (): Result {
    const r: Result = {
      ok: false,
      next: 'backend',
      messages: [],
      test: 'browser',
      label: 'Browser'
    }

    if (!window.fetch) {
      r.messages.push('Missing fetch function')
    }

    if (!window.localStorage) {
      r.messages.push('Browser has no local storage')
    } else {
      tokenType = window.localStorage.getItem('token_type') ?? ''
      accessToken = window.localStorage.getItem('access_token') ?? ''
      refreshToken = window.localStorage.getItem('refresh_token') ?? ''
      if (tokenType === '') {
        r.messages.push('Your browser is not authenticated on your Peertube instance (no token_type)')
      } else if (tokenType.toLowerCase() !== 'bearer') {
        r.messages.push('Your brower is authenticated with an unknown token type: ' + tokenType)
      } else if (accessToken === '') {
        r.messages.push('Your browser is not authenticated on your Peertube instance (no access_token)')
      } else if (refreshToken === '') {
        r.messages.push('Your browser is not authenticated on your Peertube instance (no refresh_token)')
      }
    }

    r.ok = r.messages.length === 0
    return r
  }

  let testUrl: string = window.location.pathname
  if (testUrl[testUrl.length - 1] !== '/') testUrl += '/'
  testUrl += 'test'
  async function callTest (test: string): Promise<Result> {
    const response = await window.fetch(testUrl, {
      method: 'POST',
      headers: new Headers({
        Authorization: tokenType + ' ' + accessToken,
        'content-type': 'application/json;charset=UTF-8'
      }),
      body: JSON.stringify({
        test
      })
    })
    if (!response.ok) {
      return {
        test,
        messages: [response.statusText ?? 'Unknown error'],
        ok: false
      }
    }
    const data = await response.json()
    if ((typeof data) !== 'object') {
      return {
        test,
        messages: ['Incorrect reponse type: ' + (typeof data)],
        ok: false
      }
    }
    return data as Result
  }

  async function machine (result: Result): Promise<void> {
    appendMessages(result)
    appendDebug(result)
    if (!result.ok) {
      return
    }
    if (!result.next) {
      return
    }
    const waiting = document.createElement('li')
    waiting.innerHTML = '<i>Testing...</i>'
    ul.append(waiting)
    if ((typeof result.next) === 'function') {
      // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
      const r: Result = (result.next as Function)()
      waiting.remove()
      await machine(r)
    } else {
      console.log('Calling test ' + (result.next as string))
      const r: Result = await callTest(result.next as string)
      waiting.remove()
      await machine(r)
    }
  }

  machine({
    label: 'Starting tests',
    messages: [],
    next: testBrowser,
    ok: true,
    test: 'start'
  }).then(
    () => {},
    (error) => {
      console.error(error)
      const d = document.createElement('div')
      d.style.color = 'red'
      d.textContent = error
      document.body.append(d)
    }
  )
}

launchTests()

export {}