import {
  Behaviour,
  DeviceUtilities,
  GameObject,
  serializable,
  USDZExporter,
  type NeedleXREventArgs,
} from "@needle-tools/engine";
import { Object3D } from "three";

const dishBaseY = new WeakMap<Object3D, number>();

/**
 * Place each dish model in the same Unity scene as MenuScene, assign the roots to {@link MenuController.dishes},
 * then export a single `MenuScene.glb`. Only one dish is active at a time; the HTML picker cycles entries for AR preview.
 */
export class MenuController extends Behaviour {
  isMobile: boolean = false;
  isDesktop: boolean = false;
  isXR: boolean = false;
  private dishName: string = "";

  @serializable(Object3D)
  dishes: Object3D[] = [];

  @serializable(Object3D)
  webXROrigin?: Object3D;

  /**
   * Local-space vertical bob amplitude (meters). Set to 0 to disable.
   * Peak-to-peak motion is 2× this value; 0.004 ≈ 8 mm total — subtle but visible.
   */
  @serializable()
  dishBobAmplitude = 0.004;

  /** Bob angular speed (radians per second). */
  @serializable()
  dishBobSpeed = 1.0;

  private usdzExporter?: USDZExporter;

  /** True while an immersive-ar session is active — vertical bob is paused. */
  private arSessionBobPaused = false;

  selectedDishIndex: number = 0;

  onEnable(): void {
    const params = new URLSearchParams(window.location.search);
    this.dishName = params.get("dishName") ?? "";

    if (this.webXROrigin) this.usdzExporter = this.webXROrigin.getComponent(USDZExporter) ?? undefined;

    if (this.dishName) {
      let matched = false;
      this.dishes.forEach((dish, index) => {
        if (!dish) return;
        if (dish.name === this.dishName) {
          this.selectedDishIndex = index;
          matched = true;
        }
      });
      this.dishes.forEach((dish) => {
        if (!dish) return;
        const on = matched && dish.name === this.dishName;
        if (!on) {
          this.restoreDishBaseY(dish);
        }
        GameObject.setActive(dish, on);
      });
      if (!matched) {
        this.ensureOnlySelectedDishVisible();
      }
    } else {
      this.ensureOnlySelectedDishVisible();
    }

    this.updateUSDZExporterTarget();

    void this.checkForDeviceType().then(() => {
      if (this.isMobile) {
        console.log("[MenuController] isMobile");
      } else if (this.isDesktop) {
        this.setupDesktopControls();
      } else if (this.isXR) {
        // XR-specific setup if needed
      }
    });

    this.setupMobileControls();
    this.disableDoubleTapZoom();
  }

  onEnterXR(args: NeedleXREventArgs): void {
    if (args.xr.mode === "immersive-ar") {
      this.arSessionBobPaused = true;
      this.snapActiveDishToBaseY();
    }
  }

  onLeaveXR(_args: NeedleXREventArgs): void {
    this.arSessionBobPaused = false;
  }

  update(): void {
    if (this.arSessionBobPaused) return;
    if (this.dishBobAmplitude <= 0) return;

    const valid = this.getValidDishIndices();
    if (valid.length === 0) return;

    const dish = this.dishes[this.selectedDishIndex];
    if (!dish) return;

    let base = dishBaseY.get(dish);
    if (base === undefined) {
      base = dish.position.y;
      dishBaseY.set(dish, base);
    }

    const t = this.context.time.time;
    dish.position.y = base + Math.sin(t * this.dishBobSpeed) * this.dishBobAmplitude;
  }

  async checkForDeviceType(): Promise<void> {
    const xrSupported = await this.isXRDevice();

    if (xrSupported) {
      this.isXR = true;
    } else {
      console.log("DeviceUtilities.isMobileDevice()", DeviceUtilities.isMobileDevice());
      this.isMobile = DeviceUtilities.isMobileDevice();

      if (!this.isMobile) {
        this.isDesktop = DeviceUtilities.isDesktop();
      }
    }
  }

  async isXRDevice(): Promise<boolean> {
    if (navigator.xr) {
      try {
        return await navigator.xr.isSessionSupported("immersive-vr");
      } catch {
        console.log("XR check error!");
        return false;
      }
    }
    return false;
  }

  setupMobileControls(): void {
    if (typeof document !== "undefined" && document.querySelector("#asset-picker")) {
      return;
    }
    this.createMenuMobileControls();
  }

  setupDesktopControls(): void {
    // Optional: mirror mobile controls on desktop
  }

