import { Application, Container, Graphics, Text, TextStyle, Ticker, BlurFilter } from 'pixi.js';

// ============================================================================
// Constants
// ============================================================================

const COLORS = {
  gold: 0xf0c239,
  goldLight: 0xffd700,
  goldDark: 0xd4a84b,
  red: 0xc21f30,
  redLight: 0xe63946,
  redDark: 0x8b0000,
  wineRed: 0x2a0a0e,
  black: 0x000000,
  white: 0xffffff,
};

// Easing functions
const Easing = {
  easeInOutCubic: (t: number): number => {
    return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
  },
  easeOutQuart: (t: number): number => {
    return 1 - Math.pow(1 - t, 4);
  },
  easeInQuart: (t: number): number => {
    return t * t * t * t;
  },
  easeOutBack: (t: number): number => {
    const c1 = 1.70158;
    const c3 = c1 + 1;
    return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2);
  },
  easeOutElastic: (t: number): number => {
    const c4 = (2 * Math.PI) / 3;
    return t === 0 ? 0 : t === 1 ? 1 : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1;
  },
};

// ============================================================================
// Types
// ============================================================================

export interface Participant {
  id: string;
  name: string;
  department: string;
  zodiac?: string;
  age?: number;
  avatar?: string;
  isEligible?: boolean;
}

export type LotteryPhase = 'idle' | 'galaxy' | 'storm' | 'reveal' | 'complete';

interface NameParticle {
  id: string;
  name: string;
  x: number;
  y: number;
  z: number; // For 3D sphere effect
  targetX: number;
  targetY: number;
  vx: number;
  vy: number;
  vz: number;
  rotation: number;
  rotationSpeed: number;
  scale: number;
  alpha: number;
  isEligible: boolean;
  isWinner: boolean;
  text: Text | null;
}

interface ConfettiParticle {
  x: number;
  y: number;
  vx: number;
  vy: number;
  rotation: number;
  rotationSpeed: number;
  width: number;
  height: number;
  color: number;
  alpha: number;
  gravity: number;
}

// ============================================================================
// LotteryMachine Class
// ============================================================================

export class LotteryMachine {
  private app: Application;
  private mainContainer: Container;
  private galaxyContainer: Container;
  private stormContainer: Container;
  private revealContainer: Container;
  private confettiContainer: Container;
  private scrollContainer: Container;
  private dimOverlay: Graphics;

  private nameParticles: NameParticle[] = [];
  private confettiParticles: ConfettiParticle[] = [];
  private participants: Participant[] = [];
  private winners: Participant[] = [];

  private phase: LotteryPhase = 'idle';
  private phaseTime = 0;
  private time = 0;
  private isDestroyed = false;

  // Galaxy parameters
  private sphereRadius = 300;
  private sphereRotationX = 0;
  private sphereRotationY = 0;
  private sphereRotationZ = 0;

  // Storm parameters
  private stormIntensity = 0;
  private stormAngle = 0;
  private blurFilter: BlurFilter;

  // Reveal parameters
  private revealProgress = 0;
  private scrollScale = 0;

  // Text style
  private nameStyle: TextStyle;
  private winnerStyle: TextStyle;

  // Callbacks
  private onPhaseChange?: (phase: LotteryPhase) => void;
  private onWinnersRevealed?: (winners: Participant[]) => void;

  constructor() {
    this.app = new Application();
    this.mainContainer = new Container();
    this.galaxyContainer = new Container();
    this.stormContainer = new Container();
    this.revealContainer = new Container();
    this.confettiContainer = new Container();
    this.scrollContainer = new Container();
    this.dimOverlay = new Graphics();
    this.blurFilter = new BlurFilter({ strength: 0 });

    this.nameStyle = new TextStyle({
      fontFamily: '"Noto Serif SC", serif',
      fontSize: 18,
      fill: COLORS.gold,
      fontWeight: 'bold',
    });

    this.winnerStyle = new TextStyle({
      fontFamily: '"Noto Serif SC", serif',
      fontSize: 48,
      fill: COLORS.goldLight,
      fontWeight: 'bold',
      dropShadow: {
        color: COLORS.gold,
        blur: 10,
        distance: 0,
      },
    });
  }

