import {
  Component,
  ElementRef,
  OnDestroy,
  OnInit,
  PLATFORM_ID,
  ViewChild,
  inject
} from '@angular/core';
import { isPlatformBrowser } from '@angular/common';

@Component({
  selector: 'app-particle-bg',
  standalone: true,
  host: {
    class: 'block fixed inset-0 z-0 pointer-events-none'
  },
  template: '<canvas #canvas class="absolute inset-0 h-full w-full pointer-events-auto"></canvas>'
})
export class ParticleBgComponent implements OnInit, OnDestroy {
  @ViewChild('canvas', { static: true }) private canvasRef?: ElementRef<HTMLCanvasElement>;

  private readonly platformId = inject(PLATFORM_ID);

  private context: CanvasRenderingContext2D | null = null;
  private particles: Particle[] = [];
  private mousePosition = {
    pointerX: -1000,
    pointerY: -1000
  };
  private animationId = 0;

  ngOnInit(): void {
    if (!isPlatformBrowser(this.platformId)) {
      return;
    }

    const canvas = this.canvasRef?.nativeElement;

    if (!canvas) {
      return;
    }

    const context = canvas.getContext('2d');

    if (!context) {
      return;
    }

    this.context = context;
    this.resize();

    window.addEventListener('resize', this.resizeHandler);
    window.addEventListener('mousemove', this.mouseMoveHandler);

    this.initParticles();
    this.animate();
  }

  ngOnDestroy(): void {
    if (!isPlatformBrowser(this.platformId)) {
      return;
    }

    cancelAnimationFrame(this.animationId);
    window.removeEventListener('resize', this.resizeHandler);
    window.removeEventListener('mousemove', this.mouseMoveHandler);
  }

  private readonly resizeHandler = () => this.resize();
  private readonly mouseMoveHandler = (event: MouseEvent) => {
    this.mousePosition.pointerX = event.clientX;
    this.mousePosition.pointerY = event.clientY;
  };

  private resize(): void {
    const canvas = this.canvasRef?.nativeElement;

    if (!canvas) {
      return;
    }

    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
  }

  private initParticles(): void {
    const particleCount = Math.min(
      80,
      Math.floor((window.innerWidth * window.innerHeight) / 15000)
    );

    this.particles = [];

    for (let particleIndex = 0; particleIndex < particleCount; particleIndex++) {
      this.particles.push({
        positionX: Math.random() * window.innerWidth,
        positionY: Math.random() * window.innerHeight,
        velocityX: (Math.random() - 0.5) * 0.4,
        velocityY: (Math.random() - 0.5) * 0.4,
        radius: Math.random() * 2 + 0.5,
        opacity: Math.random() * 0.5 + 0.1
      });
    }
  }

  private animate(): void {
    const canvas = this.canvasRef?.nativeElement;
    const context = this.context;

    if (!canvas || !context) {
      return;
    }

    context.clearRect(0, 0, canvas.width, canvas.height);

    for (const particle of this.particles) {
      const deltaX = particle.positionX - this.mousePosition.pointerX;
      const deltaY = particle.positionY - this.mousePosition.pointerY;
      const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);

      if (distance < 150 && distance > 0) {
        const force = (150 - distance) / 150;

        particle.velocityX += (deltaX / distance) * force * 0.3;
        particle.velocityY += (deltaY / distance) * force * 0.3;
      }

      particle.velocityX *= 0.98;
      particle.velocityY *= 0.98;

      particle.positionX += particle.velocityX;
      particle.positionY += particle.velocityY;

      if (particle.positionX < 0) {
        particle.positionX = canvas.width;
      }

      if (particle.positionX > canvas.width) {
        particle.positionX = 0;
      }

      if (particle.positionY < 0) {
        particle.positionY = canvas.height;
      }

      if (particle.positionY > canvas.height) {
        particle.positionY = 0;
      }

      context.beginPath();
      context.arc(particle.positionX, particle.positionY, particle.radius, 0, Math.PI * 2);
      context.fillStyle = `rgba(139, 92, 246, ${particle.opacity})`;
      context.fill();
    }

    for (let particleIndex = 0; particleIndex < this.particles.length; particleIndex++) {
      for (let connectionIndex = particleIndex + 1; connectionIndex < this.particles.length; connectionIndex++) {
        const sourceParticle = this.particles[particleIndex];
        const targetParticle = this.particles[connectionIndex];
        const deltaX = sourceParticle.positionX - targetParticle.positionX;
        const deltaY = sourceParticle.positionY - targetParticle.positionY;
        const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);

        if (distance < 120) {
          const opacity = (1 - distance / 120) * 0.15;

          context.beginPath();
          context.moveTo(sourceParticle.positionX, sourceParticle.positionY);
          context.lineTo(targetParticle.positionX, targetParticle.positionY);
          context.strokeStyle = `rgba(139, 92, 246, ${opacity})`;
          context.lineWidth = 0.5;
          context.stroke();
        }
      }
    }

    for (const particle of this.particles) {
      const deltaX = particle.positionX - this.mousePosition.pointerX;
      const deltaY = particle.positionY - this.mousePosition.pointerY;
      const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);

      if (distance < 200) {
        const opacity = (1 - distance / 200) * 0.25;

        context.beginPath();
        context.moveTo(particle.positionX, particle.positionY);
        context.lineTo(this.mousePosition.pointerX, this.mousePosition.pointerY);
        context.strokeStyle = `rgba(139, 92, 246, ${opacity})`;
        context.lineWidth = 0.7;
        context.stroke();
      }
    }

    this.animationId = requestAnimationFrame(() => this.animate());
  }
}

interface Particle {
  positionX: number;
  positionY: number;
  velocityX: number;
  velocityY: number;
  radius: number;
  opacity: number;
}
