<?php
namespace WooNooW\Frontend;

/**
 * Placeholder Renderer
 * Resolves dynamic placeholders to actual post/CPT data
 */
class PlaceholderRenderer
{
    /**
     * Cache timeout in seconds (5 minutes)
     */
    const CACHE_TIMEOUT = 300;

    /**
     * Dynamic source type definitions
     * Used for typed output contracts and validation
     */
    const SOURCE_TYPES = [
        'post_title' => 'scalar',
        'title' => 'scalar',
        'post_content' => 'html',
        'content' => 'html',
        'post_excerpt' => 'scalar',
        'excerpt' => 'scalar',
        'post_featured_image' => 'url',
        'featured_image' => 'url',
        'post_author' => 'scalar',
        'author' => 'scalar',
        'post_date' => 'scalar',
        'date' => 'scalar',
        'post_categories' => 'array',
        'categories' => 'array',
        'post_tags' => 'array',
        'tags' => 'array',
        'post_url' => 'url',
        'url' => 'url',
        'permalink' => 'url',
        'related_posts' => 'array',
    ];

    /**
     * Fallback values for when a source resolves to empty
     */
    const FALLBACK_VALUES = [
        'post_title' => '(Untitled)',
        'title' => '(Untitled)',
        'post_featured_image' => '',
        'featured_image' => '',
        'post_excerpt' => '',
        'excerpt' => '',
        'related_posts' => [],
    ];

    /**
     * Get expected type for a source
     *
     * @param string $source Placeholder source
     * @return string Type: scalar, html, url, array
     */
    public static function get_source_type($source)
    {
        if (isset(self::SOURCE_TYPES[$source])) {
            return self::SOURCE_TYPES[$source];
        }

        // Custom field types
        if (strpos($source, '_field_') !== false) {
            return 'scalar';
        }

        // Direct key - guess based on naming
        if (strpos($source, 'image') !== false || strpos($source, 'thumbnail') !== false) {
            return 'url';
        }

        if (strpos($source, 'ids') !== false || strpos($source, 'tags') !== false) {
            return 'array';
        }

        return 'scalar';
    }

    /**
     * Validate resolved value matches expected type
     *
     * @param mixed $value Resolved value
     * @param string $expected_type Expected type
     * @return bool True if valid
     */
    public static function validate_value_type($value, $expected_type)
    {
        switch ($expected_type) {
            case 'scalar':
            case 'html':
                return is_string($value) || is_numeric($value);

            case 'url':
                return is_string($value) && !empty($value);

            case 'array':
                return is_array($value);

            default:
                return true;
        }
    }

    /**
     * Get fallback value for a source
     *
     * @param string $source Placeholder source
     * @param string $expected_type Expected type
     * @return mixed Fallback value
     */
    public static function get_fallback($source, $expected_type = null)
    {
        if ($expected_type === 'array') {
            return [];
        }

        if (isset(self::FALLBACK_VALUES[$source])) {
            return self::FALLBACK_VALUES[$source];
        }

        return '';
    }

    /**
     * Check if a source is array-based (needs special handling)
     *
     * @param string $source Placeholder source
     * @return bool
     */
    public static function is_array_source($source)
    {
        return self::get_source_type($source) === 'array';
    }

    /**
     * Get cached post data
     *
     * @param int $post_id Post ID
     * @return array|null Cached post data or null if not cached
     */
    public static function get_cached_post_data($post_id)
    {
        $cache_key = "wn_post_data_{$post_id}";
        return get_transient($cache_key);
    }

    /**
     * Cache post data
     *
     * @param int $post_id Post ID
     * @param array $data Post data
     */
    public static function cache_post_data($post_id, $data)
    {
        $cache_key = "wn_post_data_{$post_id}";
        set_transient($cache_key, $data, self::CACHE_TIMEOUT);
    }

    /**
     * Invalidate post data cache
     *
     * @param int $post_id Post ID
     */
    public static function invalidate_post_data_cache($post_id)
    {
        $cache_key = "wn_post_data_{$post_id}";
        delete_transient($cache_key);
    }

    /**
     * Get value for a dynamic placeholder source
     *
     * @param string $source Placeholder source (e.g., 'post_title', 'post_content')
     * @param array $post_data Post data array
     * @param array $options Resolution options
     * @return mixed Resolved value
     */
    public static function get_value($source, $post_data, $options = [])
    {
        $options = wp_parse_args($options, [
            'use_fallback' => true,
            'validate_type' => true,
        ]);

        if (empty($source) || empty($post_data)) {
            return $options['use_fallback'] ? self::get_fallback($source) : '';
        }

        $expected_type = $options['validate_type'] ? self::get_source_type($source) : 'scalar';
        $value = '';

        // Standard post fields
        switch ($source) {
            case 'post_title':
            case 'title':
                $value = $post_data['title'] ?? $post_data['post_title'] ?? '';
                break;

            case 'post_content':
            case 'content':
                $value = $post_data['content'] ?? $post_data['post_content'] ?? '';
                break;

            case 'post_excerpt':
            case 'excerpt':
                $value = $post_data['excerpt'] ?? $post_data['post_excerpt'] ?? '';
                break;

            case 'post_featured_image':
            case 'featured_image':
                $value = $post_data['featured_image'] ??
                       $post_data['thumbnail'] ??
                       $post_data['_thumbnail_url'] ?? '';
                break;

            case 'post_author':
            case 'author':
                $value = $post_data['author'] ?? $post_data['post_author'] ?? '';
                break;

            case 'post_date':
            case 'date':
                $value = $post_data['date'] ?? $post_data['post_date'] ?? '';
                break;

            case 'post_categories':
            case 'categories':
                $value = $post_data['categories'] ?? [];
                break;

            case 'post_tags':
            case 'tags':
                $value = $post_data['tags'] ?? [];
                break;

            case 'post_url':
            case 'url':
            case 'permalink':
                $value = $post_data['url'] ?? $post_data['permalink'] ?? '';
                break;

            default:
                // Check for custom meta fields (format: {cpt}_field_{name})
                if (strpos($source, '_field_') !== false) {
                    $parts = explode('_field_', $source);
                    $field_name = end($parts);

                    // Try to get from meta array
                    if (isset($post_data['meta'][$field_name])) {
                        $value = $post_data['meta'][$field_name];
                    } elseif (isset($post_data[$field_name])) {
                        // Try direct field access
                        $value = $post_data[$field_name];
                    }
                } elseif (strpos($source, 'related_posts') !== false) {
                    // Handle related_posts through the dedicated method
                    $value = self::get_related_posts_for_post_data($post_data);
                } elseif (isset($post_data[$source])) {
                    // Check for direct key match
                    $value = $post_data[$source];
                }
                break;
        }

        // Validate type if enabled
        if ($options['validate_type'] && !self::validate_value_type($value, $expected_type)) {
            $value = $options['use_fallback'] ? self::get_fallback($source, $expected_type) : '';
        }

        // Apply fallback for empty values if enabled
        if ($options['use_fallback'] && $value === '') {
            $value = self::get_fallback($source, $expected_type);
        }

        return $value;
    }