  async init(canvas: HTMLCanvasElement): Promise<void> {
    await this.app.init({
      canvas,
      width: window.innerWidth,
      height: window.innerHeight,
      backgroundColor: COLORS.wineRed,
      antialias: true,
      resolution: window.devicePixelRatio || 1,
      autoDensity: true,
    });

    // Setup layer hierarchy
    this.mainContainer.addChild(this.galaxyContainer);
    this.mainContainer.addChild(this.stormContainer);
    this.mainContainer.addChild(this.dimOverlay);
    this.mainContainer.addChild(this.scrollContainer);
    this.mainContainer.addChild(this.revealContainer);
    this.mainContainer.addChild(this.confettiContainer);
    this.app.stage.addChild(this.mainContainer);

    // Apply blur filter to storm container
    this.stormContainer.filters = [this.blurFilter];

    // Start animation loop
    this.app.ticker.add(this.update.bind(this));

    // Handle resize
    window.addEventListener('resize', this.handleResize.bind(this));
  }

  // ============================================================================
  // Public API
  // ============================================================================

  /**
   * Set participants for the lottery
   */
  setParticipants(participants: Participant[]): void {
    this.participants = participants;
    this.createNameParticles();
  }

  /**
   * Filter participants by criteria (e.g., zodiac for Round 4)
   */
  filterParticipants(filter: { zodiac?: string; ageRange?: { min: number; max: number } }): void {
    this.nameParticles.forEach((p) => {
      const participant = this.participants.find((part) => part.id === p.id);
      if (!participant) return;

      let eligible = true;

      if (filter.zodiac && participant.zodiac !== filter.zodiac) {
        eligible = false;
      }

      if (filter.ageRange) {
        const age = participant.age || 0;
        if (age < filter.ageRange.min || age > filter.ageRange.max) {
          eligible = false;
        }
      }

      p.isEligible = eligible;
    });
  }

  /**
   * Start the lottery - transition to Galaxy phase
   */
  startGalaxy(): void {
    this.phase = 'galaxy';
    this.phaseTime = 0;
    this.onPhaseChange?.('galaxy');
  }

  /**
   * Start the storm - rolling phase
   */
  startStorm(): void {
    this.phase = 'storm';
    this.phaseTime = 0;
    this.stormIntensity = 0;
    this.onPhaseChange?.('storm');
  }

  /**
   * Stop and reveal winners
   */
  revealWinners(winnerIds: string[]): void {
    this.winners = this.participants.filter((p) => winnerIds.includes(p.id));

    // Mark winner particles
    this.nameParticles.forEach((p) => {
      p.isWinner = winnerIds.includes(p.id);
    });

    this.phase = 'reveal';
    this.phaseTime = 0;
    this.revealProgress = 0;
    this.scrollScale = 0;
    this.onPhaseChange?.('reveal');
  }

  /**
   * Reset to idle state
   */
  reset(): void {
    this.phase = 'idle';
    this.phaseTime = 0;
    this.winners = [];
    this.confettiParticles = [];
    this.stormIntensity = 0;
    this.blurFilter.strength = 0;

    // Reset particles
    this.nameParticles.forEach((p) => {
      p.isWinner = false;
    });

    // Clear containers
    this.revealContainer.removeChildren();
    this.scrollContainer.removeChildren();
    this.confettiContainer.removeChildren();
    this.dimOverlay.clear();

    this.onPhaseChange?.('idle');
  }

  /**
   * Set callbacks
   */
  onPhase(callback: (phase: LotteryPhase) => void): void {
    this.onPhaseChange = callback;
  }

  onWinners(callback: (winners: Participant[]) => void): void {
    this.onWinnersRevealed = callback;
  }

  // ============================================================================
  // Particle Creation
  // ============================================================================

  private createNameParticles(): void {
    // Clear existing
    this.galaxyContainer.removeChildren();
    this.stormContainer.removeChildren();
    this.nameParticles = [];

    const centerX = this.app.screen.width / 2;
    const centerY = this.app.screen.height / 2;

    this.participants.forEach((participant, index) => {
      // Distribute on sphere using fibonacci spiral
      const phi = Math.acos(1 - 2 * (index + 0.5) / this.participants.length);
      const theta = Math.PI * (1 + Math.sqrt(5)) * index;

      const x = this.sphereRadius * Math.sin(phi) * Math.cos(theta);
      const y = this.sphereRadius * Math.sin(phi) * Math.sin(theta);
      const z = this.sphereRadius * Math.cos(phi);

      // Create text
      const text = new Text({
        text: participant.name,
        style: this.nameStyle,
      });
      text.anchor.set(0.5);
      this.galaxyContainer.addChild(text);

      const particle: NameParticle = {
        id: participant.id,
        name: participant.name,
        x: centerX + x,
        y: centerY + y,
        z,
        targetX: centerX,
        targetY: centerY,
        vx: 0,
        vy: 0,
        vz: 0,
        rotation: 0,
        rotationSpeed: (Math.random() - 0.5) * 0.1,
        scale: 1,
        alpha: 1,
        isEligible: participant.isEligible !== false,
        isWinner: false,
        text,
      };

      this.nameParticles.push(particle);
    });
  }

