import {
  customElement,
  type TemplateResult,
  property,
  html,
  css,
  cssManager,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
import { DeesInputBase } from '../dees-input-base/dees-input-base.js';
import { demoFunc } from './dees-input-toggle.demo.js';
import { cssGeistFontFamily } from '../../00fonts.js';
import { themeDefaultStyles } from '../../00theme.js';

declare global {
  interface HTMLElementTagNameMap {
    'dees-input-toggle': DeesInputToggle;
  }
}

@customElement('dees-input-toggle')
export class DeesInputToggle extends DeesInputBase<DeesInputToggle> {
  // STATIC
  public static demo = demoFunc;
  public static demoGroup = 'Input';

  // INSTANCE

  @property({ type: Boolean, reflect: true })
  accessor value: boolean = false;

  /**
   * Knob position tracking (0 = off, maxTravel = on)
   */
  private currentX = 0;

  /**
   * Drag state
   */
  private isDragging = false;
  private hasDragged = false;
  private startX = 0;

  // Toggle dimensions
  private readonly trackWidth = 36;
  private readonly trackHeight = 20;
  private readonly knobSize = 14;
  private readonly padding = 2;
  private readonly maxTravel = 16; // trackWidth - knobSize - (padding * 2) - border

  constructor() {
    super();
    this.labelPosition = 'right'; // Toggle defaults to label on the right
  }

  public static styles = [
    themeDefaultStyles,
    ...DeesInputBase.baseStyles,
    cssManager.defaultStyles,
    css`
      * {
        box-sizing: border-box;
      }

      :host {
        position: relative;
        cursor: default;
        font-family: ${cssGeistFontFamily};
      }

      .maincontainer {
        display: inline-flex;
        align-items: flex-start;
        gap: 8px;
        cursor: pointer;
        user-select: none;
        transition: all 0.15s ease;
      }

      .toggle-track {
        position: relative;
        flex-shrink: 0;
        height: 20px;
        width: 36px;
        border-radius: 10px;
        background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 3.9%)')};
        border: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
        overflow: hidden;
        transition: all 0.15s ease;
        margin-top: 1px;
      }

      .maincontainer:hover .toggle-track {
        border-color: ${cssManager.bdTheme('hsl(0 0% 79.8%)', 'hsl(0 0% 20.9%)')};
      }

      :host([value]) .toggle-track {
        background: ${cssManager.bdTheme('hsl(222.2 47.4% 51.2%)', 'hsl(217.2 91.2% 59.8%)')};
        border-color: ${cssManager.bdTheme('hsl(222.2 47.4% 51.2%)', 'hsl(217.2 91.2% 59.8%)')};
      }

      .toggle-track:focus-visible {
        outline: none;
        box-shadow: 0 0 0 3px ${cssManager.bdTheme('hsl(222.2 47.4% 51.2% / 0.1)', 'hsl(217.2 91.2% 59.8% / 0.1)')};
      }

      .toggle-knob {
        position: absolute;
        top: 2px;
        width: 14px;
        height: 14px;
        border-radius: 7px;
        background: ${cssManager.bdTheme('hsl(0 0% 63.9%)', 'hsl(0 0% 45.1%)')};
        transition: left 0.15s ease, background 0.15s ease;
        touch-action: none;
      }

      .toggle-knob.dragging {
        transition: background 0.15s ease;
      }

      :host([value]) .toggle-knob {
        background: white;
      }

      /* Disabled state */
      .maincontainer.disabled {
        cursor: not-allowed;
        opacity: 0.5;
      }

      .toggle-track.disabled {
        background: ${cssManager.bdTheme('hsl(0 0% 95.1%)', 'hsl(0 0% 14.9%)')};
        border-color: ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
      }

      /* Required state (locked on) */
      :host([required][value]) .toggle-track {
        background: ${cssManager.bdTheme('hsl(222.2 47.4% 61.2%)', 'hsl(217.2 91.2% 49.8%)')};
        border-color: ${cssManager.bdTheme('hsl(222.2 47.4% 61.2%)', 'hsl(217.2 91.2% 49.8%)')};
        cursor: not-allowed;
      }

      :host([required][value]) .toggle-knob {
        background: ${cssManager.bdTheme('hsl(0 0% 85%)', 'hsl(0 0% 70%)')};
      }

      /* Label */
      .label-container {
        display: flex;
        flex-direction: column;
        gap: 2px;
        flex: 1;
      }

      .toggle-label {
        font-size: 14px;
        font-weight: 500;
        line-height: 20px;
        color: ${cssManager.bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 90%)')};
        transition: color 0.15s ease;
        letter-spacing: -0.01em;
      }

      .maincontainer:hover .toggle-label {
        color: ${cssManager.bdTheme('hsl(0 0% 9%)', 'hsl(0 0% 95%)')};
      }

      .maincontainer.disabled:hover .toggle-label {
        color: ${cssManager.bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 90%)')};
      }

      /* Description */
      .description-text {
        font-size: 12px;
        color: ${cssManager.bdTheme('hsl(0 0% 45.1%)', 'hsl(0 0% 63.9%)')};
        line-height: 1.5;
      }
    `,
  ];

  public render(): TemplateResult {
    return html`
      <div class="input-wrapper">
        <div class="maincontainer ${this.disabled ? 'disabled' : ''}" @click="${this.handleClick}">
          <div
            class="toggle-track ${this.disabled ? 'disabled' : ''}"
            tabindex="${this.disabled ? '-1' : '0'}"
            @keydown="${this.handleKeydown}"
          >
            <div
              class="toggle-knob"
              style="left: ${this.padding + this.currentX}px;"
              @pointerdown="${this.onPointerDown}"
              @pointermove="${this.onPointerMove}"
              @pointerup="${this.onPointerUp}"
              @pointercancel="${this.onPointerUp}"
            ></div>
          </div>
          <div class="label-container">
            ${this.label ? html`<div class="toggle-label">${this.label}</div>` : ''}
            ${this.description ? html`<div class="description-text">${this.description}</div>` : ''}
          </div>
        </div>
      </div>
    `;
  }

  public async firstUpdated(_changedProperties: Map<PropertyKey, unknown>) {
    await super.firstUpdated(_changedProperties);
    // Initialize knob position based on initial value
    if (this.required && !this.value) {
      this.value = true;
    }
    this.currentX = this.value ? this.maxTravel : 0;
    this.requestUpdate();
  }

  /**
   * Click handler - toggles the value
   */
  private async handleClick(event: MouseEvent) {
    if (this.isDragging || this.hasDragged) {
      event.stopPropagation();
      event.preventDefault();
      return;
    }

    if (this.disabled) {
      return;
    }

    if (this.required) {
      // Bounce animation for required toggles
      this.currentX = this.maxTravel;
      this.requestUpdate();
      await new Promise((r) => setTimeout(r, 10));
      this.currentX = this.maxTravel - 3;
      this.requestUpdate();
      await domtools.plugins.smartdelay.delayFor(150);
      this.currentX = this.maxTravel;
      this.requestUpdate();
      return;
    }

    event.stopPropagation();
    event.preventDefault();

    this.value = !this.value;
    this.currentX = this.value ? this.maxTravel : 0;
    this.requestUpdate();

    this.dispatchEvent(
      new CustomEvent('newValue', {
        detail: this.value,
        bubbles: true,
      })
    );
    this.changeSubject.next(this);

    domtools.plugins.smartdelay.delayFor(0).then(() => {
      this.hasDragged = false;
    });
  }

  /**
   * Pointer down - start dragging
   */
  private onPointerDown(event: PointerEvent) {
    if (this.required || this.disabled) {
      return;
    }

    this.isDragging = true;
    this.startX = event.clientX - this.currentX;
    (event.target as HTMLElement).setPointerCapture(event.pointerId);
  }

  /**
   * Pointer move - track drag position
   */
  private onPointerMove(event: PointerEvent) {
    if (!this.isDragging) return;
    const newX = event.clientX - this.startX;
    this.hasDragged = true;

    const toggleKnob = this.shadowRoot?.querySelector('.toggle-knob') as HTMLDivElement;
    if (toggleKnob) {
      toggleKnob.classList.add('dragging');
    }

    this.currentX = Math.max(0, Math.min(newX, this.maxTravel));
    this.requestUpdate();
  }

  /**
   * Pointer up - complete drag and snap to nearest side
   */
  private onPointerUp(event: PointerEvent) {
    if (!this.isDragging) return;
    (event.target as HTMLElement).releasePointerCapture(event.pointerId);
    this.isDragging = false;

    if (!this.hasDragged) {
      return;
    }

    const toggleKnob = this.shadowRoot?.querySelector('.toggle-knob') as HTMLDivElement;
    if (toggleKnob) {
      toggleKnob.classList.remove('dragging');
    }

    // Snap to nearest side based on midpoint
    const midpoint = this.maxTravel / 2;
    this.value = this.currentX > midpoint;
    this.currentX = this.value ? this.maxTravel : 0;
    this.requestUpdate();

    this.dispatchEvent(
      new CustomEvent('newValue', {
        detail: this.value,
        bubbles: true,
      })
    );
    this.changeSubject.next(this);

    domtools.plugins.smartdelay.delayFor(0).then(() => {
      this.hasDragged = false;
    });
  }

  /**
   * Sync knob position when value is changed externally
   */
  updated(changedProperties: Map<string, any>): void {
    super.updated(changedProperties);
    if (
      changedProperties.has('value') &&
      !this.isDragging &&
      !this.hasDragged
    ) {
      this.currentX = this.value ? this.maxTravel : 0;
      this.requestUpdate();
    }
  }

  /**
   * Keyboard support
   */
  private handleKeydown(event: KeyboardEvent) {
    if (event.key === ' ' || event.key === 'Enter') {
      event.preventDefault();
      this.handleClick(event as unknown as MouseEvent);
    }
  }

  // DeesInputBase interface implementation
  public getValue(): boolean {
    return this.value;
  }

  public setValue(valueArg: boolean): void {
    this.value = valueArg;
  }

  public focus(): void {
    const track = this.shadowRoot?.querySelector('.toggle-track');
    if (track) {
      (track as HTMLElement).focus();
    }
  }
}
