<?php
namespace EncypherProvenance;

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

/**
 * Canonical storage and sanitization contract for public editorial provenance.
 */
final class EditorialProvenance
{
    public const META_FIELDS = [
        '_encypher_public_provenance_note' => ['api' => 'public_note', 'limit' => 500, 'textarea' => true],
        '_encypher_credit_line' => ['api' => 'credit_line', 'limit' => 255, 'textarea' => false],
        '_encypher_source' => ['api' => 'source', 'limit' => 255, 'textarea' => false],
        '_encypher_transmission_reference' => ['api' => 'transmission_reference', 'limit' => 120, 'textarea' => false],
        '_encypher_digital_source_type' => ['api' => 'digital_source_type', 'limit' => 0, 'textarea' => false],
    ];

    public const DIGITAL_SOURCE_TYPES = [
        'human_created',
        'generative_ai_edited',
        'generative_ai_created',
    ];

    /**
     * Empty, invalid, and oversized values fail closed rather than entering a
     * signing request.
     *
     * @param mixed $value
     */
    public static function sanitize(string $meta_key, $value): string
    {
        if (! isset(self::META_FIELDS[$meta_key]) || ! is_scalar($value)) {
            return '';
        }

        $raw = is_string($value) ? $value : (string) $value;
        if ('_encypher_digital_source_type' === $meta_key) {
            $value = function_exists('sanitize_key') ? sanitize_key($raw) : strtolower(trim($raw));
            return in_array($value, self::DIGITAL_SOURCE_TYPES, true) ? $value : '';
        }

        $definition = self::META_FIELDS[$meta_key];
        $value = ! empty($definition['textarea'])
            ? (function_exists('sanitize_textarea_field') ? sanitize_textarea_field($raw) : trim($raw))
            : (function_exists('sanitize_text_field') ? sanitize_text_field($raw) : trim($raw));
        if (function_exists('wp_check_invalid_utf8')) {
            $checked = wp_check_invalid_utf8($value, true);
            $value = is_string($checked) ? $checked : '';
        }
        if ('' === $value || self::utf8_length($value) > (int) $definition['limit']) {
            return '';
        }
        return $value;
    }

    /**
     * Normalize the API-shaped form through the same storage rules.
     */
    public static function sanitize_api_object(array $input): array
    {
        $normalized = [];
        foreach (self::META_FIELDS as $meta_key => $definition) {
            $api_key = $definition['api'];
            if (! array_key_exists($api_key, $input)) {
                continue;
            }
            $value = self::sanitize($meta_key, $input[$api_key]);
            if ('' !== $value) {
                $normalized[$api_key] = $value;
            }
        }
        return $normalized;
    }

    /**
     * Read the five registered fields only when editorial provenance is opted in.
     */
    public static function for_post(int $post_id): array
    {
        $record = json_decode((string) get_post_meta($post_id, '_encypher_editorial_metadata', true), true);
        if (! is_array($record) || true !== ($record['include_in_provenance'] ?? false)) {
            return [];
        }

        $editorial = [];
        foreach (self::META_FIELDS as $meta_key => $definition) {
            $value = self::sanitize($meta_key, get_post_meta($post_id, $meta_key, true));
            if ('' !== $value) {
                $editorial[$definition['api']] = $value;
            }
        }
        return $editorial;
    }

    public static function utf8_length(string $value): int
    {
        if (function_exists('mb_strlen')) {
            return mb_strlen($value, 'UTF-8');
        }
        $count = preg_match_all('/./us', $value, $matches);
        return false === $count ? strlen($value) : $count;
    }
}
