import { Injectable, ElementRef } from '@angular/core';
import * as THREE from 'three';
import { RendererService } from './renderer.service';
import { IntersectionPlaneService } from './intersection-plane.service';
import { ParticleManagerService } from './particle-manager.service';

/**
 * Service responsible for handling user input interactions
 */
@Injectable({
  providedIn: 'root'
})
export class InputHandlerService {
  private raycaster = new THREE.Raycaster();
  private mouse = new THREE.Vector2();
  private isDragging = false;
  private mouseDownTime = 0;

  constructor(
    private rendererService: RendererService,
    private intersectionPlaneService: IntersectionPlaneService,
    private particleManagerService: ParticleManagerService
  ) {}

  /**
   * Initializes input event listeners
   */
  initializeInputHandlers(rendererElement: HTMLElement): void {
    rendererElement.addEventListener('mousedown', this.onMouseDown.bind(this));
    rendererElement.addEventListener('mouseup', this.onMouseUp.bind(this));
    rendererElement.addEventListener('mousemove', this.onMouseMove.bind(this));
    window.addEventListener('resize', this.onWindowResize.bind(this));
  }

  /**
   * Handles mouse down event
   */
  private onMouseDown(event: MouseEvent): void {
    this.isDragging = false;
    this.mouseDownTime = Date.now();
  }

  /**
   * Handles mouse up event
   */
  private onMouseUp(event: MouseEvent): void {
    // If mouse was down for less than 200ms and didn't move much, consider it a click, not a drag
    if (Date.now() - this.mouseDownTime < 200 && !this.isDragging) {
      this.handlePlaneClick(event);
    }
    this.isDragging = false;
  }

  /**
   * Handles mouse move event
   */
  private onMouseMove(event: MouseEvent): void {
    // If mouse moves while button is pressed, it's a drag
    if (event.buttons > 0) {
      this.isDragging = true;
    }
  }

  /**
   * Handles mouse click on the plane
   */
  private handlePlaneClick(event: MouseEvent): void {
    // Calculate mouse position in normalized device coordinates
    const rect = this.rendererService.renderer.domElement.getBoundingClientRect();
    this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
    this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;

    // Update the picking ray with the camera and mouse position
    this.raycaster.setFromCamera(this.mouse, this.rendererService.camera);

    // Calculate objects intersecting the picking ray
    const intersects = this.raycaster.intersectObject(this.intersectionPlaneService.getIntersectionPlane());

    if (intersects.length > 0) {
      const point = intersects[0].point;
      this.particleManagerService.addParticle(point.x, point.y, point.z);
    }
  }

  /**
   * Handles window resize event
   */
  private onWindowResize(): void {
    // This is delegated to the renderer service
    const container = this.rendererService.renderer.domElement.parentElement;
    if (container) {
      this.rendererService.onWindowResize(new ElementRef(container));
    }
  }

  /**
   * Removes event listeners
   */
  cleanup(rendererElement: HTMLElement): void {
    rendererElement.removeEventListener('mousedown', this.onMouseDown.bind(this));
    rendererElement.removeEventListener('mouseup', this.onMouseUp.bind(this));
    rendererElement.removeEventListener('mousemove', this.onMouseMove.bind(this));
    window.removeEventListener('resize', this.onWindowResize.bind(this));
  }
}
