<?php
/**
 * TamRank WebP Server Output
 *
 * Optioneel: wrap wp_get_attachment_image output in <picture> met WebP source.
 */

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

class TamRank_WebP_Server {
    public function __construct() {
        // Template / API images (featured images via the_post_thumbnail,
        // galleries, theme calls to wp_get_attachment_image()).
        add_filter('wp_get_attachment_image', array($this, 'filter_attachment_image'), 10, 5);

        // Content images: Gutenberg image blocks (and classic/page-builder
        // images) are stored as raw <img> in post_content and never pass
        // through wp_get_attachment_image. WP 6.0+'s wp_content_img_tag is the
        // per-<img> seam for those, so this is where the bulk of real frontend
        // images actually get a WebP source.
        add_filter('wp_content_img_tag', array($this, 'filter_content_img_tag'), 20, 1);
    }

    /**
     * Wrap a single content <img> in <picture> with a WebP <source> when a
     * .webp twin exists on disk. The original <img> stays as the fallback, so
     * browsers without WebP support (or images without a twin) are unaffected.
     */
    public function filter_content_img_tag($filtered_image) {
        $settings = tamrank_webp_get_settings();
        if (empty($settings['picture_output'])) {
            return $filtered_image;
        }
        // Skip when there is no <img>, it is already inside a <picture>, or it
        // was already wrapped in an earlier filter pass. The content-tag filter
        // can run more than once per render (e.g. in block themes) and on the
        // second pass it only sees the bare inner <img>, so we mark it on wrap.
        if (stripos($filtered_image, '<img') === false
            || stripos($filtered_image, '<picture') !== false
            || strpos($filtered_image, 'data-tamrank-webp') !== false) {
            return $filtered_image;
        }
        if (!preg_match('/\ssrc=["\']([^"\']+)["\']/i', $filtered_image, $m)) {
            return $filtered_image;
        }

        $uploads  = wp_get_upload_dir();
        $webp_src = $this->maybe_get_webp_url($m[1], $uploads);
        if (!$webp_src) {
            return $filtered_image;
        }

        // Prefer a full WebP srcset (so each responsive size maps to its twin);
        // fall back to the single WebP src when there is no srcset.
        $source_attrs = '';
        if (preg_match('/\ssrcset=["\']([^"\']+)["\']/i', $filtered_image, $ss)) {
            $webp_srcset = $this->build_webp_srcset($ss[1], $uploads);
            if ($webp_srcset) {
                $source_attrs = ' srcset="' . esc_attr($webp_srcset) . '"';
                if (preg_match('/\ssizes=["\']([^"\']+)["\']/i', $filtered_image, $sz)) {
                    $source_attrs .= ' sizes="' . esc_attr($sz[1]) . '"';
                }
            }
        }
        if ($source_attrs === '') {
            $source_attrs = ' srcset="' . esc_url($webp_src) . '"';
        }

        return '<picture><source type="image/webp"' . $source_attrs . '>' . $this->mark_img($filtered_image) . '</picture>';
    }

    /**
     * Tag the <img> so a later filter pass over already-wrapped output does not
     * wrap it a second time (idempotency marker; survives re-extraction of the
     * bare <img> from inside our <picture>).
     */
    private function mark_img($html) {
        return preg_replace('/<img(\s)/i', '<img data-tamrank-webp="1"$1', $html, 1);
    }

    /**
     * Rebuild a srcset string with WebP twins. If ANY candidate lacks a twin we
     * bail out entirely (return false) rather than emit a WebP <source> with a
     * smaller candidate set than the <img> fallback — otherwise the browser could
     * pick a lower-resolution WebP than the original JPEG/PNG path would serve.
     */
    private function build_webp_srcset($srcset, $uploads) {
        $out = array();
        foreach (explode(',', $srcset) as $candidate) {
            $candidate = trim($candidate);
            if ($candidate === '') {
                continue;
            }
            $parts      = preg_split('/\s+/', $candidate, 2);
            $webp       = $this->maybe_get_webp_url($parts[0], $uploads);
            if (!$webp) {
                // Missing twin: don't offer a strictly smaller WebP candidate set
                // than the <img> fallback — leave the original responsive srcset.
                return false;
            }
            $descriptor = isset($parts[1]) ? ' ' . $parts[1] : '';
            $out[]      = esc_url_raw($webp) . $descriptor;
        }
        return empty($out) ? false : implode(', ', $out);
    }

    public function filter_attachment_image($html, $attachment_id, $size, $icon, $attr) {
        $settings = tamrank_webp_get_settings();
        if (empty($settings['picture_output'])) {
            return $html;
        }

        $uploads = wp_get_upload_dir();
        $src = wp_get_attachment_image_src($attachment_id, $size);
        if (!$src || empty($src[0])) {
            return $html;
        }

        $url = $src[0];
        $webp_url = $this->maybe_get_webp_url($url, $uploads);
        if (!$webp_url) {
            return $html;
        }

        // Offer the full responsive WebP srcset so each size maps to its twin
        // (matches the content path); fall back to the single WebP url.
        $source_attrs = '';
        $srcset = wp_get_attachment_image_srcset($attachment_id, $size);
        if ($srcset) {
            $webp_srcset = $this->build_webp_srcset($srcset, $uploads);
            if ($webp_srcset) {
                $source_attrs = ' srcset="' . esc_attr($webp_srcset) . '"';
                $sizes_attr = wp_get_attachment_image_sizes($attachment_id, $size);
                if ($sizes_attr) {
                    $source_attrs .= ' sizes="' . esc_attr($sizes_attr) . '"';
                }
            }
        }
        if ($source_attrs === '') {
            $source_attrs = ' srcset="' . esc_url($webp_url) . '"';
        }

        return '<picture><source type="image/webp"' . $source_attrs . '>' . $this->mark_img($html) . '</picture>';
    }

    private function maybe_get_webp_url($url, $uploads) {
        // Vervang extensie door .webp als bestand bestaat
        $ext = pathinfo($url, PATHINFO_EXTENSION);
        // No extension, or the source is already WebP — nothing to swap.
        if (!$ext || strtolower($ext) === 'webp') {
            return false;
        }

        // Genereer WebP URL door extensie te vervangen
        $webp_url = preg_replace('/\.' . preg_quote($ext, '/') . '$/', '.webp', $url);

        // Converteer URL naar file path
        $webp_path = str_replace($uploads['baseurl'], $uploads['basedir'], $webp_url);

        // Controleer of WebP bestand bestaat
        if (file_exists($webp_path)) {
            return $webp_url;
        }

        return false;
    }
}
