import "@home-assistant/webawesome/dist/components/drawer/drawer";
import { css, html, LitElement, type PropertyValues } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { SwipeGestureRecognizer } from "../common/util/swipe-gesture-recognizer";
import { ScrollableFadeMixin } from "../mixins/scrollable-fade-mixin";
import { haStyleScrollbar } from "../resources/styles";

export const BOTTOM_SHEET_ANIMATION_DURATION_MS = 300;

@customElement("ha-bottom-sheet")
export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
  @property({ type: Boolean }) public open = false;

  @property({ type: Boolean, reflect: true, attribute: "flexcontent" })
  public flexContent = false;

  @state() private _drawerOpen = false;

  @query("#drawer") private _drawer!: HTMLElement;

  @query("#body") private _bodyElement!: HTMLDivElement;

  protected get scrollableElement(): HTMLElement | null {
    return this._bodyElement;
  }

  private _gestureRecognizer = new SwipeGestureRecognizer();

  private _isDragging = false;

  private _handleAfterHide(afterHideEvent: Event) {
    afterHideEvent.stopPropagation();
    this.open = false;
    const ev = new Event("closed", {
      bubbles: true,
      composed: true,
    });
    this.dispatchEvent(ev);
  }

  protected updated(changedProperties: PropertyValues): void {
    super.updated(changedProperties);
    if (changedProperties.has("open")) {
      this._drawerOpen = this.open;
    }
  }

  render() {
    return html`
      <wa-drawer
        id="drawer"
        placement="bottom"
        .open=${this._drawerOpen}
        @wa-after-hide=${this._handleAfterHide}
        without-header
        @touchstart=${this._handleTouchStart}
      >
        <slot name="header"></slot>
        <div class="content-wrapper">
          <div id="body" class="body ha-scrollbar">
            <slot></slot>
          </div>
          ${this.renderScrollableFades()}
        </div>
        <slot name="footer"></slot>
      </wa-drawer>
    `;
  }

  private _handleTouchStart = (ev: TouchEvent) => {
    // Check if any element inside drawer in the composed path has scrollTop > 0
    for (const path of ev.composedPath()) {
      const el = path as HTMLElement;
      if (el === this._drawer) {
        break;
      }
      if (el.scrollTop > 0) {
        return;
      }
    }

    this._startResizing(ev.touches[0].clientY);
  };

  private _startResizing(clientY: number) {
    // register event listeners for drag handling
    document.addEventListener("touchmove", this._handleTouchMove, {
      passive: false,
    });
    document.addEventListener("touchend", this._handleTouchEnd);
    document.addEventListener("touchcancel", this._handleTouchEnd);

    this._gestureRecognizer.start(clientY);
  }

  private _handleTouchMove = (ev: TouchEvent) => {
    const currentY = ev.touches[0].clientY;
    const delta = this._gestureRecognizer.move(currentY);

    if (delta < 0) {
      ev.preventDefault();
      this._isDragging = true;
      requestAnimationFrame(() => {
        if (this._isDragging) {
          this.style.setProperty(
            "--dialog-transform",
            `translateY(${delta * -1}px)`
          );
        }
      });
    }
  };

  private _animateSnapBack() {
    // Add transition for smooth animation
    this.style.setProperty(
      "--dialog-transition",
      `transform ${BOTTOM_SHEET_ANIMATION_DURATION_MS}ms ease-out`
    );

    // Reset transform to snap back
    this.style.removeProperty("--dialog-transform");

    // Remove transition after animation completes
    setTimeout(() => {
      this.style.removeProperty("--dialog-transition");
    }, BOTTOM_SHEET_ANIMATION_DURATION_MS);
  }

  private _handleTouchEnd = () => {
    this._unregisterResizeHandlers();

    this._isDragging = false;

    const result = this._gestureRecognizer.end();

    // If velocity exceeds threshold, use velocity direction to determine action
    if (result.isSwipe) {
      if (result.isDownwardSwipe) {
        // Downward swipe - close the bottom sheet
        this._drawerOpen = false;
      } else {
        // Upward swipe - keep open and animate back
        this._animateSnapBack();
      }
      return;
    }

    // If velocity is below threshold, use position-based logic
    // Get the drawer height to calculate 50% threshold
    const drawerBody = this._drawer.shadowRoot?.querySelector(
      '[part="body"]'
    ) as HTMLElement;
    const drawerHeight = drawerBody?.offsetHeight || 0;

    // delta is negative when dragging down
    // Close if dragged down past 50% of the drawer height
    if (
      drawerHeight > 0 &&
      result.delta < 0 &&
      Math.abs(result.delta) > drawerHeight * 0.5
    ) {
      this._drawerOpen = false;
    } else {
      this._animateSnapBack();
    }
  };

  private _unregisterResizeHandlers = () => {
    document.removeEventListener("touchmove", this._handleTouchMove);
    document.removeEventListener("touchend", this._handleTouchEnd);
    document.removeEventListener("touchcancel", this._handleTouchEnd);
  };

  disconnectedCallback() {
    super.disconnectedCallback();
    this._unregisterResizeHandlers();
    this._isDragging = false;
  }

  static get styles() {
    return [
      ...super.styles,
      haStyleScrollbar,
      css`
        wa-drawer {
          --wa-color-surface-raised: transparent;
          --spacing: 0;
          --size: var(--ha-bottom-sheet-height, auto);
          --show-duration: ${BOTTOM_SHEET_ANIMATION_DURATION_MS}ms;
          --hide-duration: ${BOTTOM_SHEET_ANIMATION_DURATION_MS}ms;
        }
        wa-drawer::part(dialog) {
          max-height: var(--ha-bottom-sheet-max-height, 90vh);
          align-items: center;
          transform: var(--dialog-transform);
          transition: var(--dialog-transition);
        }
        wa-drawer::part(body) {
          max-width: var(--ha-bottom-sheet-max-width);
          width: 100%;
          border-top-left-radius: var(
            --ha-bottom-sheet-border-radius,
            var(--ha-dialog-border-radius, var(--ha-border-radius-2xl))
          );
          border-top-right-radius: var(
            --ha-bottom-sheet-border-radius,
            var(--ha-dialog-border-radius, var(--ha-border-radius-2xl))
          );
          background-color: var(
            --ha-bottom-sheet-surface-background,
            var(--ha-dialog-surface-background, var(--mdc-theme-surface, #fff)),
          );
          padding: var(
            --ha-bottom-sheet-padding,
            0 var(--safe-area-inset-right) var(--safe-area-inset-bottom)
              var(--safe-area-inset-left)
          );
        }
        :host([flexcontent]) wa-drawer::part(body) {
          display: flex;
          flex-direction: column;
        }
        .content-wrapper {
          position: relative;
          flex: 1;
          display: flex;
          flex-direction: column;
          min-height: 0;
        }
        :host([flexcontent]) .body {
          flex: 1;
          max-width: 100%;
          display: flex;
          flex-direction: column;
          padding: var(
            --ha-bottom-sheet-padding,
            0 var(--safe-area-inset-right) var(--safe-area-inset-bottom)
              var(--safe-area-inset-left)
          );
        }
        slot[name="footer"] {
          display: block;
          padding: 0;
        }
        ::slotted([slot="footer"]) {
          display: flex;
          padding: var(--ha-space-3) var(--ha-space-4) var(--ha-space-4)
            var(--ha-space-4);
          gap: var(--ha-space-3);
          justify-content: flex-end;
          align-items: center;
          width: 100%;
          box-sizing: border-box;
        }
        :host([flexcontent]) slot[name="footer"] {
          flex-shrink: 0;
        }
      `,
    ];
  }
}

declare global {
  interface HTMLElementTagNameMap {
    "ha-bottom-sheet": HaBottomSheet;
  }
}
