<?php

namespace PHPNative\Ui;

use PHPNative\Framework\Profiler;
use PHPNative\Framework\TextRenderer;
use PHPNative\Tailwind\Parser\State;
use PHPNative\Tailwind\Style\Margin;
use PHPNative\Tailwind\Style\MediaQueryEnum;
use PHPNative\Tailwind\Style\Padding;
use PHPNative\Tailwind\Style\StateEnum;
use PHPNative\Tailwind\StyleParser;

abstract class Component
{
    protected int $id;

    protected $children = [];

    protected bool $visible = true;

    protected bool $overlay = false;

    protected bool $layoutDirty = true;

    protected bool $renderDirty = true;

    protected int $zIndex = 0;

    protected StateEnum $currentState = StateEnum::normal;

    protected null|Component $parent = null; // Reference to parent component

    // Texture caching for performance
    protected $cachedTexture = null; // Normal state texture
    protected $cachedHoverTexture = null; // Hover state texture
    protected bool $useTextureCache = false;
    protected bool $textureCacheValid = false;

    protected $cachedShadowTexture = null; // Cached shadow texture (normal state)
    protected $cachedShadowHoverTexture = null; // Cached shadow texture (hover state)
    protected bool $shadowCacheValid = false;
    protected bool $shadowHoverCacheValid = false;

    protected Viewport $viewport;

    protected array $computedStyles = [];
    protected array $hoverStylesCached = [];
    protected array $normalStylesCached = [];
    protected Viewport $contentViewport;

    protected null|Window $attachedWindow = null;

    // Cache whether this component or any of its children have hover styles
    protected null|bool $hasHoverStylesCache = null;

    public function __construct(
        protected string $style = '',
    ) {
        // Initialize viewports with default values
        // These will be properly set during layout()
        $this->id = rand();

        $this->viewport = new Viewport(
            x: 0,
            y: 0,
            width: 0,
            height: 0,
            windowWidth: 800,
            windowHeight: 600,
        );
        $this->contentViewport = clone $this->viewport;
    }

    public function setStyle(string $style): void
    {
        if ($this->style === $style) {
            return;
        }

        $this->style = $style;
        $this->markDirty(true);
    }

    /**
     * Destructor - clean up resources
     */
    public function __destruct()
    {
        $this->invalidateTextureCache();
    }

    /**
     * Clean up component resources
     */
    public function cleanup(): void
    {
        // Free texture cache
        $this->invalidateTextureCache();

        // Recursively cleanup children
        foreach ($this->children as $child) {
            if (method_exists($child, 'cleanup')) {
                $child->cleanup();
            }
        }
    }

    public function setViewport(Viewport $viewport): void
    {
        $this->viewport = $viewport;
    }

    public function getViewport(): Viewport
    {
        return $this->viewport;
    }

    public function getContentViewport(): Viewport
    {
        return $this->contentViewport;
    }

    public function setContentViewport(Viewport $viewport): void
    {
        $this->contentViewport = $viewport;
    }

    /**
     * Apply offset to viewport and all child viewports recursively
     */
    public function applyScrollOffset(int $offsetX, int $offsetY): void
    {
        // Offset this component's viewports
        $this->viewport->x = (int) ($this->viewport->x - $offsetX);
        $this->viewport->y = (int) ($this->viewport->y - $offsetY);
        $this->contentViewport->x = (int) ($this->contentViewport->x - $offsetX);
        $this->contentViewport->y = (int) ($this->contentViewport->y - $offsetY);

        // Recursively apply to all children
        foreach ($this->children as $child) {
            $child->applyScrollOffset($offsetX, $offsetY);
        }
    }

    public function setZIndex(int $zIndex): void
    {
        $this->zIndex = $zIndex;
    }

    public function getZIndex(): int
    {
        return $this->zIndex;
    }

    public function isVisible(): bool
    {
        return $this->visible;
    }

    public function setVisible(bool $visible): void
    {
        if ($this->visible !== $visible) {
            $this->visible = $visible;
            $this->markDirty(true);
        }
    }

    public function setOverlay(bool $overlay): void
    {
        if ($this->overlay !== $overlay) {
            $this->overlay = $overlay;
            $this->markDirty(false);
        }
    }

    public function isOverlay(): bool
    {
        return $this->overlay;
    }