  // ============================================================================
  // Animation Loop
  // ============================================================================

  private update(ticker: Ticker): void {
    if (this.isDestroyed) return;

    this.time += ticker.deltaMS;
    this.phaseTime += ticker.deltaMS;

    switch (this.phase) {
      case 'galaxy':
        this.updateGalaxy(ticker.deltaMS);
        break;
      case 'storm':
        this.updateStorm(ticker.deltaMS);
        break;
      case 'reveal':
        this.updateReveal(ticker.deltaMS);
        break;
      case 'complete':
        this.updateComplete(ticker.deltaMS);
        break;
    }

    // Always update confetti
    this.updateConfetti(ticker.deltaMS);
  }

  // ============================================================================
  // Galaxy Phase (Idle Sphere)
  // ============================================================================

  private updateGalaxy(deltaMs: number): void {
    const centerX = this.app.screen.width / 2;
    const centerY = this.app.screen.height / 2;

    // Slowly rotate the sphere
    this.sphereRotationY += deltaMs * 0.0003;
    this.sphereRotationX = Math.sin(this.time * 0.0001) * 0.2;

    this.nameParticles.forEach((p) => {
      if (!p.text) return;

      // Apply 3D rotation
      const cosY = Math.cos(this.sphereRotationY);
      const sinY = Math.sin(this.sphereRotationY);
      const cosX = Math.cos(this.sphereRotationX);
      const sinX = Math.sin(this.sphereRotationX);

      // Rotate around Y axis
      const x1 = (p.x - centerX) * cosY - p.z * sinY;
      const z1 = (p.x - centerX) * sinY + p.z * cosY;

      // Rotate around X axis
      const y1 = (p.y - centerY) * cosX - z1 * sinX;
      const z2 = (p.y - centerY) * sinX + z1 * cosX;

      // Project to 2D with perspective
      const perspective = 800;
      const scale = perspective / (perspective + z2);

      p.text.x = centerX + x1 * scale;
      p.text.y = centerY + y1 * scale;
      p.text.scale.set(scale * 0.8);

      // Depth-based alpha and color
      const depthAlpha = (z2 + this.sphereRadius) / (this.sphereRadius * 2);
      p.text.alpha = p.isEligible ? 0.3 + depthAlpha * 0.7 : 0.15;

      // Dim ineligible names
      if (!p.isEligible) {
        p.text.style.fill = 0x666666;
      } else {
        p.text.style.fill = COLORS.gold;
      }

      // Z-sorting
      p.text.zIndex = Math.floor(z2);
    });

    this.galaxyContainer.sortChildren();
  }

  // ============================================================================
  // Storm Phase (Tornado)
  // ============================================================================

  private updateStorm(deltaMs: number): void {
    const centerX = this.app.screen.width / 2;
    const centerY = this.app.screen.height / 2;

    // Ramp up storm intensity
    this.stormIntensity = Math.min(1, this.stormIntensity + deltaMs * 0.001);
    this.stormAngle += deltaMs * 0.01 * this.stormIntensity;

    // Apply motion blur based on intensity
    this.blurFilter.strength = this.stormIntensity * 8;

    this.nameParticles.forEach((p, index) => {
      if (!p.text) return;

      // Tornado vortex motion
      const baseAngle = this.stormAngle + (index / this.nameParticles.length) * Math.PI * 2;
      const verticalPos = ((this.time * 0.001 + index * 0.1) % 2) - 1; // -1 to 1
      const radius = 100 + Math.abs(verticalPos) * 200 * this.stormIntensity;

      const targetX = centerX + Math.cos(baseAngle) * radius;
      const targetY = centerY + verticalPos * 300;

      // Smooth interpolation
      p.x += (targetX - p.x) * 0.1;
      p.y += (targetY - p.y) * 0.1;

      p.text.x = p.x;
      p.text.y = p.y;
      p.text.rotation += p.rotationSpeed * this.stormIntensity * 3;
      p.text.alpha = p.isEligible ? 0.8 : 0.2;

      // Scale based on position
      const distFromCenter = Math.sqrt(
        Math.pow(p.x - centerX, 2) + Math.pow(p.y - centerY, 2)
      );
      p.text.scale.set(0.5 + (1 - distFromCenter / 400) * 0.5);
    });
  }