  createMenuMobileControls(): void {
    const menuControlsContainer = document.createElement("div");
    menuControlsContainer.id = "menuControlsZone";
    menuControlsContainer.style.cssText = `
            position: absolute;
            display: flex;
            justify-content: space-between;
            bottom: 10%;
            left: 10%;
            right: 10%;
            height: 150px;
        `;

    document.body.appendChild(menuControlsContainer);

    const previousButton = document.createElement("button");
    previousButton.id = "previousButton";
    previousButton.style.cssText = `
            height: 100px;
            width: 100px;
            background-color: #ffffff;
            color: #111111;
            border: none;
            border-radius: 50px;
            box-shadow: 0 8px 24px rgba(0,0,0,0.25);
            display: flex;
            align-items: center;
            justify-content: center;
            touch-action: manipulation;
        `;
    previousButton.setAttribute("aria-label", "Previous");
    previousButton.innerHTML = `
            <svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M15 18L9 12L15 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
            </svg>
        `;
    previousButton.onclick = this.selectPreviousDish.bind(this);

    const nextButton = document.createElement("button");
    nextButton.id = "nextButton";
    nextButton.style.cssText = `
            height: 100px;
            width: 100px;
            background-color: #ffffff;
            color: #111111;
            border: none;
            border-radius: 50px;
            box-shadow: 0 8px 24px rgba(0,0,0,0.25);
            display: flex;
            align-items: center;
            justify-content: center;
            touch-action: manipulation;
        `;
    nextButton.setAttribute("aria-label", "Next");
    nextButton.innerHTML = `
            <svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M9 6L15 12L9 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
            </svg>
        `;
    nextButton.onclick = this.selectNextDish.bind(this);

    if (this.dishName) {
      previousButton.disabled = true;
      previousButton.style.display = "none";
      nextButton.disabled = true;
      nextButton.style.display = "none";
    }

    menuControlsContainer.appendChild(previousButton);
    menuControlsContainer.appendChild(nextButton);
  }

  private disableDoubleTapZoom(): void {
    let lastTouchEnd = 0;
    const onTouchEnd = (event: TouchEvent): void => {
      const now = Date.now();
      if (now - lastTouchEnd <= 300) {
        event.preventDefault();
      }
      lastTouchEnd = now;
    };
    document.addEventListener("touchend", onTouchEnd, { passive: false });
  }

  private getValidDishIndices(): number[] {
    return this.dishes.map((dish, index) => (dish != null ? index : -1)).filter((index) => index >= 0);
  }

  /** Show exactly one dish: current {@link selectedDishIndex}, or the first valid slot if the index is unset. */
  private ensureOnlySelectedDishVisible(): void {
    const valid = this.getValidDishIndices();
    if (valid.length === 0) return;

    let idx = this.selectedDishIndex;
    if (valid.indexOf(idx) < 0) {
      idx = valid[0];
      this.selectedDishIndex = idx;
    }

    valid.forEach((i) => {
      const active = i === this.selectedDishIndex;
      const d = this.dishes[i];
      if (!active) {
        this.restoreDishBaseY(d);
      }
      GameObject.setActive(this.dishes[i], active);
    });
  }

  private restoreDishBaseY(dish: Object3D | null | undefined): void {
    if (!dish) return;
    const y = dishBaseY.get(dish);
    if (y !== undefined) {
      dish.position.y = y;
    }
  }

  private snapActiveDishToBaseY(): void {
    this.restoreDishBaseY(this.dishes[this.selectedDishIndex]);
  }

  getDishSlotCount(): number {
    return this.getValidDishIndices().length;
  }

  getPickerLabel(): string {
    const valid = this.getValidDishIndices();
    if (valid.length === 0) return "Menu";
    const pos = Math.max(0, valid.indexOf(this.selectedDishIndex));
    const dish = this.dishes[this.selectedDishIndex];
    const label = dish?.name?.trim();
    if (label) {
      return `${label} (${pos + 1}/${valid.length})`;
    }
    return `Dish ${pos + 1} / ${valid.length}`;
  }

  selectPreviousDish(): void {
    const valid = this.getValidDishIndices();
    if (valid.length === 0) return;

    let pos = valid.indexOf(this.selectedDishIndex);
    if (pos < 0) pos = 0;

    this.restoreDishBaseY(this.dishes[valid[pos]]);
    GameObject.setActive(this.dishes[valid[pos]], false);
    pos = (pos - 1 + valid.length) % valid.length;
    this.selectedDishIndex = valid[pos];
    GameObject.setActive(this.dishes[this.selectedDishIndex], true);

    this.updateUSDZExporterTarget();
  }

  selectNextDish(): void {
    const valid = this.getValidDishIndices();
    if (valid.length === 0) return;

    let pos = valid.indexOf(this.selectedDishIndex);
    if (pos < 0) pos = 0;

    this.restoreDishBaseY(this.dishes[valid[pos]]);
    GameObject.setActive(this.dishes[valid[pos]], false);
    pos = (pos + 1) % valid.length;
    this.selectedDishIndex = valid[pos];
    GameObject.setActive(this.dishes[this.selectedDishIndex], true);

    this.updateUSDZExporterTarget();
  }

  private updateUSDZExporterTarget(): void {
    const dish = this.dishes[this.selectedDishIndex];
    if (this.usdzExporter && dish) {
      this.usdzExporter.objectToExport = dish;
    }
  }

  getUrlParameter(name: string): string | null {
    const params = new URLSearchParams(window.location.search);
    return params.get(name);
  }
}
