(function ($) {
    "use strict";

    const initPhysicsTags = ($scope) => {
        const wrapper = $scope.find('.sifency-physics-wrapper')[0];
        if (!wrapper) return;

        if (typeof Matter === 'undefined') {
            console.error('Sifency Error: Matter.js engine is not loaded.');
            return;
        }

        const config = JSON.parse(wrapper.dataset.config || '{}');
        const gravityY = parseFloat(config.gravityY || 1);
        const restitution = parseFloat(config.restitution || 0.6); // Bounce
        const frictionAir = parseFloat(config.frictionAir || 0.01);
        const hasWalls = config.walls === 'true';

        const canvas = wrapper.querySelector('.sifency-physics-canvas');
        const domTags = wrapper.querySelectorAll('.sifency-physics-tag');

        // Matter.js Module Aliases
        const Engine = Matter.Engine,
              Render = Matter.Render,
              Runner = Matter.Runner,
              Bodies = Matter.Bodies,
              Composite = Matter.Composite,
              Mouse = Matter.Mouse,
              MouseConstraint = Matter.MouseConstraint;

        // 1. Create Engine and World
        const engine = Engine.create();
        engine.world.gravity.y = gravityY;

        let width = wrapper.clientWidth;
        let height = wrapper.clientHeight;

        // Optional: Render is only for debugging/canvas (We hide it via CSS because we sync DOM elements)
        const render = Render.create({
            element: wrapper,
            engine: engine,
            canvas: canvas,
            options: {
                width: width,
                height: height,
                background: 'transparent',
                wireframes: false, // Set to true to see the physics boxes
                showAngleIndicator: false
            }
        });

        const bodies = [];

        // 2. Create Physics Bodies based on actual DOM Tag Sizes
        domTags.forEach((tag, i) => {
            // Get actual width/height of the text tags from CSS
            const tagWidth = tag.offsetWidth;
            const tagHeight = tag.offsetHeight;

            // Spawn them slightly apart at the top
            const startX = (width / 2) + (Math.random() * 200 - 100);
            const startY = -200 - (i * 100); // Drops from above screen

            // Create invisible physics rectangle
            const body = Bodies.rectangle(startX, startY, tagWidth, tagHeight, {
                restitution: restitution, // Bounciness
                friction: 0.1,
                frictionAir: frictionAir,
                chamfer: { radius: tagHeight / 2 } // Rounded corners like a pill
            });

            bodies.push(body);
            
            // Link the physics body to the HTML tag
            tag.physicsBody = body; 
        });

        // Add tags to the world
        Composite.add(engine.world, bodies);

        // 3. Create Walls (Floor, Left, Right)
        if (hasWalls) {
            const wallOptions = { isStatic: true, render: { visible: false } };
            // Floor
            Composite.add(engine.world, Bodies.rectangle(width / 2, height + 25, width + 100, 50, wallOptions));
            // Left Wall
            Composite.add(engine.world, Bodies.rectangle(-25, height / 2, 50, height * 2, wallOptions));
            // Right Wall
            Composite.add(engine.world, Bodies.rectangle(width + 25, height / 2, 50, height * 2, wallOptions));
            // Ceiling (Optional, prevents them bouncing out top)
            // Composite.add(engine.world, Bodies.rectangle(width / 2, -200, width + 100, 50, wallOptions));
        }

        // 4. Add Mouse Interaction (Drag and Throw)
        const mouse = Mouse.create(wrapper);
        const mouseConstraint = MouseConstraint.create(engine, {
            mouse: mouse,
            constraint: {
                stiffness: 0.2, // How tightly mouse holds the tag
                render: { visible: false }
            }
        });

        Composite.add(engine.world, mouseConstraint);

        // Keep the mouse in sync with scrolling
        render.mouse = mouse;

        // 5. Sync Loop (Matches DOM Tags to Invisible Physics Bodies)
        Matter.Events.on(engine, 'afterUpdate', function() {
            domTags.forEach(tag => {
                const body = tag.physicsBody;
                if (!body) return;

                // Sync Position (X, Y) and Rotation (Angle)
                tag.style.transform = `translate(-50%, -50%) translate(${body.position.x}px, ${body.position.y}px) rotate(${body.angle}rad)`;
            });
        });

        // 6. Handle Window Resize
        window.addEventListener('resize', () => {
            width = wrapper.clientWidth;
            height = wrapper.clientHeight;

            render.canvas.width = width;
            render.canvas.height = height;

            if (hasWalls) {
                // Remove old walls and add new ones (Quick hack for resize)
                // In production, you'd update body vertices instead
            }
        });

        // 7. Start the Engine
        Render.run(render);
        const runner = Runner.create();
        Runner.run(runner, engine);

        // Cleanup on Elementor Edit
        $scope.on('remove', () => {
            Render.stop(render);
            Runner.stop(runner);
            Engine.clear(engine);
            render.canvas.remove();
        });
    };

    $(window).on('elementor/frontend/init', function () {
        elementorFrontend.hooks.addAction(
            'frontend/element_ready/sifency-physics-tags.default',
            initPhysicsTags
        );
    });

})(jQuery);