    /**
     * Get related posts from post_data array
     *
     * @param array $post_data Post data array (must contain 'id' and 'type')
     * @param int $count Number of related posts
     * @return array Related posts data
     */
    public static function get_related_posts_for_post_data($post_data, $count = 3)
    {
        $post_id = $post_data['id'] ?? 0;
        $post_type = $post_data['type'] ?? 'post';

        if (!$post_id) {
            return [];
        }

        return self::get_related_posts($post_id, $count, $post_type);
    }
    
    /**
     * Build post data array from WP_Post object
     * 
     * @param \WP_Post|int $post Post object or ID
     * @return array Post data array
     */
    public static function build_post_data($post)
    {
        if (is_numeric($post)) {
            $post = get_post($post);
        }
        
        if (!$post || !($post instanceof \WP_Post)) {
            return [];
        }
        
        $data = [
            'id' => $post->ID,
            'title' => $post->post_title,
            'content' => apply_filters('the_content', $post->post_content),
            'excerpt' => $post->post_excerpt ?: wp_trim_words($post->post_content, 30),
            'date' => get_the_date('', $post),
            'date_iso' => get_the_date('c', $post),
            'url' => get_permalink($post),
            'slug' => $post->post_name,
            'type' => $post->post_type,
        ];
        
        // Author
        $author_id = $post->post_author;
        $data['author'] = get_the_author_meta('display_name', $author_id);
        $data['author_url'] = get_author_posts_url($author_id);
        
        // Featured image
        $thumbnail_id = get_post_thumbnail_id($post);
        if ($thumbnail_id) {
            $data['featured_image'] = get_the_post_thumbnail_url($post, 'large');
            $data['featured_image_id'] = $thumbnail_id;
        }
        
        // Taxonomies
        $taxonomies = get_object_taxonomies($post->post_type);
        foreach ($taxonomies as $taxonomy) {
            $terms = get_the_terms($post, $taxonomy);
            if ($terms && !is_wp_error($terms)) {
                $data[$taxonomy] = array_map(function($term) {
                    return [
                        'id' => $term->term_id,
                        'name' => $term->name,
                        'slug' => $term->slug,
                        'url' => get_term_link($term),
                    ];
                }, $terms);
            }
        }
        
        // Shortcuts for common taxonomies
        if (isset($data['category'])) {
            $data['categories'] = $data['category'];
        }
        if (isset($data['post_tag'])) {
            $data['tags'] = $data['post_tag'];
        }
        
        // Custom meta fields
        $meta = get_post_meta($post->ID);
        if ($meta) {
            $data['meta'] = [];
            foreach ($meta as $key => $values) {
                // Skip internal meta keys
                if (strpos($key, '_') === 0) {
                    continue;
                }
                $data['meta'][$key] = count($values) === 1 ? $values[0] : $values;
            }
        }
        
        return $data;
    }
    
    /**
     * Get related posts
     * 
     * @param int $post_id Current post ID
     * @param int $count Number of related posts
     * @param string $post_type Post type
     * @return array Related posts data
     */
    public static function get_related_posts($post_id, $count = 3, $post_type = 'post')
    {
        // Get categories of current post
        $categories = get_the_category($post_id);
        $category_ids = wp_list_pluck($categories, 'term_id');
        
        $args = [
            'post_type' => $post_type,
            'posts_per_page' => $count,
            'post__not_in' => [$post_id],
            'orderby' => 'date',
            'order' => 'DESC',
        ];
        
        if (!empty($category_ids)) {
            $args['category__in'] = $category_ids;
        }
        
        $query = new \WP_Query($args);
        $related = [];
        
        foreach ($query->posts as $post) {
            $related[] = [
                'id' => $post->ID,
                'title' => $post->post_title,
                'excerpt' => $post->post_excerpt ?: wp_trim_words($post->post_content, 20),
                'url' => get_permalink($post),
                'featured_image' => get_the_post_thumbnail_url($post, 'medium'),
                'date' => get_the_date('', $post),
            ];
        }
        
        wp_reset_postdata();
        
        return $related;
    }
}
