import { html, type TemplateResult } from 'lit'
import { ifDefined } from 'lit/directives/if-defined.js'
import { classMap } from 'lit/directives/class-map.js'
import { Activity } from './Activity.js'
import type { UiDialogClosingReason } from '../md/dialog/internals/Dialog.js'
import state from '../decorators/state.js'
import { IntentResult } from './ActivityManager.js'

import '../md/dialog/ui-dialog.js'
import '../md/button/ui-button.js'

export interface ActionButtonInit {
  /**
   * The label to use for the button.
   */
  label?: string
  /**
   * Whether the button is disabled.
   */
  disabled?: boolean
  /**
   * Optional callback to be called when the button is clicked.
   */
  callback?: (e: MouseEvent) => void
}

/**
 * A special kind of activity that renders content in a modal dialog.
 * When a modal activity is running the underlying activities are paused.
 *
 * A modal activity uses the UiDialogElement to render its content. The dialog
 * is inserted directly into the `<body>` element of the document and rendered
 * in the top layer. Because of that, the modal activity does not support
 * regular rendering logic. It creates its own render root that is used to render
 * the template.
 */
export abstract class ModalActivity extends Activity {
  /**
   * The list of classes to apply to the dialog.
   */
  protected dialogClasses: Record<string, boolean> = {}
  /**
   * Controls dialog visibility without controlling the activity state.
   */
  @state() accessor opened = true

  /**
   * The id of the form used to render the dialog.
   */
  protected formId?: string

  override onResume(): void | Promise<void> {
    this.renderRoot = document.createElement('div')
    this.renderRoot.classList.add('modal-activity-container')
    document.body.appendChild(this.renderRoot)
    super.onResume()
    this.requestUpdate()
  }

  override async finish(): Promise<void> {
    if (this.renderRoot) {
      document.body.removeChild(this.renderRoot)
      this.renderRoot = undefined
    }
    await super.finish()
  }

  protected handleDialogClosed(e: CustomEvent<UiDialogClosingReason>): void {
    const { cancelled, value } = e.detail
    if (cancelled) {
      this.handleNegativeAction(value)
    } else {
      this.handlePositiveAction(value)
    }
  }

  /**
   * Called when the dialog is closed with a negative action.
   * Override this function to handle the negative action.
   * @param value The value passed to the dialog when it is closed.
   */
  protected handleNegativeAction(value: unknown): void {
    this.setResult(IntentResult.RESULT_CANCELED, value)
    this.finish()
  }

  /**
   * Called when the dialog is closed with a positive action.
   * Override this function to handle the positive action.
   * @param value The value passed to the dialog when it is closed.
   */
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  protected handlePositiveAction(value: unknown): void {
    this.finish()
  }

  /**
   * Override this method to handle the form submission.
   * @param e The submit event.
   */
  protected handleSubmit(e: SubmitEvent): void {
    e.preventDefault()
  }

  /**
   * The function called by the rendered when rendering this dialog.
   * This way, the `render()` function is still used as the primary template provider.
   */
  renderDialog(): TemplateResult {
    const classes = {
      activityDialog: true,
      [this.constructor.name.toLowerCase()]: true,
      ...this.dialogClasses,
    }
    return html`<ui-dialog modal .open="${this.opened}" class="${classMap(classes)}" @close="${this.handleDialogClosed}"
      >${this.render()}</ui-dialog
    >`
  }

  /**
   * Used with dialogs that render forms. Override the `handleSubmit` method
   * to handle the form submission.
   * @param content The content to render in the form.
   * @param name Optional form name
   */
  protected renderForm(content: TemplateResult, name?: string): TemplateResult {
    if (!this.formId) {
      this.formId = `form-${this.constructor.name.toLowerCase()}`
    }
    return html`<form
      id="${this.formId}"
      name="${ifDefined(name)}"
      class="dialog-content"
      @submit="${this.handleSubmit}"
    >
      ${content}
    </form>`
  }

  /**
   * Renders the cancel button for the dialog.
   * @param label The label to use for the cancel button.
   */
  protected renderNegativeButton(init: ActionButtonInit = {}): TemplateResult {
    const { label = 'Cancel' } = init
    return html`<ui-button color="text" slot="button" type="button" value="dismiss" @click="${init.callback}"
      >${label}</ui-button
    >`
  }

  /**
   * Renders the submit button for the dialog.
   * If the formId is set, the button will be a submit button.
   * Otherwise, it will be a regular button.
   * @param label The label to use for the submit button.
   */
  protected renderPositiveButton(init: ActionButtonInit = {}): TemplateResult {
    const { label = 'OK', disabled = false } = init
    const { formId } = this
    const type = formId ? 'submit' : 'button'
    // UI buttons have a proper support for form association.
    return html`<ui-button
      color="text"
      slot="button"
      ?disabled="${disabled}"
      type="${type}"
      value="confirm"
      form="${ifDefined(formId)}"
      @click="${init.callback}"
      >${label}</ui-button
    >`
  }

  /**
   * Renders the title for the dialog.
   * @param title The title to render in the dialog.
   */
  protected renderTitle(title: string): TemplateResult {
    return html`<span slot="title">${title}</span>`
  }
}
