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

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

const COLORS = {
    gold: 0xf0c239,
    goldLight: 0xffd700,
    goldDark: 0xd4a84b,
    red: 0xc21f30,
    trackLine: 0x333333,
    trackBg: 0x1a1a1a,
    textMuted: 0x888888,
};

const Easing = {
    easeOutQuart: (t: number): number => 1 - Math.pow(1 - t, 4),
    easeInOutCubic: (t: number): number =>
        t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2,
};

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

export interface RaceHorse {
    id: string;
    name: string;
    votes: number;
    trackIndex: number;
    // Animation state
    currentX: number;
    targetX: number;
    glowIntensity: number;
    targetGlowIntensity: number;
    particles: Array<{ x: number; y: number; alpha: number; speed: number }>;
}

export interface HorseRaceConfig {
    trackCount: number;
    trackHeight: number;
    trackPadding: number;
    minX: number;
    maxX: number;
    horseSize: number;
}

// ============================================================================
// HorseRaceRenderer Class
// ============================================================================

export class HorseRaceRenderer {
    private app: Application;
    private container: Container;
    private trackLayer: Graphics;
    private horseLayer: Graphics;
    private labelLayer: Container;

    private horses: Map<string, RaceHorse> = new Map();
    private maxVotes: number = 1;
    private config: HorseRaceConfig;

    private isDestroyed = false;
    private time = 0;

    constructor() {
        this.app = new Application();
        this.container = new Container();
        this.trackLayer = new Graphics();
        this.horseLayer = new Graphics();
        this.labelLayer = new Container();

        this.config = {
            trackCount: 8,
            trackHeight: 80,
            trackPadding: 20,
            minX: 150,
            maxX: 0, // Will be set on init
            horseSize: 20,
        };
    }

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

        this.config.maxX = this.app.screen.width - 100;

        this.container.addChild(this.trackLayer);
        this.container.addChild(this.horseLayer);
        this.container.addChild(this.labelLayer);
        this.app.stage.addChild(this.container);

        this.app.ticker.add(this.update.bind(this));
        window.addEventListener('resize', this.handleResize.bind(this));

