import {
  ControllerPath,
  type Events,
  type FormDefinition,
  type Page,
  type Section
} from '@defra/forms-model'
import Boom from '@hapi/boom'
import { type Lifecycle, type RouteOptions, type Server } from '@hapi/hapi'

import { type ComponentCollection } from '../components/ComponentCollection.js'
import { type FormComponent } from '../components/FormComponent.js'
import {
  getSaveAndExitHelpers,
  getStartPath,
  normalisePath
} from '../helpers.js'
import { type FormModel } from '../models/index.js'
import { type ExecutableCondition } from '../models/types.js'
import {
  type FormContext,
  type PageViewModelBase
} from '../types.js'
import {
  type FormRequest,
  type FormRequestPayload,
  type FormRequestPayloadRefs,
  type FormRequestRefs,
  type FormResponseToolkit
} from '../../../routes/types.js'

export class PageController {
  /**
   * The base class for all page controllers. Page controllers are responsible for generating the get and post route handlers when a user navigates to `/{id}/{path*}`.
   */
  def: FormDefinition
  name?: string
  model: FormModel
  pageDef: Page
  id?: string
  title: string
  section?: Section
  condition?: ExecutableCondition
  events?: Events
  collection?: ComponentCollection
  viewName = 'index'
  allowSaveAndExit = false

  constructor(model: FormModel, pageDef: Page) {
    const { def } = model

    this.def = def
    this.name = def.name
    this.model = model
    this.pageDef = pageDef
    this.id = pageDef.id
    this.title = pageDef.title
    this.events = pageDef.events

    // Resolve section
    if (pageDef.section) {
      this.section = model.getSection(pageDef.section)
    }

    // Resolve condition
    if (pageDef.condition) {
      this.condition = model.conditions[pageDef.condition]
    }

    // Override view name
    if (pageDef.view) {
      this.viewName = pageDef.view
    }
  }

  get path() {
    return this.pageDef.path
  }

  get href() {
    const { path } = this
    return this.getHref(`/${normalisePath(path)}`)
  }

  get keys() {
    return this.collection?.keys ?? []
  }

  /**
   * {@link https://hapi.dev/api/?v=20.1.2#route-options}
   */
  get getRouteOptions(): RouteOptions<FormRequestRefs> {
    return {}
  }

  /**
   * {@link https://hapi.dev/api/?v=20.1.2#route-options}
   */
  get postRouteOptions(): RouteOptions<FormRequestPayloadRefs> {
    return {}
  }

  get viewModel(): PageViewModelBase {
    const { name, section, title } = this

    const showTitle = true
    const pageTitle = title
    const sectionTitle = section?.hideTitle !== true ? section?.title : ''

    return {
      name,
      page: this,
      pageTitle,
      sectionTitle,
      showTitle,
      isStartPage: false,
      serviceUrl: this.getHref('/'),
      feedbackLink: this.feedbackLink,
      phaseTag: this.phaseTag
    }
  }

  get feedbackLink() {
    return this.def.options?.disableUserFeedback
      ? undefined
      : `/form/feedback?formId=${this.model.formId}`
  }

  get phaseTag() {
    const { def } = this
    return def.phaseBanner?.phase
  }

  getHref(path: string): string {
    const basePath = this.model.basePath

    if (path === '/') {
      return `/${basePath}`
    }

    // if ever the path is not prefixed with a slash, add it
    const relativeTargetPath = path.startsWith('/') ? path.substring(1) : path
    let finalPath = `/${basePath}`
    if (relativeTargetPath) {
      finalPath += `/${relativeTargetPath}`
    }
    finalPath = finalPath.replace(/\/{2,}/g, '/')

    return finalPath
  }

  getStartPath() {
    return getStartPath(this.model)
  }

  getSummaryPath() {
    return ControllerPath.Summary.valueOf()
  }

  getStatusPath() {
    return ControllerPath.Status.valueOf()
  }

  makeGetRouteHandler(): (
    request: FormRequest,
    context: FormContext,
    h: FormResponseToolkit
  ) => ReturnType<Lifecycle.Method<FormRequestRefs>> {
    return (request, context, h) => {
      const { viewModel, viewName } = this
      return h.view(viewName, viewModel)
    }
  }

  makePostRouteHandler(): (
    request: FormRequestPayload,
    context: FormContext,
    h: FormResponseToolkit
  ) => ReturnType<Lifecycle.Method<FormRequestPayloadRefs>> {
    throw Boom.badRequest('Unsupported POST route handler for this page')
  }

  /**
   * Get supplementary state keys for clearing component state.
   *
   * This method returns page controller-level state keys only. The core component's
   * state key (the component's name) is managed separately by the framework and should
   * NOT be included in the returned array.
   *
   * Returns an empty array by default. Override in subclasses to provide
   * page-specific supplementary state keys (e.g., upload state, cached data).
   * @param _component - The component to get supplementary state keys for (optional)
   * @returns Array of supplementary state keys to clear (excluding the component name itself)
   */
  getStateKeys(_component?: FormComponent): string[] {
    return []
  }

  shouldShowSaveAndExit(server: Server): boolean {
    return getSaveAndExitHelpers(server) !== undefined && this.allowSaveAndExit
  }
}