    public function invalidateTextureCache(): void
    {
        if ($this->cachedTexture !== null) {
            sdl_destroy_texture($this->cachedTexture);
            $this->cachedTexture = null;
        }
        if ($this->cachedHoverTexture !== null) {
            sdl_destroy_texture($this->cachedHoverTexture);
            $this->cachedHoverTexture = null;
        }
        if ($this->cachedShadowTexture !== null) {
            sdl_destroy_texture($this->cachedShadowTexture);
            $this->cachedShadowTexture = null;
        }
        if ($this->cachedShadowHoverTexture !== null) {
            sdl_destroy_texture($this->cachedShadowHoverTexture);
            $this->cachedShadowHoverTexture = null;
        }
        $this->textureCacheValid = false;
        $this->shadowCacheValid = false;
        $this->shadowHoverCacheValid = false;
        $this->renderDirty = true;
    }

    public function buildTextureCache(&$renderer, null|TextRenderer $textRenderer = null): void
    {
        if (!$this->useTextureCache || $this->viewport->width <= 0 || $this->viewport->height <= 0) {
            return;
        }

        Profiler::start('buildTextureCache');
        Profiler::increment('texture_cache_built');

        // Clean up old textures
        $this->invalidateTextureCache();

        // Create texture for normal state
        $this->currentState = StateEnum::normal;
        $this->computedStyles = $this->normalStylesCached;
        $normalTexture = sdl_create_texture(
            $renderer,
            SDL_PIXELFORMAT_RGBA8888,
            SDL_TEXTUREACCESS_TARGET,
            (int) $this->viewport->width,
            (int) $this->viewport->height,
        );

        if ($normalTexture) {
            sdl_set_texture_blend_mode($normalTexture, SDL_BLENDMODE_BLEND);
            sdl_set_render_target($renderer, $normalTexture);
            sdl_set_render_draw_color($renderer, 0, 0, 0, 0);
            sdl_render_clear($renderer);

            // Temporarily disable caching to render to texture
            $oldCache = $this->useTextureCache;
            $this->useTextureCache = false;

            // Render at 0,0 into texture
            $oldX = $this->viewport->x;
            $oldY = $this->viewport->y;
            $this->viewport->x = 0;
            $this->viewport->y = 0;

            $this->render($renderer, $textRenderer);
            $this->renderContent($renderer, $textRenderer);

            $this->viewport->x = $oldX;
            $this->viewport->y = $oldY;
            $this->useTextureCache = $oldCache;

            sdl_set_render_target($renderer, null);
            $this->cachedTexture = $normalTexture;
        }

        // Create texture for hover state
        $this->currentState = StateEnum::hover;
        $this->computedStyles = $this->hoverStylesCached;
        $hoverTexture = sdl_create_texture(
            $renderer,
            SDL_PIXELFORMAT_RGBA8888,
            SDL_TEXTUREACCESS_TARGET,
            (int) $this->viewport->width,
            (int) $this->viewport->height,
        );

        if ($hoverTexture) {
            sdl_set_texture_blend_mode($hoverTexture, SDL_BLENDMODE_BLEND);
            sdl_set_render_target($renderer, $hoverTexture);
            sdl_set_render_draw_color($renderer, 0, 0, 0, 0);
            sdl_render_clear($renderer);

            $oldCache = $this->useTextureCache;
            $this->useTextureCache = false;

            $oldX = $this->viewport->x;
            $oldY = $this->viewport->y;
            $this->viewport->x = 0;
            $this->viewport->y = 0;

            $this->render($renderer, $textRenderer);
            $this->renderContent($renderer, $textRenderer);

            $this->viewport->x = $oldX;
            $this->viewport->y = $oldY;
            $this->useTextureCache = $oldCache;

            sdl_set_render_target($renderer, null);
            $this->cachedHoverTexture = $hoverTexture;
        }

        // Restore normal state
        $this->currentState = StateEnum::normal;
        $this->computedStyles = $this->normalStylesCached;
        $this->textureCacheValid = true;

        Profiler::end('buildTextureCache');
    }

    public function getCachedTexture()
    {
        return $this->cachedTexture;
    }

    public function setCachedTexture($texture): void
    {
        // Free old texture if exists
        if ($this->cachedTexture !== null) {
            sdl_destroy_texture($this->cachedTexture);
        }
        $this->cachedTexture = $texture;
    }

