import { LitElement, css, html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { map } from 'lit/directives/map.js';
import { when } from 'lit/directives/when.js';

@customElement('arx-select')
export class StyledSelect<T> extends LitElement {
  @property() placeholder = '';
  @property() value = '';
  @property({ type: Boolean }) disabled = false;
  @property() name = '';
  @property({ type: Boolean }) required = false;
  @property() label = '';
  @property({ type: Array }) options: T[] = [];
  @property() valueMapper?: (option: T) => string;
  @property() textMapper?: (option: T) => string;

  static override styles = css`
    :host {
      display: inline-block;
      width: 100%;
      margin-bottom: 8px;
    }

    label {
      display: block;
      margin-bottom: 8px;
      font-size: 14px;
      color: var(--color-base-content);
      font-weight: 500;
    }

    select {
      width: 100%;
      padding: 14px 16px;
      font-size: 16px;
      color: var(--color-base-content);
      appearance: none;
      -webkit-appearance: none;
      -moz-appearance: none;
      background-color: var(--color-base-100);
      border: var(--border) solid var(--color-base-300);
      border-radius: var(--radius-field);
      box-shadow: inset calc(var(--depth) * 2px) calc(var(--depth) * 2px)
          calc(var(--depth) * 4px)
          oklch(from var(--color-base-content) l c h / 0.15),
        inset calc(var(--depth) * -2px) calc(var(--depth) * -2px)
          calc(var(--depth) * 4px) oklch(from var(--color-base-100) l c h / 0.7),
        0 0 0 transparent;
      transition: all 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
      cursor: pointer;
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
      background-repeat: no-repeat;
      background-position: right 16px center;
      padding-right: 40px;
    }

    select:hover {
      transform: translateY(-1px);
      background-color: oklch(from var(--color-base-100) calc(l + 0.02) c h);
    }

    select:focus {
      outline: none;
      transform: translateY(1px);
      border-top: var(--border) solid
        oklch(from var(--color-base-300) l c h / 0.8);
      border-left: var(--border) solid
        oklch(from var(--color-base-300) l c h / 0.8);
      border-bottom: var(--border) solid
        oklch(from var(--color-base-200) l c h / 0.8);
      border-right: var(--border) solid
        oklch(from var(--color-base-200) l c h / 0.8);
      box-shadow: inset calc(var(--depth) * 3px) calc(var(--depth) * 3px)
          calc(var(--depth) * 5px)
          oklch(from var(--color-base-content) l c h / 0.2),
        inset calc(var(--depth) * -1px) calc(var(--depth) * -1px)
          calc(var(--depth) * 3px) oklch(from var(--color-base-100) l c h / 0.5),
        0 0 0 transparent;
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
      background-repeat: no-repeat;
      background-position: right 16px center;
      background-color: linear-gradient(
        145deg,
        oklch(from var(--color-base-100) calc(l - 0.03) c h),
        oklch(from var(--color-base-100) l c h)
      );
    }

    select:disabled {
      opacity: 0.7;
      cursor: not-allowed;
      box-shadow: inset calc(var(--depth) * 1px) calc(var(--depth) * 1px)
          calc(var(--depth) * 2px)
          oklch(from var(--color-base-content) l c h / 0.05),
        inset calc(var(--depth) * -1px) calc(var(--depth) * -1px)
          calc(var(--depth) * 2px) oklch(from var(--color-base-100) l c h / 0.4);
      transform: none;
    }

    select option[value=""] {
      color: var(--color-secondary);
    }
  `;

  private _getOptionValue(option: T) {
    if (option === undefined || option === null) return '';
    if (this.valueMapper) return this.valueMapper(option);
    if (typeof option === 'object' && 'value' in (option as object))
      return (option as unknown as { value: string }).value;
    return String(option);
  }

  private _getOptionText(option: T) {
    if (option === undefined || option === null) return '';
    if (this.textMapper) return this.textMapper(option);
    if (typeof option === 'object' && 'label' in (option as object))
      return (option as unknown as { label: string }).label;
    return String(option);
  }

  override render() {
    return html`
      ${when(this.label, () => html`<label for="select-${this.name}">${this.label}</label>`)}
      <select
        id="select-${this.name}"
        .value=${this.value}
        ?disabled=${this.disabled}
        ?required=${this.required}
        name=${this.name}
        @change=${this._handleChange}
        @focus=${this._handleFocus}
        @blur=${this._handleBlur}
        @keydown=${this._handleKeyDown}
        @keyup=${this._handleKeyUp}
      >
        ${when(
          this.placeholder,
          () =>
            html`<option value="" ?selected=${!this.value} disabled>
              ${this.placeholder}
            </option>`,
        )}
        ${map(this.options, (option) => {
          const optionValue = this._getOptionValue(option);
          const optionText = this._getOptionText(option);
          return html`
            <option
              .value=${optionValue}
              ?selected=${optionValue === this.value}
            >
              ${optionText}
            </option>
          `;
        })}
      </select>
    `;
  }

  private _handleKeyDown(e: KeyboardEvent) {
    this.dispatchEvent(
      new CustomEvent('keydown', {
        detail: { key: e.key },
        bubbles: true,
        composed: true,
      }),
    );
  }

  private _handleKeyUp(e: KeyboardEvent) {
    this.dispatchEvent(
      new CustomEvent('keyup', {
        detail: { key: e.key },
        bubbles: true,
        composed: true,
      }),
    );
  }

  private _handleFocus() {
    this.dispatchEvent(
      new CustomEvent('focus', {
        bubbles: true,
        composed: true,
      }),
    );
  }

  private _handleBlur() {
    this.dispatchEvent(
      new CustomEvent('blur', {
        bubbles: true,
        composed: true,
      }),
    );
  }

  private _handleChange(e: Event) {
    const select = e.target as HTMLSelectElement;
    this.value = select.value;
    this.dispatchEvent(
      new CustomEvent('change', {
        detail: { value: this.value },
        bubbles: true,
        composed: true,
      }),
    );
  }
}
