import type { IPktComboboxOption } from './combobox-types'
import { findOptionByValue, isMaxSelectionReached } from 'shared-utils/combobox/option-utils'
import { getSingleValueForInput } from 'shared-utils/combobox/input-utils'
import { selectionMutators } from './combobox-utils'
import { ComboboxBase } from './combobox-base'

/**
 * Value management layer for PktCombobox.
 * Handles selection, deselection, user-added values, and input reset.
 */
export class ComboboxValue extends ComboboxBase {
  public toggleValue(value: string | null): void {
    if (this.disabled) return

    this.touched = true
    this._userInfoMessage = ''
    this._addValueText = null

    const valueFromOptions: string | null = findOptionByValue(this.options, value)?.value || null
    const isSelected: boolean = this._value.includes(value || valueFromOptions || '')
    const isInOption: boolean = !!valueFromOptions
    const isDisabled: boolean = this._options.find((o) => o.value === value)?.disabled || false
    const isEmpty: boolean = !value?.trim()
    const isSingle: boolean = !this.multiple
    const isMultiple: boolean = this.multiple
    const isMaxItemsReached: boolean = isMaxSelectionReached(this._value.length, this.maxlength)

    let shouldOptionsBeOpen: boolean = false
    let shouldResetInput: boolean = true
    let userInfoMessage: string | null = ''
    let searchValue: string | null = ''

    if (isDisabled) return

    // Not in option list and allowUserInput is true
    if (!isInOption && this.allowUserInput && !isEmpty) {
      this.addNewUserValue(value)
      userInfoMessage = 'Ny verdi lagt til'
      shouldOptionsBeOpen = isMultiple
    }

    // Not in option list and allowUserInput is false
    else if (!isInOption && !this.allowUserInput) {
      if (isSingle && this._value[0]) {
        this.removeValue(this._value[0])
      }
      shouldResetInput = false
      shouldOptionsBeOpen = true
      userInfoMessage = 'Ingen treff i søket'
    }

    // Value is already selected — deselect it
    else if (isSelected) {
      this.removeValue(valueFromOptions)
      shouldOptionsBeOpen = true
      // For single+typeahead: clear the input immediately so tab-out doesn't re-select
      if (
        isSingle &&
        this._hasTextInput &&
        this.inputRef.value &&
        this.inputRef.value.type !== 'hidden'
      ) {
        this.inputRef.value.value = ''
      }
    }

    // Empty value in single-select mode — clear selection
    else if (isEmpty && isSingle) {
      this.removeAllSelected()
      shouldOptionsBeOpen = true
    }

    // Single-select — replace current selection
    else if (isSingle) {
      this._value[0] && this.removeSelected(this._value[0])
      this.setSelected(valueFromOptions)
      shouldOptionsBeOpen = false
    }

    // Multi-select with room for more selections
    else if (isMultiple && !isMaxItemsReached) {
      this.setSelected(valueFromOptions)
      shouldOptionsBeOpen = true
    }

    // Multi-select with max selections reached
    else if (isMultiple && isMaxItemsReached) {
      userInfoMessage = 'Maks antall valg nådd'
      shouldResetInput = false
      searchValue = value
    }

    // No matching condition — fallback
    else {
      isSingle && this.removeAllSelected()
      userInfoMessage = 'Ingen gyldig verdi valgt'
      shouldResetInput = false
      shouldOptionsBeOpen = true
      searchValue = value
    }

    this._isOptionsOpen = shouldOptionsBeOpen
    if (!shouldOptionsBeOpen) {
      if (isSingle && this._hasTextInput) {
        // Suppress the next handleFocus from reopening the dropdown,
        // then move focus back to the text input so screen readers
        // announce the selected value instead of the browser window.
        this._suppressNextOpen = true
      }
      window.setTimeout(() => {
        this.focusTrigger()
      }, 0)
    }
    this._userInfoMessage = userInfoMessage
    this._search = searchValue || ''
    this.resetComboboxInput(shouldResetInput)
    isMultiple && this.updateMaxReached()
  }

