import { html, PropertyValues, TemplateResult } from "lit";
import { property, queryAsync, state } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";
import { when } from 'lit/directives/when.js';
import { BeginPressConfig, EndPressConfig } from "../controllers/ActionController.js";
import UiRipple from "../effects/ripple.js";
import { Button } from "./Button.js";
import { ripple } from "../effects/rippleDirective.js";
import '../../define/ui/ui-ripple.js';

/**
 * The type of the icon button rendered.
 */
export enum IconButtonType {
  'filled' = 'filled', 
  'tonal' = 'tonal', 
  'outlined' = 'outlined', 
  'standard' = 'standard',
}

/**
 * @fires active - An event dispatched when the `active` state  has changed. This only happens when the button is `toggle`.
 */
export default class UiIconButton extends Button {
  /**
   * The type of the rendered button according to Material 3 spec.
   * 
   * @default {ButtonType.filled}
   * @attribute
   */
  @property({ type: String, reflect: true, }) type: IconButtonType;

  /**
   * Whether the button can be toggled.
   * A toggle button behaves different as it gets the `aria-pressed` attribute 
   * which changes depending on the pressed state.
   * A toggle button is activated until it is clicked/pressed again.
   * A toggle button dispatched `active` event when the press state change. 
   * The host can check the `active` property to know the state.
   * 
   * @attribute
   */
  @property({ type: Boolean, reflect: true, }) toggle?: boolean;

  /**
   * A flag that determines whether the button is activated (pressed when `toggle` is set).
   * It is `undefined` and makes no effect when `toggle` is not set.
   * 
   * @attribute
   */
  @property({ type: Boolean, reflect: true, }) active?: boolean;

  @queryAsync('ui-ripple') protected ripple!: Promise<UiRipple | null>;

  @state() protected showRipple = false;

  protected readonly getRipple = (): Promise<UiRipple | null> => {
    this.showRipple = true;
    return this.ripple;
  };

  constructor() {
    super();

    this.type = IconButtonType.standard;
  }

  protected override update(changedProperties: PropertyValues<this>): void {
    if (changedProperties.has('active')) {
      this.setAttribute('aria-pressed', String(this.active));
    }
    super.update(changedProperties);
  }

  protected async pressRipple(): Promise<void> {
    const element = await this.getRipple();
    if (element && !element.isPressed) {
      element.beginPress();
    }
  }

  protected async endRipple(): Promise<void> {
    const element = await this.getRipple();
    element?.endPress();
  }

  override beginPress(options: BeginPressConfig): void {
    super.beginPress(options);
    this.pressRipple();
  }

  override endPress(config: EndPressConfig): void {
    super.endPress(config);
    this.endRipple();
    const { cancelled, reason } = config;
    if (cancelled) {
      return;
    }
    if (!reason || reason === 'enter' || reason === 'space') {
      if (this.toggle) {
        this.active = !this.active;
        // note, only dispatch events when user interacting with the UI.
        this.dispatchEvent(new Event('active'));
      }
    }
    if (reason === 'enter' || reason === 'space') {
      if (!this.toggle) {
        this.click();
      }
    }
  }

  protected override render(): TemplateResult {
    const { pressed=false } = this;
    const containerClasses = classMap({
      surface: true,
      pressed,
    });
    return html`
    <div class="${containerClasses}" ${ripple(this.getRipple)}>
      <div class="container"></div>
      <div class="state"></div>
      ${when(this.showRipple, this.renderRipple)}
      <div class="content">
        <slot></slot>
      </div>
    </div>
    `;
  }

  protected renderRipple = (): TemplateResult => {
    const { disabled } = this;
    return html`<ui-ripple class="ripple" ?disabled="${disabled}"></ui-ripple>`;
  };
}