  // ============================================================================
  // Reveal Phase
  // ============================================================================

  private updateReveal(deltaMs: number): void {
    const centerX = this.app.screen.width / 2;
    const centerY = this.app.screen.height / 2;

    this.revealProgress = Math.min(1, this.revealProgress + deltaMs * 0.0008);
    const eased = Easing.easeOutQuart(this.revealProgress);

    // Reduce blur
    this.blurFilter.strength = (1 - eased) * 8;

    // Dim background
    this.dimOverlay.clear();
    this.dimOverlay.rect(0, 0, this.app.screen.width, this.app.screen.height);
    this.dimOverlay.fill({ color: COLORS.black, alpha: eased * 0.7 });

    // Disperse non-winners outward
    this.nameParticles.forEach((p) => {
      if (!p.text) return;

      if (p.isWinner) {
        // Winners fly to center
        const targetX = centerX;
        const targetY = centerY - 50;

        p.x += (targetX - p.x) * 0.05 * eased;
        p.y += (targetY - p.y) * 0.05 * eased;
        p.text.alpha = 1;
        p.text.scale.set(1 + eased * 1.5);
      } else {
        // Non-winners disperse
        const angle = Math.atan2(p.y - centerY, p.x - centerX);
        const disperseDistance = 500 * eased;

        p.x += Math.cos(angle) * disperseDistance * 0.02;
        p.y += Math.sin(angle) * disperseDistance * 0.02;
        p.text.alpha = Math.max(0, 1 - eased * 1.5);
      }

      p.text.x = p.x;
      p.text.y = p.y;
    });

    // Draw scroll background
    if (this.revealProgress > 0.3) {
      this.scrollScale = Easing.easeOutBack((this.revealProgress - 0.3) / 0.7);
      this.drawScroll(centerX, centerY, this.scrollScale);
    }

    // Spawn confetti when reveal is complete
    if (this.revealProgress >= 1 && this.phase === 'reveal') {
      this.phase = 'complete';
      this.spawnConfettiBurst();
      this.createWinnerDisplay();
      this.onWinnersRevealed?.(this.winners);
      this.onPhaseChange?.('complete');
    }
  }

  private drawScroll(centerX: number, centerY: number, scale: number): void {
    this.scrollContainer.removeChildren();

    const scrollGraphics = new Graphics();
    const width = 600 * scale;
    const height = 400 * scale;

    // Main scroll body
    scrollGraphics.roundRect(
      centerX - width / 2,
      centerY - height / 2,
      width,
      height,
      20 * scale
    );
    scrollGraphics.fill({ color: COLORS.redDark, alpha: 0.95 });

    // Gold border
    scrollGraphics.roundRect(
      centerX - width / 2 + 5,
      centerY - height / 2 + 5,
      width - 10,
      height - 10,
      15 * scale
    );
    scrollGraphics.stroke({ color: COLORS.gold, width: 3 * scale });

    // Inner decorative border
    scrollGraphics.roundRect(
      centerX - width / 2 + 15,
      centerY - height / 2 + 15,
      width - 30,
      height - 30,
      10 * scale
    );
    scrollGraphics.stroke({ color: COLORS.goldDark, width: 1 * scale });

    this.scrollContainer.addChild(scrollGraphics);
  }

