<?php
if (!defined('ABSPATH')) exit;

class RANKFORGE_Meta_Templates {
    private static $instance = null;
    public static function instance() { if (!self::$instance) self::$instance = new self(); return self::$instance; }

    private function __construct() {}

    /**
 * Get default templates per post type
 */
    public function get_default_templates() {
        return [
            'post' => [
                'title' => '{{title}} {{separator}} {{siteName}}',
                'description' => '{{excerpt}}',
            ],
            'page' => [
                'title' => '{{title}} {{separator}} {{siteName}}',
                'description' => '{{excerpt}}',
            ],
            'product' => [
                'title' => '{{title}} {{separator}} {{siteName}}',
                'description' => '{{excerpt}}',
            ],
        ];
    }

    /**
 * Get list of available variables with descriptions
 */
    public function get_available_variables() {
        return [
            '{{title}}'         => __('Post title', 'rankforge'),
            '{{siteName}}'      => __('Site name', 'rankforge'),
            '{{siteDesc}}'      => __('Site tagline', 'rankforge'),
            '{{excerpt}}'       => __('Post excerpt or first paragraph', 'rankforge'),
            '{{author}}'        => __('Author display name', 'rankforge'),
            '{{category}}'      => __('Primary category name', 'rankforge'),
            '{{date}}'          => __('Publish date', 'rankforge'),
            '{{modifiedDate}}'  => __('Modified date', 'rankforge'),
            '{{currentYear}}'   => __('Current year', 'rankforge'),
            '{{currentMonth}}'  => __('Current month name', 'rankforge'),
            '{{separator}}'     => __('Configurable separator', 'rankforge'),
            /* xgettext:no-php-format */
            /* translators: %field:NAME% is a literal token syntax, not a placeholder; do not translate it. */
            '{{field:NAME}}'    => __('Custom field value (FieldForge / ACF). Also accepts %field:NAME% syntax.', 'rankforge'),
        ];
    }

    /**
 * Resolve a template string by replacing variables with actual post values
 */
    public function resolve($template, $post_id) {
        if (empty($template)) return '';

        $post = get_post($post_id);
        if (!$post) return $template;

        $separator = $this->get_separator();

        // Build excerpt: post_excerpt or first paragraph
        $excerpt = '';
        if (!empty($post->post_excerpt)) {
            $excerpt = wp_strip_all_tags($post->post_excerpt);
        } else {
            $content = wp_strip_all_tags($post->post_content);
            $sentences = preg_split('/[.!?]+/', $content, 4);
            foreach ($sentences as $s) {
                $s = trim($s);
                if (empty($s)) continue;
                if (mb_strlen($excerpt . $s . '. ') > 160) break;
                $excerpt .= $s . '. ';
            }
            $excerpt = trim($excerpt) ?: mb_substr($content, 0, 160);
        }

        // Primary category
        $category = '';
        $cats = get_the_category($post_id);
        if (!empty($cats)) {
            $category = $cats[0]->name;
        }

        $replacements = [
            '{{title}}'         => $post->post_title,
            '{{siteName}}'      => get_bloginfo('name'),
            '{{siteDesc}}'      => get_bloginfo('description'),
            '{{excerpt}}'       => $excerpt,
            '{{author}}'        => get_the_author_meta('display_name', $post->post_author),
            '{{category}}'      => $category,
            '{{date}}'          => get_the_date('', $post),
            '{{modifiedDate}}'  => get_the_modified_date('', $post),
            '{{currentYear}}'   => gmdate('Y'),
            '{{currentMonth}}'  => gmdate('F'),
            '{{separator}}'     => $separator,
        ];

        $result = str_replace(array_keys($replacements), array_values($replacements), $template);

        // Field tokens: %field:NAME% and {{field:NAME}}
        // Resolves to FieldForge / ACF custom field values on the same post.
        // Empty / missing fields render as empty string (cleanup then drops dangling separators).
        $result = $this->resolve_field_tokens($result, $post_id);

        // Cleanup: remove empty variables that left dangling separators
        $result = $this->cleanup($result, $separator);

        return $result;
    }

