(function ($, Drupal, once, drupalSettings) {
  'use strict'

  Drupal.siteimproveAccessibility = Drupal.siteimproveAccessibility || {}

  /**
   * Siteimprove Accessibility scan functionality.
   */
  Drupal.siteimproveAccessibility.scan = {

    /**
     * Runs the accessibility check on the page.
     */
    accessibilityCheck: async function () {
      const htmlDom = $('html')
        .clone()
        .find('#toolbar-administration, .siteimprove-component, .floating-si')
        .remove()
        .end()

      const alfaPage = await Drupal.alfaActions.alfaJQuery.toPage(htmlDom)
      return this.evaluatePage(alfaPage)
    },

    /**
     * Defines which rules to use for the evaluation.
     * Currently, uses AA and A conformance level rules.
     */
    getCustomRules: function () {
      return Drupal.alfaActions.alfaRules.filter(
        (rule) => Drupal.alfaActions.alfaRuleFilter.aaFilter(rule)
      )
    },

    /**
     * Evaluates the alfaPage scan results.
     */
    evaluatePage: async function (alfaPage) {
      const customRules = this.getCustomRules()
      const outcomes = await Drupal.alfaActions.alfaAudit.of(alfaPage, customRules).evaluate()
      return this.processAuditScan(outcomes, drupalSettings.siteimprove_accessibility.scannedNode)
    },

    /**
     * Processes audit scan results.
     */
    processAuditScan: function (outcomes, scannedNode) {
      const auditScan = {
        scannedNode,
        scan_results: [],
        scan_stats: {},
      }

      for (const outcome of outcomes) {
        if (outcome._outcome === 'failed') {
          const rule = outcome.rule.uri.split('/').pop()
          const conformance = Drupal.alfaActions.getRuleMeta(rule).conformance
          auditScan.scan_stats[rule] = auditScan.scan_stats[rule] || {
            conformance,
            occurrence: 0,
          }
          auditScan.scan_stats[rule].occurrence += 1

          auditScan.scan_results.push(
            outcome.toJSON({
              verbosity: Drupal.alfaActions.alfaJson.Serializable.Verbosity.Low,
            })
          )
        }
      }

      return auditScan
    },

    /**
     * Saves the audit scan results.
     */
    saveAuditScan: async function (auditScan) {
      try {
        const csrfResponse = await fetch('/session/token', {
          method: 'GET',
          credentials: 'include',
        })

        if (!csrfResponse.ok) {
          throw new Error('Failed to fetch CSRF token')
        }

        const csrfToken = await csrfResponse.text()

        const response = await fetch('/siteimprove-accessibility/save-scan', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'X-CSRF-Token': csrfToken,
          },
          credentials: 'include',
          body: JSON.stringify(auditScan),
        })

        if (!response.ok) {
          throw new Error(`Failed to save audit scan: ${response.statusText}`)
        }

        return await response.json()
      }
      catch (error) {
        console.error('Error saving audit scan.')
        return null
      }
    },

    /**
     * Runs the accessibility check, saves results, and renders SPRI.
     */
    run: async function () {
      try {
        const auditScan = await this.accessibilityCheck()
        const response = await this.saveAuditScan(auditScan)
        if (response) {
          Drupal.alfaActions.alfaRendererSPRI({ failedItems: auditScan.scan_results })
        }
      }
      catch (error) {
        console.error('Error running accessibility scan.')
      }
    },

    /**
     * Handles manual scans triggered by button clicks.
     */
    handleManualScan: function (context) {
      if (drupalSettings.siteimprove_accessibility.scannedNode.enableManualScans == true) {
        $('.alfa-scan-button').removeClass('hidden')
      }

      once('alfaScanButton', '.alfa-scan-button', context).forEach((element) => {
        element.addEventListener('click', async () => {
          const $button = $(element)
          const $throbber = $('.alfa-scan-throbber')

          // Disable button and show throbber
          $button.prop('disabled', true)
          $throbber.removeClass('hidden')

          try {
            $('.alfa-scan-button').hide()
            await this.run()

            // Show results and manipulate UI.
            $('.floating-si').addClass('floating-si-border-wrap')
            $('.alfa-scan-report').show()

          }
          catch (error) {
            console.error('Error during scan')
            alert('Something went wrong. Please try again.')
          }
          finally {
            // Re-enable button and hide throbber
            $button.prop('disabled', false)
            $throbber.addClass('hidden')
          }
        })
      })
    },

    /**
     * Automatically scans page when on preview with auto-scan enabled.
     */
    handlePreviewAutoScan: function (context) {
      once('alfaScanBlockWrapper', '.alfa-scan-block-wrapper', context).forEach(async () => {
        await this.run()

        $('.floating-si').addClass('floating-si-border-wrap')
        $('.alfa-scan-report').show()
      })
      this.handleManualScan(context)
    },

    /**
     * Handles UI logic when the "close" button is clicked.
     */
    handleAlfaScanClose: function (context) {
      once('alfaScanClose', '.alfa-scan-close', context).forEach((element) => {
        element.addEventListener('click', () => {
          $('.floating-si').removeClass('floating-si-border-wrap')
          $('.alfa-scan-report').hide()
          if (drupalSettings.siteimprove_accessibility.scannedNode.enableManualScans == true) {
            $('.alfa-scan-button').show()
          }
        })
      })
    },
  }

  /**
   * Attaches the behavior for Alfa Scan Button.
   */
  Drupal.behaviors.alfaScanButton = {
    attach (context) {
      const scannedNode = drupalSettings.siteimprove_accessibility.scannedNode

      // Automatically run on preview if enabled, otherwise wait for manual
      // trigger.
      if (scannedNode.isPreview && scannedNode.previewAutoScan) {
        Drupal.siteimproveAccessibility.scan.handlePreviewAutoScan(context)
      }
      else {
        Drupal.siteimproveAccessibility.scan.handleManualScan(context)
      }

      // Attach UI handlers.
      Drupal.siteimproveAccessibility.scan.handleAlfaScanClose(context)
    },
  }
})(jQuery, Drupal, once, drupalSettings)
