import {
  ComponentType,
  hasFormComponents,
  isFormType,
  type DeclarationFieldComponent,
  type Item
} from '@defra/forms-model'
import joi, {
  type ArraySchema,
  type BooleanSchema,
  type StringSchema
} from 'joi'

import {
  FormComponent,
  isFormValue
} from './FormComponent.js'
import { messageTemplate } from '../pageControllers/validationOptions.js'
import {
  type ErrorMessageTemplateList,
  type FormPayload,
  type FormState,
  type FormStateValue,
  type FormSubmissionError,
  type FormSubmissionState,
  type FormValue
} from '../types.js'

export class DeclarationField extends FormComponent {
  private readonly DEFAULT_DECLARATION_LABEL = 'I understand and agree'

  declare options: DeclarationFieldComponent['options']

  declare declarationConfirmationLabel: string

  declare formSchema: ArraySchema<StringSchema[]>
  declare stateSchema: BooleanSchema
  declare content: string
  headerStartLevel: number

  constructor(
    def: DeclarationFieldComponent,
    props: ConstructorParameters<typeof FormComponent>[1]
  ) {
    super(def, props)

    const { options, content } = def

    let checkboxSchema = joi.string().valid('true')

    if (options.required !== false) {
      checkboxSchema = checkboxSchema.required()
    }

    const formSchema = joi
      .array()
      .items(checkboxSchema, joi.string().valid('unchecked').strip())
      .label(this.label)
      .single()
      .messages({
        'any.required': messageTemplate.declarationRequired as string,
        'any.unknown': messageTemplate.declarationRequired as string,
        'array.includesRequiredUnknowns':
          messageTemplate.declarationRequired as string
      }) as ArraySchema<StringSchema[]>

    this.formSchema = formSchema
    this.stateSchema = joi.boolean().cast('string').label(this.label).required()

    this.options = options
    this.content = content
    this.declarationConfirmationLabel =
      options.declarationConfirmationLabel ?? this.DEFAULT_DECLARATION_LABEL
    const formComponents = hasFormComponents(props.page?.pageDef)
      ? props.page.pageDef.components
      : []
    const numOfQuestionsOnPage = formComponents.filter((q) =>
      isFormType(q.type)
    ).length
    const hasGuidance = formComponents.some(
      (comp, idx) => comp.type === ComponentType.Markdown && idx === 0
    )
    this.headerStartLevel = numOfQuestionsOnPage < 2 && !hasGuidance ? 2 : 3
  }

  getFormValueFromState(state: FormSubmissionState) {
    const { name } = this
    return state[name] === true ? 'true' : 'false'
  }

  getFormDataFromState(state: FormSubmissionState): FormPayload {
    const { name } = this
    return { [name]: state[name] === true ? 'true' : 'false' }
  }

  getStateFromValidForm(payload: FormPayload): FormState {
    const { name } = this
    const payloadValue = payload[name]
    const value =
      this.isValue(payloadValue) &&
      payloadValue.length > 0 &&
      payloadValue.every((v) => {
        return v === 'true'
      })

    return { [name]: value }
  }

  getContextValueFromFormValue(value: FormValue | FormPayload): boolean {
    return value === 'true'
  }

  getFormValue(value?: FormStateValue | FormState) {
    return this.isValue(value) ? value : undefined
  }

  getDisplayStringFromFormValue(value: FormValue | FormPayload): string {
    return value === 'true' ? this.declarationConfirmationLabel : 'Not provided'
  }

  getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {
    const defaultDeclarationConfirmationLabel =
      'I confirm that I understand and accept this declaration'
    const {
      hint,
      content,
      declarationConfirmationLabel = defaultDeclarationConfirmationLabel
    } = this

    const viewModel = super.getViewModel(payload, errors)
    let { fieldset, label } = viewModel

    fieldset ??= {
      legend: {
        text: label.text,
        classes: 'govuk-fieldset__legend--m'
      }
    }

    const payloadValue = payload[this.name]
    const isChecked =
      payloadValue === 'true' ||
      payloadValue === true ||
      (Array.isArray(payloadValue) && payloadValue.some((x) => x === 'true'))

    return {
      ...viewModel,
      hint: hint ? { text: hint } : undefined,
      fieldset,
      content,
      items: [
        {
          text: declarationConfirmationLabel,
          value: 'true',
          checked: isChecked
        }
      ],
      headerStartLevel: this.headerStartLevel
    }
  }

  isValue(value?: FormStateValue | FormState): value is Item['value'][] {
    if (!Array.isArray(value)) {
      return false
    }

    // Skip checks when empty
    if (!value.length) {
      return true
    }

    return value.every(isFormValue)
  }

  /**
   * For error preview page that shows all possible errors on a component
   */
  getAllPossibleErrors(): ErrorMessageTemplateList {
    return DeclarationField.getAllPossibleErrors()
  }

  /**
   * Static version of getAllPossibleErrors that doesn't require a component instance.
   */
  static getAllPossibleErrors(): ErrorMessageTemplateList {
    return {
      baseErrors: [
        { type: 'required', template: messageTemplate.declarationRequired }
      ],
      advancedSettingsErrors: []
    }
  }

  static isBool(value?: FormStateValue | FormState): value is boolean {
    return isFormValue(value) && typeof value === 'boolean'
  }
}
