<script>
    import { onMount, onDestroy } from 'svelte';
    import { browser } from '$app/environment';

    let canvas;
    let ctx;
    let animationId;
    let particles = [];
    let crystal = new Set(); // Stored as "x,y" strings for O(1) lookup
    let width, height;
    let centerX, centerY;
    let hue = 0;
    let phase = 'growing'; // 'growing' | 'shattering' | 'dissolving'
    let shatterParticles = [];
    let phaseStartTime = 0;

    const CONFIG = {
        particleCount: 500,         // Active random walkers
        particleSpeed: 3,           // Movement speed
        stickDistance: 2,           // Distance to attach to crystal
        maxCrystalSize: 8000,       // Max crystal points before shatter
        hueShiftSpeed: 0.3,         // Color cycling speed
        shatterDuration: 2000,      // Milliseconds for shatter effect
        dissolveDuration: 2000      // Milliseconds for dissolve effect
    };

    function initCanvas() {
        if (!canvas) return;
        width = window.innerWidth;
        height = window.innerHeight;
        canvas.width = width;
        canvas.height = height;
        ctx = canvas.getContext('2d');
        centerX = Math.floor(width / 2);
        centerY = Math.floor(height / 2);
    }

    function initCrystal() {
        crystal.clear();
        particles = [];
        shatterParticles = [];
        phase = 'growing';
        phaseStartTime = performance.now();

        // Seed crystal at center with a small cluster
        for (let dx = -2; dx <= 2; dx++) {
            for (let dy = -2; dy <= 2; dy++) {
                crystal.add(`${centerX + dx},${centerY + dy}`);
            }
        }

        // Initialize random walkers from edges
        for (let i = 0; i < CONFIG.particleCount; i++) {
            particles.push(createParticle());
        }

        // Clear canvas
        if (ctx) {
            ctx.fillStyle = 'black';
            ctx.fillRect(0, 0, width, height);
        }
    }

    function createParticle() {
        // Spawn from random edge
        const edge = Math.floor(Math.random() * 4);
        let x, y;
        switch (edge) {
            case 0: x = Math.random() * width; y = 0; break;
            case 1: x = width; y = Math.random() * height; break;
            case 2: x = Math.random() * width; y = height; break;
            case 3: x = 0; y = Math.random() * height; break;
        }
        return { x, y, hue: Math.random() * 360 };
    }

    function update() {
        if (phase === 'growing') {
            updateGrowing();
        } else if (phase === 'shattering') {
            updateShattering();
        } else if (phase === 'dissolving') {
            updateDissolving();
        }
    }

    function updateGrowing() {
        hue = (hue + CONFIG.hueShiftSpeed) % 360;

        for (let i = particles.length - 1; i >= 0; i--) {
            const p = particles[i];

            // Random walk toward center with bias
            const dx = centerX - p.x;
            const dy = centerY - p.y;
            const dist = Math.sqrt(dx * dx + dy * dy);

            // Biased random walk (DLA with drift)
            p.x += (Math.random() - 0.5) * CONFIG.particleSpeed * 2 + (dx / dist) * 0.5;
            p.y += (Math.random() - 0.5) * CONFIG.particleSpeed * 2 + (dy / dist) * 0.5;

            // Check for crystallization
            if (shouldCrystallize(p)) {
                const px = Math.round(p.x);
                const py = Math.round(p.y);
                crystal.add(`${px},${py}`);
                particles[i] = createParticle(); // Respawn
            }

            // Respawn if out of bounds
            if (p.x < 0 || p.x > width || p.y < 0 || p.y > height) {
                particles[i] = createParticle();
            }
        }

        // Check if crystal is full
        if (crystal.size > CONFIG.maxCrystalSize) {
            startShatter();
        }
    }

    function shouldCrystallize(p) {
        const px = Math.round(p.x);
        const py = Math.round(p.y);

        // Check neighbors
        for (let dx = -CONFIG.stickDistance; dx <= CONFIG.stickDistance; dx++) {
            for (let dy = -CONFIG.stickDistance; dy <= CONFIG.stickDistance; dy++) {
                if (crystal.has(`${px + dx},${py + dy}`)) {
                    return true;
                }
            }
        }
        return false;
    }

    function startShatter() {
        phase = 'shattering';
        phaseStartTime = performance.now();
        shatterParticles = [];

        // Convert crystal points to shatter particles
        for (const key of crystal) {
            const [x, y] = key.split(',').map(Number);
            const angle = Math.atan2(y - centerY, x - centerX);
            const dist = Math.sqrt((x - centerX) ** 2 + (y - centerY) ** 2);
            shatterParticles.push({
                x, y,
                vx: Math.cos(angle) * (2 + Math.random() * 4),
                vy: Math.sin(angle) * (2 + Math.random() * 4),
                hue: (hue + dist * 0.3) % 360,
                alpha: 1,
                size: 2 + Math.random() * 2
            });
        }
    }

    function updateShattering() {
        const elapsed = performance.now() - phaseStartTime;

        for (const p of shatterParticles) {
            p.x += p.vx;
            p.y += p.vy;
            p.alpha = Math.max(0, 1 - (elapsed / CONFIG.shatterDuration));
        }

        if (elapsed >= CONFIG.shatterDuration) {
            phase = 'dissolving';
            phaseStartTime = performance.now();
        }
    }

    function updateDissolving() {
        const elapsed = performance.now() - phaseStartTime;

        for (const p of shatterParticles) {
            p.alpha = Math.max(0, 1 - (elapsed / CONFIG.dissolveDuration));
        }

        if (elapsed >= CONFIG.dissolveDuration) {
            initCrystal(); // Regrow
        }
    }

    function draw() {
        // Semi-transparent overlay for trail effect
        ctx.fillStyle = 'rgba(0, 0, 0, 0.15)';
        ctx.fillRect(0, 0, width, height);

        if (phase === 'growing') {
            drawCrystal();
            drawParticles();
        } else {
            drawShatterParticles();
        }
    }

    function drawCrystal() {
        for (const key of crystal) {
            const [x, y] = key.split(',').map(Number);
            const dist = Math.sqrt((x - centerX) ** 2 + (y - centerY) ** 2);
            const h = (hue + dist * 0.3) % 360;

            ctx.fillStyle = `hsla(${h}, 80%, 60%, 0.9)`;
            ctx.fillRect(x - 1, y - 1, 3, 3);
        }
    }

    function drawParticles() {
        ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
        for (const p of particles) {
            ctx.fillRect(p.x, p.y, 2, 2);
        }
    }

    function drawShatterParticles() {
        for (const p of shatterParticles) {
            if (p.alpha <= 0) continue;
            ctx.fillStyle = `hsla(${p.hue}, 80%, 60%, ${p.alpha})`;
            ctx.beginPath();
            ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
            ctx.fill();
        }
    }

    function startAnimation() {
        function loop() {
            update();
            draw();
            animationId = requestAnimationFrame(loop);
        }
        loop();
    }

    function handleResize() {
        initCanvas();
        initCrystal();
    }

    onMount(() => {
        if (!browser) return;

        initCanvas();
        initCrystal();
        startAnimation();

        window.addEventListener('resize', handleResize);
    });

    onDestroy(() => {
        if (animationId) {
            cancelAnimationFrame(animationId);
        }
        if (browser) {
            window.removeEventListener('resize', handleResize);
        }
    });
</script>

<canvas bind:this={canvas} class="fractal-canvas"></canvas>

<style>
    .fractal-canvas {
        position: absolute;
        inset: 0;
        pointer-events: none;
    }
</style>