    /**
 * Replace %field:NAME% and {{field:NAME}} tokens with the post's
 * FieldForge / ACF custom field value. Both syntaxes are accepted —
 * %field:…% mirrors how RankForge's older inline help describes custom
 * fields, {{field:…}} keeps consistency with the Mustache-style {{title}}
 * tokens above.
 *
 * Resolution order:
 * 1. apply_filters('rankforge_resolve_meta_token', null, $name, $post_id)
 * — themes / 3rd-party can short-circuit (returns string ⇒ used as-is)
 * 2. get_field($name, $post_id) when ACF / FieldForge ACF-compat is loaded
 * 3. get_post_meta($post_id, $name, true) as a flat fallback
 *
 * Arrays/objects are JSON-encoded so we never inject "Array" into
 * <meta> output. Empty values render empty (cleanup strips dangling
 * separators).
 *
 * @param string $text Template / manual-override text after {{…}} replacement.
 * @param int $post_id Current post id.
 * @return string
 */
    public function resolve_field_tokens($text, $post_id) {
        if ($text === '' || strpos($text, 'field:') === false) return $text;

        return preg_replace_callback(
            '/(?:%field:([A-Za-z0-9_\-]+)%|\{\{\s*field:([A-Za-z0-9_\-]+)\s*\}\})/',
            function ($m) use ($post_id) {
                $name = $m[1] !== '' ? $m[1] : $m[2];
                if ($name === '') return '';

                // Filter hook for theme overrides (e.g. computed values that
                // aren't in postmeta).
                $override = apply_filters('rankforge_resolve_meta_token', null, $name, $post_id);
                if (is_string($override)) {
                    return $override;
                }

                $value = '';
                if (function_exists('get_field')) {
                    $value = get_field($name, $post_id);
                }
                if ($value === '' || $value === null || $value === false) {
                    $value = get_post_meta($post_id, $name, true);
                }

                if (is_array($value) || is_object($value)) {
                    // Avoid injecting "Array" into HTML attribute. JSON keeps
                    // some debuggability if a theme accidentally points to a
                    // repeater — but cleanup will collapse empty-ish output.
                    $value = wp_json_encode($value);
                }

                return (string) $value;
            },
            $text
        );
    }

    /**
 * Cascade: Post → Taxonomy → Global → fallback
 * Returns resolved title/description for a post
 */
    public function get_resolved_meta($post_id, $field = 'title') {
        // Level 1: Post-level manual override
        $meta_key = ($field === 'title') ? '_rankforge_meta_title' : '_rankforge_meta_description';
        $manual = get_post_meta($post_id, $meta_key, true);
        if (!empty($manual)) {
            // Manual override may still contain %field:NAME% / {{field:NAME}}
            // tokens — let editors reference FieldForge / ACF values without
            // hand-typing them per post.
            return $this->resolve_field_tokens($manual, $post_id);
        }

        // Level 2: Taxonomy-level template
        $tax_templates = get_option('rankforge_tax_templates', []);
        $post = get_post($post_id);
        if ($post) {
            $taxonomies = get_object_taxonomies($post->post_type, 'names');
            foreach ($taxonomies as $tax) {
                $terms = wp_get_post_terms($post_id, $tax, ['fields' => 'ids']);
                if (!empty($terms) && !is_wp_error($terms)) {
                    foreach ($terms as $term_id) {
                        $key = $tax . '_' . $term_id;
                        if (!empty($tax_templates[$key][$field])) {
                            return $this->resolve($tax_templates[$key][$field], $post_id);
                        }
                    }
                }
            }
        }

        // Level 3: Global template per post type
        $global_templates = get_option('rankforge_title_templates', []);
        $post_type = $post ? $post->post_type : 'post';
        if (!empty($global_templates[$post_type][$field])) {
            return $this->resolve($global_templates[$post_type][$field], $post_id);
        }

        // Level 4: Default templates
        $defaults = $this->get_default_templates();
        if (!empty($defaults[$post_type][$field])) {
            return $this->resolve($defaults[$post_type][$field], $post_id);
        }

        // Final fallback: WordPress default
        if ($field === 'title') {
            return $post ? $post->post_title : '';
        }
        return $post ? mb_substr(wp_strip_all_tags($post->post_content), 0, 160) : '';
    }

    /**
 * Get configured separator
 */
    public function get_separator() {
        $sep = get_option('rankforge_title_separator', '|');
        return !empty($sep) ? $sep : '|';
    }

    /**
 * Clean up resolved template: remove empty values and dangling separators
 */
    private function cleanup($text, $separator) {
        $sep_escaped = preg_quote($separator, '/');

        // Remove double separators (e.g. "Title | | Site")
        $text = preg_replace('/\s*' . $sep_escaped . '\s*' . $sep_escaped . '\s*/', ' ' . $separator . ' ', $text);

        // Remove leading separator
        $text = preg_replace('/^\s*' . $sep_escaped . '\s*/', '', $text);

        // Remove trailing separator
        $text = preg_replace('/\s*' . $sep_escaped . '\s*$/', '', $text);

        // Collapse multiple spaces
        $text = preg_replace('/\s{2,}/', ' ', $text);

        return trim($text);
    }
}
