<?php
/**
 * WS4: RSL (Really Simple Licensing) export with the Encypher provenance
 * extension.
 *
 * Emits an RSL 1.0 document that identifies signed content and its verifiable
 * provenance. Rights selections stored by WordPress are editor input, not
 * trusted public license terms, so this exporter does not project them.
 *
 * Two surfaces:
 *   - Site-wide feed at /.well-known/rsl.xml listing recently-signed content.
 *   - Per-post discovery via a <link rel="license" type="application/rsl+xml">
 *     tag in the document <head> pointing at the post's RSL fragment.
 *
 * The <provenance> element carries:
 *   verify      = the public verification endpoint
 *   type        = "c2pa watermark" (C2PA wrapper + VS256-RS granular markers)
 *   granularity = document | sentence (sentence when per-segment markers exist)
 *   embedded    = true (provenance travels inside the content)
 *
 * @package EncypherProvenance
 */

namespace EncypherProvenance;

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

class RslExport
{
    const FEED_PATH = '/.well-known/rsl.xml';
    const RSL_NS    = 'https://rslstandard.org/rsl';

    /**
     * Whether public text provenance is a complete commit for the content
     * WordPress serves now.
     */
    public static function has_current_committed_text_provenance(int $post_id): bool
    {
        if (
            '' !== (string) get_post_meta($post_id, '_encypher_provenance_generation_pending', true)
            || ! get_post_meta($post_id, '_encypher_marked', true)
        ) {
            return false;
        }
        $signed_text = get_post_meta($post_id, '_encypher_signed_text', true);
        $status = (string) get_post_meta($post_id, '_encypher_provenance_status', true);
        $committed_hash = strtolower(trim((string) get_post_meta($post_id, '_encypher_provenance_cached_content_hash', true)));
        $post = get_post($post_id);
        if (
            ! is_string($signed_text)
            || '' === $signed_text
            || ! in_array($status, ['c2pa_protected', 'c2pa_verified', 'signed', 'verified'], true)
            || 1 !== preg_match('/\A[0-9a-f]{32}\z/', $committed_hash)
            || ! $post
        ) {
            return false;
        }
        return hash_equals($committed_hash, md5((string) $post->post_content));
    }
    /**
     * Whether a post may be exposed through a public RSL surface.
     */
    private static function is_public_rsl_post(int $post_id): bool
    {
        $post = get_post($post_id);
        return $post
            && 'publish' === (string) $post->post_status
            && is_post_publicly_viewable($post)
            && '' === (string) $post->post_password;
    }


    public function register_hooks(): void
    {
        add_action('init', [$this, 'maybe_serve_feed'], 1);
        add_action('wp_head', [$this, 'output_post_license_link']);
        add_filter('query_vars', [$this, 'register_query_vars']);
        add_action('template_redirect', [$this, 'maybe_serve_post_rsl']);
    }

    public function register_query_vars(array $vars): array
    {
        $vars[] = 'encypher_rsl_post';
        return $vars;
    }

    /**
     * Serve the site-wide RSL feed at /.well-known/rsl.xml.
     */
    public function maybe_serve_feed(): void
    {
        $request_uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '';
        if ('' === $request_uri) {
            return;
        }
        $path = (string) wp_parse_url($request_uri, PHP_URL_PATH);
        if ($path !== self::FEED_PATH) {
            return;
        }

        $posts = get_posts([
            'post_type'      => ['post', 'page'],
            'post_status'    => 'publish',
            // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- bounded candidate query; each result is checked against the full current-commit predicate below.
            'meta_key'       => '_encypher_signed_text',
            // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- require a non-empty side-store candidate before full current-commit validation.
            'meta_compare'   => 'EXISTS',
            // phpcs:ignore WordPress.WP.PostsPerPage.posts_per_page_posts_per_page -- bounded to 200; machine-readable RSL feed, not a front-end loop.
            'posts_per_page' => 200,
            'fields'         => 'ids',
            // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.SuppressFilters_suppress_filters -- deterministic feed output; deliberately bypasses query filters.
            'suppress_filters' => true,
        ]);

        $posts = array_values(array_filter(
            array_map('intval', $posts),
            [self::class, 'has_current_committed_text_provenance']
        ));
        header('Content-Type: application/rsl+xml; charset=utf-8');
        echo $this->build_rsl_document($posts); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- trusted RSL XML builder; dynamic values escaped via esc_url/esc_attr inside build_rsl_document().
        exit;
    }