        this.drawTracks();
    }

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

    /**
     * Set the list of programs (horses) to display
     */
    setPrograms(programs: Array<{ id: string; name: string; votes: number }>): void {
        // Update max votes
        this.maxVotes = Math.max(1, ...programs.map((p) => p.votes));

        programs.forEach((program, index) => {
            const trackIndex = index % this.config.trackCount;
            const trackY = this.getTrackY(trackIndex);

            if (this.horses.has(program.id)) {
                // Update existing horse
                const horse = this.horses.get(program.id)!;
                horse.votes = program.votes;
                horse.targetX = this.calculateHorseX(program.votes);
                horse.targetGlowIntensity = this.calculateGlowIntensity(program.votes);
            } else {
                // Create new horse
                const startX = this.config.minX;
                const horse: RaceHorse = {
                    id: program.id,
                    name: program.name,
                    votes: program.votes,
                    trackIndex,
                    currentX: startX,
                    targetX: this.calculateHorseX(program.votes),
                    glowIntensity: 0,
                    targetGlowIntensity: this.calculateGlowIntensity(program.votes),
                    particles: [],
                };
                this.horses.set(program.id, horse);

                // Create label
                this.createHorseLabel(horse, trackY);
            }
        });

        this.drawTracks();
    }

    /**
     * Update a single program's votes
     */
    updateVotes(programId: string, newVotes: number): void {
        const horse = this.horses.get(programId);
        if (!horse) return;

        horse.votes = newVotes;

        // Recalculate max votes
        this.maxVotes = Math.max(1, ...Array.from(this.horses.values()).map((h) => h.votes));

        // Update all horses' target positions
        this.horses.forEach((h) => {
            h.targetX = this.calculateHorseX(h.votes);
            h.targetGlowIntensity = this.calculateGlowIntensity(h.votes);
        });

        // Add burst particles for the updated horse
        this.addParticleBurst(horse);
    }

    // ============================================================================
    // Calculation Helpers
    // ============================================================================

    private getTrackY(trackIndex: number): number {
        const totalHeight = this.config.trackCount * this.config.trackHeight;
        const startY = (this.app.screen.height - totalHeight) / 2;
        return startY + trackIndex * this.config.trackHeight + this.config.trackHeight / 2;
    }

    private calculateHorseX(votes: number): number {
        const progress = this.maxVotes > 0 ? votes / this.maxVotes : 0;
        return this.config.minX + (this.config.maxX - this.config.minX) * progress;
    }

    private calculateGlowIntensity(votes: number): number {
        const progress = this.maxVotes > 0 ? votes / this.maxVotes : 0;
        return 0.3 + progress * 0.7;
    }

    // ============================================================================
    // Drawing
    // ============================================================================

    private drawTracks(): void {
        this.trackLayer.clear();

        for (let i = 0; i < this.config.trackCount; i++) {
            const y = this.getTrackY(i);

            // Track background stripe (alternating)
            const stripeAlpha = i % 2 === 0 ? 0.05 : 0.02;
            this.trackLayer.rect(
                0,
                y - this.config.trackHeight / 2,
                this.app.screen.width,
                this.config.trackHeight
            );
            this.trackLayer.fill({ color: 0xffffff, alpha: stripeAlpha });

            // Track line
            this.trackLayer.moveTo(this.config.minX - 20, y);
            this.trackLayer.lineTo(this.config.maxX + 50, y);
            this.trackLayer.stroke({ color: COLORS.trackLine, alpha: 0.3, width: 1 });

            // Start line
            this.trackLayer.moveTo(this.config.minX, y - 20);
            this.trackLayer.lineTo(this.config.minX, y + 20);
            this.trackLayer.stroke({ color: COLORS.gold, alpha: 0.5, width: 2 });
        }

        // Finish line
        const finishX = this.config.maxX;
        this.trackLayer.moveTo(finishX, this.getTrackY(0) - this.config.trackHeight / 2);
        this.trackLayer.lineTo(finishX, this.getTrackY(this.config.trackCount - 1) + this.config.trackHeight / 2);
        this.trackLayer.stroke({ color: COLORS.gold, alpha: 0.8, width: 3 });

        // Finish checkered pattern
        const checkerSize = 10;
        for (let row = 0; row < 4; row++) {
            for (let col = 0; col < 3; col++) {
                if ((row + col) % 2 === 0) {
                    this.trackLayer.rect(
                        finishX + col * checkerSize,
                        this.getTrackY(0) - this.config.trackHeight / 2 + row * checkerSize,
                        checkerSize,
                        checkerSize
                    );
                    this.trackLayer.fill({ color: COLORS.gold, alpha: 0.6 });
                }
            }
        }
    }

    private createHorseLabel(horse: RaceHorse, y: number): void {
        const style = new TextStyle({
            fontFamily: 'SimSun, Songti SC, serif',
            fontSize: 16,
            fill: COLORS.textMuted,
            align: 'right',
        });

        const label = new Text({ text: horse.name, style });
        label.anchor.set(1, 0.5);
        label.x = this.config.minX - 30;
        label.y = y;
        label.name = `label-${horse.id}`;

        this.labelLayer.addChild(label);
    }

    private drawHorse(horse: RaceHorse): void {
        const y = this.getTrackY(horse.trackIndex);
        const x = horse.currentX;
        const size = this.config.horseSize;
        const intensity = horse.glowIntensity;

        // Outer glow rings
        for (let i = 4; i >= 0; i--) {
            const ratio = i / 4;
            const glowRadius = size * (1.5 + ratio * 2) * intensity;
            const alpha = (1 - ratio) * 0.2 * intensity;

            this.horseLayer.circle(x, y, glowRadius);
            this.horseLayer.fill({ color: COLORS.gold, alpha });
        }

        // Main body (gold orb)
        this.horseLayer.circle(x, y, size);
        this.horseLayer.fill({ color: COLORS.gold, alpha: 0.9 });

        // Inner bright core
        this.horseLayer.circle(x, y, size * 0.6);
        this.horseLayer.fill({ color: COLORS.goldLight, alpha: 1 });

        // White hot center
        this.horseLayer.circle(x, y, size * 0.3);
        this.horseLayer.fill({ color: 0xffffff, alpha: 0.8 });

        // Draw particles
        horse.particles.forEach((p) => {
            this.horseLayer.circle(p.x, y + (Math.random() - 0.5) * 10, 3);
            this.horseLayer.fill({ color: COLORS.goldLight, alpha: p.alpha });
        });
    }

    private addParticleBurst(horse: RaceHorse): void {
        const y = this.getTrackY(horse.trackIndex);
        for (let i = 0; i < 8; i++) {
            horse.particles.push({
                x: horse.currentX,
                y,
                alpha: 1,
                speed: 2 + Math.random() * 3,
            });
        }
    }

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

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

        this.time += ticker.deltaMS;
        this.horseLayer.clear();

        this.horses.forEach((horse) => {
            // Smoothly interpolate position
            const dx = horse.targetX - horse.currentX;
            horse.currentX += dx * 0.08; // Smooth easing

            // Smoothly interpolate glow
            const dg = horse.targetGlowIntensity - horse.glowIntensity;
            horse.glowIntensity += dg * 0.1;

            // Update particles (trail effect)
            horse.particles = horse.particles.filter((p) => {
                p.x -= p.speed;
                p.alpha -= 0.02;
                return p.alpha > 0;
            });

            // Add trail particles when moving fast
            if (Math.abs(dx) > 5 && Math.random() > 0.7) {
                const y = this.getTrackY(horse.trackIndex);
                horse.particles.push({
                    x: horse.currentX - this.config.horseSize,
                    y,
                    alpha: 0.6,
                    speed: 1 + Math.random() * 2,
                });
            }

            this.drawHorse(horse);
        });
    }

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

    private handleResize(): void {
        if (this.isDestroyed) return;

        this.app.renderer.resize(window.innerWidth, window.innerHeight);
        this.config.maxX = this.app.screen.width - 100;

        // Update label positions
        this.horses.forEach((horse) => {
            const label = this.labelLayer.getChildByName(`label-${horse.id}`) as Text;
            if (label) {
                label.y = this.getTrackY(horse.trackIndex);
            }
            horse.targetX = this.calculateHorseX(horse.votes);
        });

        this.drawTracks();
    }

    destroy(): void {
        this.isDestroyed = true;
        window.removeEventListener('resize', this.handleResize.bind(this));
        this.horses.clear();
        this.app.destroy(true, { children: true });
    }
}