    public function useTextureCache(): bool
    {
        return $this->useTextureCache;
    }

    public function setUseTextureCache(bool $use): void
    {
        $this->useTextureCache = $use;
        if (!$use) {
            $this->invalidateTextureCache();
        }
    }

    public function markClean(): void
    {
        $this->layoutDirty = false;
        $this->renderDirty = false;

        foreach ($this->children as $child) {
            $child->markClean();
        }
    }

    public function setParent(null|Component $parent): void
    {
        $this->parent = $parent;
    }

    public function getParent(): null|Component
    {
        return $this->parent;
    }

    public function update(): void
    {
        foreach ($this->children as $child) {
            $child->update();
        }
    }

    public function layout(null|TextRenderer $textRenderer = null): void
    {
        $this->normalStylesCached = StyleParser::parse($this->style)->getValidStyles(
            MediaQueryEnum::normal,
            StateEnum::normal,
        );
        $this->hoverStylesCached = StyleParser::parse($this->style)->getValidStyles(
            MediaQueryEnum::normal,
            StateEnum::hover,
        );
        if ($this->currentState == StateEnum::hover) {
            $this->computedStyles = $this->hoverStylesCached;
        } else {
            $this->computedStyles = $this->normalStylesCached;
        }
        if (isset($this->computedStyles[Margin::class]) && ($m = $this->computedStyles[Margin::class])) {
            $this->viewport->x = (int) ($this->viewport->x + $m->left);
            $this->viewport->width = max(0, ($this->viewport->width - $m->right) - $m->left);
            $this->viewport->y = (int) ($this->viewport->y + $m->top);
            $this->viewport->height = max(0, ($this->viewport->height - $m->bottom) - $m->top);
        }

        $this->contentViewport = clone $this->viewport;

        if (isset($this->computedStyles[Padding::class]) && ($p = $this->computedStyles[Padding::class])) {
            $this->contentViewport->x = (int) ($this->contentViewport->x + $p->left);
            $this->contentViewport->width = max(0, ($this->contentViewport->width - $p->right) - $p->left);
            $this->contentViewport->y = (int) ($this->contentViewport->y + $p->top);
            $this->contentViewport->height = max(0, ($this->contentViewport->height - $p->bottom) - $p->top);
        }

        if ($this->useTextureCache) {
            $this->invalidateTextureCache();
        }

        $this->layoutDirty = false;
        $this->renderDirty = true;
    }