  private createWinnerDisplay(): void {
    this.revealContainer.removeChildren();

    const centerX = this.app.screen.width / 2;
    const centerY = this.app.screen.height / 2;

    // Arrange winners
    const winnerCount = this.winners.length;
    const spacing = Math.min(120, 500 / winnerCount);

    this.winners.forEach((winner, index) => {
      const offsetX = (index - (winnerCount - 1) / 2) * spacing;

      const text = new Text({
        text: winner.name,
        style: this.winnerStyle,
      });
      text.anchor.set(0.5);
      text.x = centerX + offsetX;
      text.y = centerY - 30;

      // Department subtitle
      const deptText = new Text({
        text: winner.department,
        style: new TextStyle({
          fontFamily: '"Noto Serif SC", serif',
          fontSize: 20,
          fill: COLORS.goldDark,
        }),
      });
      deptText.anchor.set(0.5);
      deptText.x = centerX + offsetX;
      deptText.y = centerY + 30;

      this.revealContainer.addChild(text);
      this.revealContainer.addChild(deptText);
    });
  }

  // ============================================================================
  // Complete Phase
  // ============================================================================

  private updateComplete(deltaMs: number): void {
    // Gentle pulsing of winner names
    const pulse = Math.sin(this.time * 0.003) * 0.05 + 1;

    this.revealContainer.children.forEach((child, index) => {
      if (child instanceof Text && index % 2 === 0) {
        child.scale.set(pulse);
      }
    });
  }

  // ============================================================================
  // Confetti System
  // ============================================================================

  private spawnConfettiBurst(): void {
    const centerX = this.app.screen.width / 2;
    const colors = [COLORS.gold, COLORS.goldLight, COLORS.red, COLORS.redLight];

    for (let i = 0; i < 150; i++) {
      const angle = Math.random() * Math.PI * 2;
      const speed = 5 + Math.random() * 10;

      this.confettiParticles.push({
        x: centerX + (Math.random() - 0.5) * 200,
        y: this.app.screen.height * 0.3,
        vx: Math.cos(angle) * speed,
        vy: Math.sin(angle) * speed - 5,
        rotation: Math.random() * Math.PI * 2,
        rotationSpeed: (Math.random() - 0.5) * 0.3,
        width: 8 + Math.random() * 8,
        height: 4 + Math.random() * 4,
        color: colors[Math.floor(Math.random() * colors.length)],
        alpha: 1,
        gravity: 0.15 + Math.random() * 0.1,
      });
    }
  }

  private updateConfetti(deltaMs: number): void {
    const confettiGraphics = new Graphics();
    this.confettiContainer.removeChildren();

    this.confettiParticles = this.confettiParticles.filter((p) => {
      // Physics
      p.vy += p.gravity;
      p.vx *= 0.99; // Air resistance
      p.x += p.vx;
      p.y += p.vy;
      p.rotation += p.rotationSpeed;

      // Fade out when below screen
      if (p.y > this.app.screen.height) {
        p.alpha -= 0.02;
      }

      if (p.alpha <= 0) return false;

      // Draw confetti piece (calculate rotated rectangle corners)
      const cos = Math.cos(p.rotation);
      const sin = Math.sin(p.rotation);
      const hw = p.width / 2;
      const hh = p.height / 2;

      // Calculate rotated corners
      const corners = [
        { x: -hw, y: -hh },
        { x: hw, y: -hh },
        { x: hw, y: hh },
        { x: -hw, y: hh },
      ].map(corner => ({
        x: p.x + corner.x * cos - corner.y * sin,
        y: p.y + corner.x * sin + corner.y * cos,
      }));

      // Draw as polygon
      confettiGraphics.moveTo(corners[0].x, corners[0].y);
      confettiGraphics.lineTo(corners[1].x, corners[1].y);
      confettiGraphics.lineTo(corners[2].x, corners[2].y);
      confettiGraphics.lineTo(corners[3].x, corners[3].y);
      confettiGraphics.closePath();
      confettiGraphics.fill({ color: p.color, alpha: p.alpha });

      return true;
    });

    this.confettiContainer.addChild(confettiGraphics);
  }

  // ============================================================================
  // Lifecycle
  // ============================================================================

  private handleResize(): void {
    if (this.isDestroyed) return;
    this.app.renderer.resize(window.innerWidth, window.innerHeight);
  }

  destroy(): void {
    this.isDestroyed = true;
    window.removeEventListener('resize', this.handleResize.bind(this));
    this.nameParticles = [];
    this.confettiParticles = [];
    this.app.destroy(true, { children: true });
  }

  // ============================================================================
  // Getters
  // ============================================================================

  get currentPhase(): LotteryPhase {
    return this.phase;
  }

  get currentWinners(): Participant[] {
    return this.winners;
  }
}