  protected setSelected(value: string | null): void {
    if (this._value.includes(value as string)) return

    if (this.multiple && isMaxSelectionReached(this._value.length, this.maxlength)) {
      this._userInfoMessage = 'Maks antall valg nådd'
      return
    }

    !this.multiple && this.removeAllSelected()

    this._value = value ? [...this._value, value] : this._value
    selectionMutators.markOptionSelected(this._options, value)

    this.resetComboboxInput(true)
  }

  protected removeSelected(value: string | null): void {
    if (!value) return
    this._value = this._value.filter((v) => v !== value)
    const _opt = findOptionByValue(this.options, value)
    if (_opt) {
      selectionMutators.markOptionDeselected(this.options, value)
      if (_opt.userAdded) {
        this._options = [...this._options.filter((o) => o.value !== value)]
        this.options = [...this.options.filter((o) => o.value !== value)]
      } else if (!this._options.some((o) => o.value === value)) {
        // Re-add only if option was filtered out (e.g. by typeahead)
        this._options = [...this._options, _opt]
      }
    }
  }

  protected addAllOptions(): void {
    if (!this.multiple) return
    if (this.maxlength && this._options.length > this.maxlength) {
      this._userInfoMessage = 'For mange valgt'
      return
    }

    this._value = this._options.map((option) => option.value)
    selectionMutators.markAllSelected(this._options)
    this.requestUpdate()
  }

  protected removeAllSelected(): void {
    this._value = []
    selectionMutators.markAllDeselected(this._options)
    this._options = selectionMutators.removeUserAddedOptions(this._options)
    this.requestUpdate()
  }

  protected addValue(): void {
    const input = this.inputRef.value?.value.trim() || ''
    this._search = input

    // If the typed value is already selected, don't toggle (which would deselect).
    // Just reset the input and keep the "Verdien er allerede valgt" message.
    if (input && this._value.includes(input)) {
      this.resetComboboxInput(true)
      this._userInfoMessage = 'Verdien er allerede valgt'
      return
    }

    this.toggleValue(input)
  }

  protected removeValue(value: string | null): void {
    this._value = this.multiple ? this._value.filter((v) => v !== value) : []
    this.removeSelected(value)
  }

  protected addNewUserValue(value: string | null): void {
    if (!value || value.trim() === '') return

    if (!this.multiple) {
      this._value[0] && this.removeSelected(this._value[0])
      this._value = [value]
    } else if (!findOptionByValue(this.options, value)) {
      if (isMaxSelectionReached(this._value.length, this.maxlength)) return
      this._value = [...this._value, value]
    }

    const newOption: IPktComboboxOption = { value, label: value, userAdded: true, selected: true }

    this.options = [newOption, ...this.options]
    this._options = [newOption, ...this._options]
    this.resetComboboxInput(true)

    if (!this.multiple) {
      this._isOptionsOpen = false
      if (this._hasTextInput) {
        this._suppressNextOpen = true
      }
      window.setTimeout(() => {
        this.focusTrigger()
      }, 0)
    }

    this.requestUpdate()
  }

  protected resetComboboxInput(shouldResetInput: boolean = true): void {
    this._addValueText = null
    if (this.inputRef.value && this.inputRef.value.type !== 'hidden' && shouldResetInput) {
      this._search = ''
      if (!this.multiple) {
        // Single+typeahead: show the selected value's display text in the input
        this.inputRef.value.value = this._value[0]
          ? getSingleValueForInput(this._value[0], this.options, this.displayValueAs)
          : ''
        this._userInfoMessage = ''
      } else {
        this.inputRef.value.value = ''
      }
    }
    this._options = [...this.options]
  }

  protected removeLastValue(e: Event): void {
    if (this._value.length === 0) return

    e.preventDefault()

    const val = this._value[this._value.length - 1]
    val && this.removeSelected(val)

    this.updateMaxReached()
  }
}