    public function render(&$renderer, null|TextRenderer $textRenderer = null): void
    {
        if (!$this->visible) {
            return;
        }

        Profiler::start('render');

        // Use cached texture if available and valid
        if ($this->useTextureCache) {
            Profiler::increment('uses_cache');
            if ($this->textureCacheValid) {
                Profiler::increment('cache_valid');
                $texture = $this->currentState == StateEnum::hover ? $this->cachedHoverTexture : $this->cachedTexture;

                if ($texture !== null) {
                    Profiler::increment('texture_cache_hit');
                    Profiler::start('render_cached');
                    // Render cached texture

                    sdl_render_texture($renderer, $texture, [
                        'x' => $this->viewport->x,
                        'y' => $this->viewport->y,
                        'w' => $this->viewport->width,
                        'h' => $this->viewport->height,
                    ]);
                    Profiler::end('render_cached');
                    Profiler::end('render');
                    return;
                } else {
                    Profiler::increment('texture_null');
                }
            } else {
                Profiler::increment('cache_invalid');
            }
        }

        Profiler::increment('render_normal');

        // Render normally if no cache or cache building
        if (
            isset($this->computedStyles[\PHPNative\Tailwind\Style\Background::class]) &&
                ($bg = $this->computedStyles[\PHPNative\Tailwind\Style\Background::class])
        ) {
            if ($this->currentState == StateEnum::hover) {
                sdl_set_render_draw_color($renderer, $bg->color->red, $bg->color->green, $bg->color->blue, 10);
            } else {
                sdl_set_render_draw_color(
                    $renderer,
                    $bg->color->red,
                    $bg->color->green,
                    $bg->color->blue,
                    $bg->color->alpha,
                );
            }

            if (
                isset($this->computedStyles[\PHPNative\Tailwind\Style\Border::class]) &&
                    ($border = $this->computedStyles[\PHPNative\Tailwind\Style\Border::class])
            ) {
                // SDL3: sdl_rounded_box_ex uses (x1, y1, x2, y2) instead of (x, y, w, h)
                $x2 = $this->viewport->x + $this->viewport->width;
                $y2 = $this->viewport->y + $this->viewport->height;

                sdl_rounded_box_ex(
                    $renderer,
                    (int) $this->viewport->x,
                    (int) $this->viewport->y,
                    (int) $x2,
                    (int) $y2,
                    $border->roundTopLeft ?? 0,
                    $border->roundTopRight ?? 0,
                    $border->roundBottomRight ?? 0,
                    $border->roundBottomLeft ?? 0,
                    $bg->color->red,
                    $bg->color->green,
                    $bg->color->blue,
                    $bg->color->alpha,
                );
            } else {
                sdl_render_fill_rect($renderer, [
                    'x' => $this->viewport->x,
                    'y' => $this->viewport->y,
                    'w' => $this->viewport->width,
                    'h' => $this->viewport->height,
                ]);
            }
        }

        // Optional border stroke (separate von der Hintergrundfüllung)
        if (isset($this->computedStyles[\PHPNative\Tailwind\Style\Border::class])) {
            /** @var \PHPNative\Tailwind\Style\Border $border */
            $border = $this->computedStyles[\PHPNative\Tailwind\Style\Border::class];

            $top = (int) ($border->top ?? 0);
            $bottom = (int) ($border->bottom ?? 0);
            $left = (int) ($border->left ?? 0);
            $right = (int) ($border->right ?? 0);

            $hasStroke = $border->enabled || $top > 0 || $bottom > 0 || $left > 0 || $right > 0;

            if ($hasStroke) {
                $color = $border->color;
                // Fallback-Farbe, wenn keine explizite gesetzt ist
                $red = ($color->red >= 0) ? $color->red : 160;
                $green = ($color->green >= 0) ? $color->green : 160;
                $blue = ($color->blue >= 0) ? $color->blue : 160;
                $alpha = $color->alpha;

                sdl_set_render_draw_color($renderer, $red, $green, $blue, $alpha);

                $x = (int) $this->viewport->x;
                $y = (int) $this->viewport->y;
                $w = (int) $this->viewport->width;
                $h = (int) $this->viewport->height;

                // Obere Kante
                if ($top > 0) {
                    sdl_render_fill_rect($renderer, [
                        'x' => $x,
                        'y' => $y,
                        'w' => $w,
                        'h' => $top,
                    ]);
                }

                // Untere Kante
                if ($bottom > 0) {
                    sdl_render_fill_rect($renderer, [
                        'x' => $x,
                        'y' => $y + $h - $bottom,
                        'w' => $w,
                        'h' => $bottom,
                    ]);
                }

                // Linke Kante
                if ($left > 0) {
                    sdl_render_fill_rect($renderer, [
                        'x' => $x,
                        'y' => $y,
                        'w' => $left,
                        'h' => $h,
                    ]);
                }

                // Rechte Kante
                if ($right > 0) {
                    sdl_render_fill_rect($renderer, [
                        'x' => $x + $w - $right,
                        'y' => $y,
                        'w' => $right,
                        'h' => $h,
                    ]);
                }
            }
        }
        if (defined('DEBUG_RENDERING') && DEBUG_RENDERING) {
            sdl_set_render_draw_color($renderer, rand(0, 255), rand(0, 255), rand(0, 255), 10);
            sdl_render_rect($renderer, [
                'x' => $this->viewport->x,
                'y' => $this->viewport->y,
                'w' => $this->viewport->width,
                'h' => $this->viewport->height,
            ]);
        }

        Profiler::end('render');
    }

