import onFind from './dom/onFind'
import Cookies from 'js-cookie'
import rtc from './rtc'

interface PublishBroadcastData {
  mainObjectId: string
  contentId: string
}

// Updates the list of suggestions added to fields
function updateAddedSuggestions(): void {
  const addedSuggestions = new Set<string>()
  const selected = document.getElementsByClassName('ContentSelector-select')

  Array.from(selected)
    .filter((list) => list.firstChild !== null)
    .forEach((list) => {
      const firstChild = list.firstChild as HTMLElement
      const id = firstChild?.getAttribute('value')
      const parentNode = list.parentNode as HTMLElement
      if (id && !parentNode.classList.contains('is-removing')) {
        addedSuggestions.add(id)
      }
    })
  updateSuggestionsVisibility(addedSuggestions)
}

// Updates the visibility of added suggestions
function updateSuggestionsVisibility(
  addedSuggestionsArray2: Set<string>,
): void {
  const addedClassName = 'is-added'
  document
    .querySelectorAll<HTMLElement>('.Suggestion-add')
    .forEach((suggestion) => {
      if (suggestion !== null) {
        const id = suggestion.dataset.suggestionId
        if (id && addedSuggestionsArray2.has(id)) {
          suggestion.classList.add(addedClassName)
        } else {
          suggestion.classList.remove(addedClassName)
        }
      }
    })
}

// Add suggestions to the correct field when clicked
onFind('.Suggestion-add', (item: HTMLElement) => {
  item.addEventListener('click', () => {
    if (item.dataset.label) {
      addItem(item, item.dataset.label)
      updateAddedSuggestions()
    }
  })
})

// Post to the servlet for new suggestions, sending the form so changes can be recognized
onFind('button[data-suggestions-url]', (element: HTMLButtonElement) => {
  const url = element.dataset.suggestionsUrl
  if (!url) return

  element.addEventListener('click', async () => {
    if (element.classList.contains('is-refreshing')) {
      return
    }

    try {
      const frame = element.closest<HTMLElement>('.frame')
      if (!frame) return

      // Show spinner while waiting on response
      element.classList.add('is-refreshing')

      const form = frame.closest<HTMLFormElement>('form')
      if (!form) return

      const data = new FormData(form)
      const headers = new Headers()
      const csrf = Cookies.get('bsp.csrf')
      if (csrf) {
        headers.append('Brightspot-CSRF', csrf)
      }

      const response = await fetch(url, {
        method: 'POST',
        headers: headers,
        body: data,
      })
      const itemHtml = await response.text()

      // Response received, remove spinner
      element.classList.remove('is-refreshing')

      frame.innerHTML = itemHtml
    } catch (error) {
      console.error(error)
    }
  })

  // Set suggestions to inactive or active after page load
  updateAddedSuggestions()
})

// Receive publish broadcasts for new Suggestion items and add the item to the field
onFind('.Suggestions-list-add', (suggestionsList: HTMLElement) => {
  const suggestionIds = Array.from(
    suggestionsList.querySelectorAll<HTMLElement>(
      '.Suggestion-add[data-suggestion-id]',
    ),
  )
    .map((el) => el.dataset.suggestionId)
    .filter((id): id is string => id !== undefined)

  // Hide collapsible with empty suggestions results
  if (
    suggestionsList.childNodes.length === 0 &&
    suggestionsList.parentNode instanceof HTMLElement
  ) {
    suggestionsList.parentNode.style.display = 'none'
  }

  rtc.receive(
    'com.psddev.cms.tool.page.content.PublishBroadcast',
    (data: unknown) => {
      const publishData = data as PublishBroadcastData
      const contentId = publishData.mainObjectId || publishData.contentId
      if (suggestionIds.indexOf(contentId) === -1) {
        setTimeout(() => {
          const refresh = document.querySelector<HTMLButtonElement>(
            'button[data-suggestions-url]',
          )
          if (refresh) {
            refresh.click()
          }
        }, 2000)
      }
    },
  )
})

// Add a suggestion to the correct field and scroll to it
function addItem(item: HTMLElement, label: string): void {
  const form = item.closest<HTMLFormElement>('form[data-content-id]')
  if (!form) return

  const fieldName = item.dataset.fieldName
  if (!fieldName) return

  const fieldDiv = form.querySelector<HTMLElement>(
    `div[data-field-name="${fieldName}"]`,
  )
  if (!fieldDiv) return

  const addButton = fieldDiv.querySelector<HTMLElement>('.addButton')
  let fieldInput: HTMLInputElement | null

  if (addButton) {
    addButton.click()
    const repeatableObjectId = fieldDiv.querySelector<HTMLElement>(
      '.repeatableObjectId',
    )
    const addToTop = repeatableObjectId?.classList.contains('addToTop')
    fieldInput = addToTop
      ? fieldDiv.querySelector<HTMLInputElement>(
          ':first-child > input.objectId',
        )
      : fieldDiv.querySelector<HTMLInputElement>(':last-child > input.objectId')
  } else {
    fieldInput = fieldDiv.querySelector<HTMLInputElement>('input.objectId')
  }

  if (!fieldInput) return

  fieldInput.setAttribute('data-label', label)
  fieldInput.setAttribute('data-label-html', label)

  const suggestionId = item.dataset.suggestionId
  if (suggestionId) {
    fieldInput.value = suggestionId
  }

  if (addButton) {
    const sourceContainer = fieldInput.closest<HTMLLIElement>('li')
    if (sourceContainer) {
      const typeId = item.dataset.typeId
      if (typeId) {
        sourceContainer.setAttribute('data-sortable-item-type', typeId)
      }

      onFind(
        sourceContainer,
        ':scope .ComboInput[aria-expanded=true]',
        (dropDown: HTMLElement) => {
          dropDown.click()
        },
      )
    }
  } else {
    fieldDiv.querySelectorAll<HTMLElement>('.is-removing').forEach((el) => {
      el.classList.remove('is-removing')
    })
  }

  const event = document.createEvent('HTMLEvents')
  event.initEvent('change', true, false)
  fieldInput.dispatchEvent(event)

  fieldDiv.scrollIntoView()
}