    /**
     * Output a per-post RSL discovery <link> in the document head.
     */
    public function output_post_license_link(): void
    {
        if (! is_singular()) {
            return;
        }
        $post_id = get_the_ID();
        if (
            ! $post_id
            || ! self::is_public_rsl_post((int) $post_id)
            || ! self::has_current_committed_text_provenance((int) $post_id)
        ) {
            return;
        }
        $url = add_query_arg('encypher_rsl_post', $post_id, home_url('/'));
        printf(
            '<link rel="license" type="application/rsl+xml" href="%s" />' . "\n",
            esc_url($url)
        );
    }

    /**
     * Serve a single-post RSL fragment when ?encypher_rsl_post=<id> is set.
     */
    public function maybe_serve_post_rsl(): void
    {
        $post_id = (int) get_query_var('encypher_rsl_post');
        if ($post_id <= 0) {
            return;
        }
        if (
            ! self::is_public_rsl_post($post_id)
            || ! self::has_current_committed_text_provenance($post_id)
        ) {
            status_header(404);
            exit;
        }
        header('Content-Type: application/rsl+xml; charset=utf-8');
        echo $this->build_rsl_document([$post_id]); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- trusted RSL XML builder; dynamic values escaped via esc_url/esc_attr inside build_rsl_document().
        exit;
    }

    /**
     * Build an RSL document for the given signed post ids.
     *
     * @param int[] $post_ids
     */
    public function build_rsl_document(array $post_ids): string
    {
        $verify_base = $this->verification_api_base();

        $lines = [];
        $lines[] = '<?xml version="1.0" encoding="UTF-8"?>';
        $lines[] = '<rsl xmlns="' . esc_attr(self::RSL_NS) . '">';

        foreach ($post_ids as $post_id) {
            $post_id = (int) $post_id;
            if (! self::is_public_rsl_post($post_id)) {
                continue;
            }
            if (! self::has_current_committed_text_provenance($post_id)) {
                continue;
            }
            $permalink = get_permalink($post_id);
            if (! $permalink) {
                continue;
            }

            $total_sentences = (int) get_post_meta($post_id, '_encypher_provenance_total_sentences', true);
            $granularity     = $total_sentences > 0 ? 'sentence' : 'document';
            $instance_id     = (string) get_post_meta($post_id, '_encypher_provenance_instance_id', true);
            $verify_url      = $instance_id && $verify_base
                ? trailingslashit($verify_base) . rawurlencode($instance_id)
                : (string) get_post_meta($post_id, '_encypher_provenance_verification_url', true);

            $lines[] = '  <content url="' . esc_url($permalink) . '">';


            // Encypher provenance extension element.
            $lines[] = sprintf(
                '    <provenance verify="%s" type="c2pa watermark" granularity="%s" embedded="true" />',
                esc_attr((string) $verify_url),
                esc_attr($granularity)
            );

            $lines[] = '  </content>';
        }

        $lines[] = '</rsl>';
        return implode("\n", $lines) . "\n";
    }


    /**
     * Public verification endpoint base (e.g. https://verify.encypher.com or
     * the site's own /c2pa-verify/ slug) used in the <provenance verify="">.
     */
    private function verification_api_base(): string
    {
        $settings = get_option('encypher_provenance_settings', []);
        if (is_array($settings) && ! empty($settings['verify_base_url'])) {
            return rtrim((string) $settings['verify_base_url'], '/');
        }
        // Fall back to the on-site verification page slug.
        return rtrim(home_url('/' . Plugin::get_verify_slug()), '/');
    }
}