    public function renderContent(&$renderer, null|TextRenderer $textRenderer = null): void
    {
        if (!$this->visible) {
            return;
        }

        // Check if we need to clip children based on overflow
        $needsClipping = false;
        $clipRect = null;

        if (isset($this->computedStyles[\PHPNative\Tailwind\Style\Overflow::class])) {
            $overflow = $this->computedStyles[\PHPNative\Tailwind\Style\Overflow::class];
            // Enable clipping for hidden, clip, scroll, or auto overflow
            if (
                $overflow->x !== \PHPNative\Tailwind\Style\OverflowEnum::hidden ||
                    $overflow->y !== \PHPNative\Tailwind\Style\OverflowEnum::hidden
            ) {
                // Actually, let's clip for anything that's not the default
                $needsClipping = true;
                $clipRect = [
                    'x' => (int) $this->contentViewport->x,
                    'y' => (int) $this->contentViewport->y,
                    'w' => (int) $this->contentViewport->width,
                    'h' => (int) $this->contentViewport->height,
                ];
            }
        }

        // Set clip rect if needed
        if ($needsClipping && $clipRect) {
            sdl_set_render_clip_rect($renderer, $clipRect);
        }

        // Render children (unless we're using texture cache and it's valid)
        if (!($this->useTextureCache && $this->textureCacheValid)) {
            foreach ($this->children as $child) {
                // Skip rendering children that are completely outside the clip rect
                if ($needsClipping && $clipRect) {
                    $childViewport = $child->getViewport();
                    // Check if child is visible within clip rect
                    if (
                        ($childViewport->x + $childViewport->width) < $clipRect['x'] ||
                            $childViewport->x > ($clipRect['x'] + $clipRect['w']) ||
                            ($childViewport->y + $childViewport->height) < $clipRect['y'] ||
                            $childViewport->y > ($clipRect['y'] + $clipRect['h'])
                    ) {
                        continue; // Child is completely outside clip rect, skip rendering
                    }
                }

                $child->render($renderer, $textRenderer);
                $child->renderContent($renderer, $textRenderer);
            }
        }

        // Reset clip rect
        if ($needsClipping) {
            sdl_set_render_clip_rect($renderer, null);
        }
    }

    /**
     * Build texture cache for this component and all children that have caching enabled
     */
    public function buildTextureCacheRecursive(&$renderer, null|TextRenderer $textRenderer = null): void
    {
        // Build cache for this component if needed
        if ($this->useTextureCache && !$this->textureCacheValid) {
            $this->buildTextureCache($renderer, $textRenderer);
        }

        // Recursively build cache for children
        foreach ($this->children as $child) {
            $child->buildTextureCacheRecursive($renderer, $textRenderer);
        }
    }

    public function addComponent(Component $component): void
    {
        $this->children[] = $component;
        $component->setParent($this);
        if ($this->attachedWindow !== null) {
            $component->attachToWindow($this->attachedWindow);
        }
        $this->markDirty(true); // Adding a child means we need to re-layout and re-render
    }

    /**
     * Collect all overlays recursively from this component and all children
     * @return array Array of overlay components
     */
    public function collectOverlays(): array
    {
        $overlays = [];

        foreach ($this->children as $child) {
            if ($child->isOverlay()) {
                $overlays[] = $child;
            }

            // Recursively collect from children
            $overlays = array_merge($overlays, $child->collectOverlays());
        }

        return $overlays;
    }

