<?php
declare(strict_types=1);

namespace Instinctor\Helpers;

if ( ! defined( 'ABSPATH' ) ) { exit; }

/**
 * Directional shadow filter builder — emits the SVG <filter> + CSS that
 * paints a soft shadow OUTSIDE the silhouette on any subset of the four
 * sides (top / right / bottom / left). Composes one feFlood + offset +
 * feGaussianBlur chain per active side, then feMerge into a single
 * filter that the wrapper consumes via filter:url(#...).
 *
 * Reads four per-side flags + Depth (strength) + Soft (blur) keys with
 * the caller-supplied prefix — image renders 'image_shadow_*', video
 * renders 'video_shadow_*'. Same engine, same data shape, namespaced.
 *
 * Extracted from element-renderers/image.php in v2.1.1305 so the video
 * block (and any future block that wants the directional-shadow UX)
 * can reuse it without duplicating the SVG primitive chain.
 */
class ShadowFilter
{
    /**
     * Build the filter SVG + CSS for a wrapper.
     *
     * @param array  $settings  Element settings (flat, post device-resolve).
     * @param int    $elId      Element id — used to namespace the filter id.
     * @param string $keyPrefix Setting key prefix, e.g. 'image_shadow_'.
     *
     * @return array{css: string, svg: string} Empty strings when no
     *         side is active or strength is 0. Caller appends css to
     *         the wrapper style and echoes svg before the wrapper.
     */
    public static function build(array $settings, int $elId, string $keyPrefix = 'image_shadow_'): array
    {
        $isOn = static function ($v): bool {
            if ($v === null || $v === '') return false;
            return !($v === '0' || $v === 0 || $v === false);
        };

        $shTop    = $isOn($settings[$keyPrefix . 'top']    ?? null);
        $shRight  = $isOn($settings[$keyPrefix . 'right']  ?? null);
        $shBottom = $isOn($settings[$keyPrefix . 'bottom'] ?? null);
        $shLeft   = $isOn($settings[$keyPrefix . 'left']   ?? null);

        // Back-compat: read the legacy single-side key when no per-side
        // flags are set. Image used 'image_shadow_side' = top|right|...
        // Video has no legacy key but the fallback is harmless.
        if (!$shTop && !$shRight && !$shBottom && !$shLeft) {
            $legacySide = (string) ($settings[$keyPrefix . 'side'] ?? '');
            if ($legacySide === 'top')    $shTop    = true;
            if ($legacySide === 'right')  $shRight  = true;
            if ($legacySide === 'bottom') $shBottom = true;
            if ($legacySide === 'left')   $shLeft   = true;
        }

        $strength = max(0, min(100, intval($settings[$keyPrefix . 'strength'] ?? 40)));
        $blur     = max(0, min(50,  intval($settings[$keyPrefix . 'blur']     ?? 12)));

        $activeSides = array_filter([
            'top'    => $shTop,
            'right'  => $shRight,
            'bottom' => $shBottom,
            'left'   => $shLeft,
        ]);
        if (empty($activeSides) || $strength === 0) {
            return ['css' => '', 'svg' => ''];
        }

        // Filter id is namespaced by prefix so an image + video on the
        // same page never collide.
        $filterId = 'impulse-' . preg_replace('/_+$/', '', $keyPrefix) . '-' . $elId;

        $offset   = max(4, intval(round($blur * 1.5)));
        $floodOp  = number_format($strength / 100, 3, '.', '');

        // feFlood + feComposite-out vs SourceAlpha builds the silhouette
        // "outside" mask in black at the requested opacity. Each side
        // then offsets, blurs, and feComposite-in back to silhouette
        // edge — feeding into a final feMerge that paints the original
        // graphic on top of every side's shadow contribution.
        $chains = '<feFlood flood-color="black" flood-opacity="' . $floodOp . '" result="flood"/>'
                . '<feComposite in="flood" in2="SourceAlpha" operator="out" result="outside"/>';
        $mergeNodes = '<feMergeNode in="SourceGraphic"/>';
        $dir = [
            'top'    => [0,  $offset],
            'bottom' => [0, -$offset],
            'left'   => [ $offset, 0],
            'right'  => [-$offset, 0],
        ];
        $n = 0;
        foreach ($activeSides as $side => $_) {
            [$dx, $dy] = $dir[$side];
            $n++;
            $chains .= '<feOffset in="outside" dx="' . $dx . '" dy="' . $dy . '" result="shifted' . $n . '"/>'
                     . '<feGaussianBlur in="shifted' . $n . '" stdDeviation="' . $blur . '" result="blurred' . $n . '"/>'
                     . '<feComposite in="blurred' . $n . '" in2="SourceAlpha" operator="in" result="shadow' . $n . '"/>';
            $mergeNodes .= '<feMergeNode in="shadow' . $n . '"/>';
        }

        $svg = '<svg width="0" height="0" style="position:absolute;width:0;height:0;overflow:hidden" aria-hidden="true">'
             . '<filter id="' . esc_attr($filterId) . '" x="-20%" y="-20%" width="140%" height="140%">'
                 . $chains
                 . '<feMerge>' . $mergeNodes . '</feMerge>'
             . '</filter></svg>';
        $css = 'filter:url(#' . esc_attr($filterId) . ');';

        return ['css' => $css, 'svg' => $svg];
    }
}