    /**
     * Handle mouse click event
     * @return bool True if event was handled
     */
    public function handleMouseClick(float $mouseX, float $mouseY, int $button): bool
    {
        // Don't handle events if component is not visible
        if (!$this->visible) {
            return false;
        }

        // Default implementation: propagate to children
        foreach ($this->children as $child) {
            if ($child->handleMouseClick($mouseX, $mouseY, $button)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Handle mouse move event
     */
    public function handleMouseMove(float $mouseX, float $mouseY): void
    {
        Profiler::start('handleMouseMove');
        Profiler::increment('mouse_move_events');

        // Check if mouse is over this component
        $isMouseOver =
            $mouseX >= $this->viewport->x &&
            $mouseX <= ($this->viewport->x + $this->viewport->width) &&
            $mouseY >= $this->viewport->y &&
            $mouseY <= ($this->viewport->y + $this->viewport->height);

        // Update state based on mouse position
        $previousState = $this->currentState;
        $this->currentState = $isMouseOver ? StateEnum::hover : StateEnum::normal;

        // Recompute styles if state changed
        if ($previousState !== $this->currentState) {
            Profiler::increment('state_changes');
            if ($this->currentState == StateEnum::hover) {
                $this->computedStyles = $this->hoverStylesCached;
            } else {
                $this->computedStyles = $this->normalStylesCached;
            }

            // Only mark dirty if we're NOT using texture cache
            // If we use texture cache, we already have both states cached
            if (!$this->useTextureCache) {
                $this->markDirty(false, false);
            }
        }

        // Only propagate to children if mouse is over this component or we need to clear hover states
        if ($isMouseOver || $previousState === StateEnum::hover) {
            // Check if we have overflow clipping
            $hasClipping = isset($this->computedStyles[\PHPNative\Tailwind\Style\Overflow::class]);
            $clipRect = null;

            if ($hasClipping) {
                $overflow = $this->computedStyles[\PHPNative\Tailwind\Style\Overflow::class];
                if (
                    $overflow->x !== \PHPNative\Tailwind\Style\OverflowEnum::hidden ||
                        $overflow->y !== \PHPNative\Tailwind\Style\OverflowEnum::hidden
                ) {
                    $clipRect = [
                        'x' => $this->contentViewport->x,
                        'y' => $this->contentViewport->y,
                        'w' => $this->contentViewport->width,
                        'h' => $this->contentViewport->height,
                    ];
                }
            }

            foreach ($this->children as $child) {
                // Skip overlays - they are handled separately by the Window
                if (!$child->isOverlay() && $child->isVisible()) {
                    // OPTIMIZATION: Skip propagation if child has no hover styles
                    // This dramatically reduces event processing for components like Labels
                    if (!$child->hasHoverStyles()) {
                        continue;
                    }

                    $childViewport = $child->getViewport();

                    // Skip if child viewport is invalid
                    if ($childViewport->width <= 0 || $childViewport->height <= 0) {
                        continue;
                    }

                    // If we have clipping, check if child is visible within clip rect
                    if ($clipRect !== null) {
                        // Skip children that are completely outside the clip rect
                        if (
                            ($childViewport->x + $childViewport->width) < $clipRect['x'] ||
                                $childViewport->x > ($clipRect['x'] + $clipRect['w']) ||
                                ($childViewport->y + $childViewport->height) < $clipRect['y'] ||
                                $childViewport->y > ($clipRect['y'] + $clipRect['h'])
                        ) {
                            // Child is outside visible area, send fake event to clear hover
                            $child->handleMouseMove(-1000, -1000);
                            continue;
                        }
                    }

                    $child->handleMouseMove($mouseX, $mouseY);
                }
            }
        }

        Profiler::end('handleMouseMove');
    }

    /**
     * Handle mouse button release event
     */
    public function handleMouseRelease(float $mouseX, float $mouseY, int $button): void
    {
        // Default implementation: propagate to children
        foreach ($this->children as $child) {
            $child->handleMouseRelease($mouseX, $mouseY, $button);
        }
    }

    /**
     * Handle mouse wheel event
     * @return bool True if event was handled
     */
    public function handleMouseWheel(float $mouseX, float $mouseY, float $deltaY): bool
    {
        // Don't handle events if component is not visible
        if (!$this->visible) {
            return false;
        }

        // Default implementation: propagate to children
        foreach ($this->children as $child) {
            if ($child->handleMouseWheel($mouseX, $mouseY, $deltaY)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Handle text input event
     * @param string $text The input text
     */
    public function handleTextInput(string $text): void
    {
        // Default implementation: propagate to children
        foreach ($this->children as $child) {
            $child->handleTextInput($text);
        }
    }

    /**
     * Handle key down event
     * @param int $keycode SDL keycode
     * @return bool True if event was handled
     */
    public function handleKeyDown(int $keycode, int $mod = 0): bool
    {
        // Default implementation: propagate to children
        foreach ($this->children as $child) {
            if ($child->handleKeyDown($keycode, $mod)) {
                return true;
            }
        }
        return false;
    }

    public function needsLayout(): bool
    {
        return $this->layoutDirty;
    }

    public function isDirty(): bool
    {
        return $this->renderDirty;
    }

    public function markDirty(bool $requiresLayout = false, bool $bubble = true): void
    {
        if ($requiresLayout) {
            $this->layoutDirty = true;
            $this->renderDirty = true;
            $this->invalidateTextureCache();

            if ($this->attachedWindow !== null) {
                $this->attachedWindow->setShouldBeReLayouted(true);
            }
        } else {
            $this->renderDirty = true;
        }

        if ($bubble && $this->parent !== null) {
            $this->parent->markDirty($requiresLayout);
            return;
        }

        if (!$bubble && !$requiresLayout) {
            $ancestor = $this->parent;
            while ($ancestor !== null && $ancestor->useTextureCache()) {
                $ancestor->renderDirty = true;
                $ancestor->invalidateTextureCache();
                $ancestor = $ancestor->getParent();
            }
        }
    }

    public function attachToWindow(Window $window): void
    {
        $this->attachedWindow = $window;

        foreach ($this->children as $child) {
            $child->attachToWindow($window);
        }
    }

    /**
     * Check if this component or any of its children have hover styles
     * This is cached for performance
     */
    public function hasHoverStyles(): bool
    {
        if ($this->hasHoverStylesCache !== null) {
            return $this->hasHoverStylesCache;
        }

        // Check if this component has hover styles
        if (str_contains($this->style, ':hover') || str_contains($this->style, 'hover:')) {
            $this->hasHoverStylesCache = true;
            return true;
        }

        // Check if any children have hover styles
        foreach ($this->children as $child) {
            if ($child->hasHoverStyles()) {
                $this->hasHoverStylesCache = true;
                return true;
            }
        }

        $this->hasHoverStylesCache = false;
        return false;
    }

    public function detachFromWindow(): void
    {
        $this->attachedWindow = null;

        foreach ($this->children as $child) {
            $child->detachFromWindow();
        }
    }

    /**
     * @return array<Component>
     */
    public function getChildren(): array
    {
        return $this->children;
    }

    /**
     * Render shadow if this component has one
     * Called by parent container before rendering the component itself
     */
    public function renderShadowIfPresent(&$renderer): void
    {
        if (isset($this->computedStyles[\PHPNative\Tailwind\Style\Shadow::class])) {
            $shadow = $this->computedStyles[\PHPNative\Tailwind\Style\Shadow::class];
            if ($shadow instanceof \PHPNative\Tailwind\Style\Shadow) {
                $this->renderShadow($renderer, $shadow);
            }
        }
    }

    /**
     * Box blur algorithm for the alpha channel of the shadow texture
     * Keeps RGB constant so tinted shadows don't desaturate while fading out.
     */
    private function boxBlurAlpha(array &$alphaMap, int $width, int $height, int $radius): void
    {
        if ($radius <= 0) {
            return;
        }

        $temp = array_fill(0, $width * $height, 0);

        for ($y = 0; $y < $height; $y++) {
            for ($x = 0; $x < $width; $x++) {
                $alphaSum = 0;
                $count = 0;

                for ($dy = -$radius; $dy <= $radius; $dy++) {
                    $ny = $y + $dy;
                    if ($ny < 0 || $ny >= $height) {
                        continue;
                    }
                    $rowOffset = $ny * $width;

                    for ($dx = -$radius; $dx <= $radius; $dx++) {
                        $nx = $x + $dx;
                        if ($nx < 0 || $nx >= $width) {
                            continue;
                        }

                        $alphaSum += $alphaMap[$rowOffset + $nx];
                        $count++;
                    }
                }

                $temp[($y * $width) + $x] = $count > 0 ? ((int) ($alphaSum / $count)) : 0;
            }
        }

        $alphaMap = $temp;
    }

    /**
     * Create a blurred shadow texture
     */
    private function createShadowTexture(
        &$renderer,
        int $width,
        int $height,
        int $blurRadius,
        int $alpha,
        int $r = 0,
        int $g = 0,
        int $b = 0,
    ): mixed {
        if ($width <= 0 || $height <= 0) {
            return null;
        }

        // If the optimized C helper is available, use it.
        if (function_exists('sdl_create_box_shadow_texture')) {
            return sdl_create_box_shadow_texture(
                $renderer,
                $width,
                $height,
                $blurRadius,
                $alpha,
                $r,
                $g,
                $b,
            );
        }

        // Fallback: existing PHP implementation

        // Create alpha map (single channel) - start with transparent
        $alphaMap = array_fill(0, $width * $height, 0);

        // Fill only the inner rectangle with semi-transparent color
        // Leave a border for the blur to spread into
        $margin = $blurRadius + 2;
        for ($y = $margin; $y < ($height - $margin); $y++) {
            for ($x = $margin; $x < ($width - $margin); $x++) {
                $alphaMap[($y * $width) + $x] = $alpha;
            }
        }

        // Apply box blur on alpha to spread transparency without washing out color
        $this->boxBlurAlpha($alphaMap, $width, $height, $blurRadius);

        // Build final pixel buffer with constant RGB and varying alpha
        $pixels = [];
        $totalPixels = $width * $height;
        for ($i = 0; $i < $totalPixels; $i++) {
            $pixelAlpha = $alphaMap[$i];
            $pixels[$i] = ($pixelAlpha << 24) | ($r << 16) | ($g << 8) | $b;
        }

        // Create texture
        $texture = sdl_create_texture($renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, $width, $height);

        if (!$texture) {
            return null;
        }

        // Update texture with pixel data
        sdl_update_texture($texture, null, $pixels, $width * 4);

        // Set blend mode for alpha blending
        sdl_set_texture_blend_mode($texture, SDL_BLENDMODE_BLEND);

        return $texture;
    }

    /**
     * Render shadow effect for the component using texture-based approach
     */
    private function renderShadow(&$renderer, \PHPNative\Tailwind\Style\Shadow $shadow): void
    {
        if ($shadow->size === 'none') {
            return;
        }

        // Define shadow properties based on size
        // Alpha values much lower for transparent shadows (0-255 range)
        $shadowProps = match ($shadow->size) {
            'sm' => ['blur' => 2, 'offsetX' => 0, 'offsetY' => 1, 'alpha' => 25],
            'base' => ['blur' => 3, 'offsetX' => 0, 'offsetY' => 2, 'alpha' => 35],
            'md' => ['blur' => 4, 'offsetX' => 0, 'offsetY' => 4, 'alpha' => 45],
            'lg' => ['blur' => 6, 'offsetX' => 0, 'offsetY' => 10, 'alpha' => 60],
            'xl' => ['blur' => 10, 'offsetX' => 0, 'offsetY' => 20, 'alpha' => 75],
            '2xl' => ['blur' => 15, 'offsetX' => 0, 'offsetY' => 25, 'alpha' => 90],
            'inner' => ['blur' => 3, 'offsetX' => 0, 'offsetY' => 0, 'alpha' => 35, 'inner' => true],
            default => ['blur' => 3, 'offsetX' => 0, 'offsetY' => 2, 'alpha' => 35],
        };

        // Apply opacity modifier if specified (0-100 range -> 0-255 range)
        $alpha = $shadowProps['alpha'];
        if ($shadow->opacity !== null) {
            // Convert percentage to 0-255 range and apply to base alpha
            $alpha = (int) (($shadow->opacity / 100) * 255);
        }

        // Get shadow color (default to black if not specified)
        $r = $shadow->color->red >= 0 ? $shadow->color->red : 0;
        $g = $shadow->color->green >= 0 ? $shadow->color->green : 0;
        $b = $shadow->color->blue >= 0 ? $shadow->color->blue : 0;

        // Choose cache based on current state (normal vs hover)
        $isHover = $this->currentState === \PHPNative\Tailwind\Style\StateEnum::hover;
        $cacheTexture = $isHover ? $this->cachedShadowHoverTexture : $this->cachedShadowTexture;
        $cacheValid = $isHover ? $this->shadowHoverCacheValid : $this->shadowCacheValid;

        if ($cacheValid && $cacheTexture !== null) {
            $shadowTexture = $cacheTexture;
        } else {
            // Create shadow texture with blur based on current state's styles
            $shadowTexture = $this->createShadowTexture(
                $renderer,
                (int) $this->viewport->width,
                (int) $this->viewport->height,
                $shadowProps['blur'],
                $alpha,
                $r,
                $g,
                $b,
            );

            if (!$shadowTexture) {
                return;
            }

            // Cache per state
            if ($isHover) {
                $this->cachedShadowHoverTexture = $shadowTexture;
                $this->shadowHoverCacheValid = true;
            } else {
                $this->cachedShadowTexture = $shadowTexture;
                $this->shadowCacheValid = true;
            }
        }

        // Render shadow texture with offset
        $shadowRect = [
            'x' => $this->viewport->x + $shadowProps['offsetX'],
            'y' => $this->viewport->y + $shadowProps['offsetY'],
            'w' => $this->viewport->width,
            'h' => $this->viewport->height,
        ];

        sdl_render_texture($renderer, $shadowTexture, $shadowRect);

        // Clean up texture only if it's not cached
        // If cached, it will be destroyed in invalidateTextureCache()
    }
}
