<?php

/**
 * Product Sitemap Provider
 *
 * Generates sitemaps for WooCommerce products.
 *
 * @package ProRank\SEO\Modules\Indexing\Sitemaps
 * @since   2.0.0
 */

declare(strict_types=1);

namespace ProRank\SEO\Modules\Indexing\Sitemaps;

use ProRank\SEO\Core\SettingsManager;

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


/**
 * ProductSitemapProvider class
 */


class ProductSitemapProvider extends BaseSitemapProvider {
    private ?SettingsManager $settings_manager = null;
    
    /**
     * Provider slug
     *
     * @var string
     */
    protected string $slug = 'product';
    
    /**
     * Provider name
     *
     * @var string
     */
    protected string $name = 'Products';
    
    /**
     * Items per page
     *
     * @var int
     */
    protected int $items_per_page = 1000;
    
    /**
     * Check if provider is enabled
     *
     * @return bool
     */
    public function is_enabled(): bool {
        // Check if WooCommerce is active
        if (!class_exists('WooCommerce')) {
            return false;
        }
        
        // Check settings
        $plugin = \ProRank\SEO\Plugin::get_instance();
        $settings_manager = $this->settings_manager ?: $plugin->settings();
        $sitemap_settings = $this->resolve_sitemap_settings($settings_manager);
        
        // Check if WC integration is enabled
        $wc_enabled = $settings_manager->get('enable_wc_sitemap_integration', 'ecommerce_seo', true);
        if (!$wc_enabled) {
            return false;
        }
        
        // Check if products are included
        $enabled_post_types = $sitemap_settings['include_post_types'] ?? ['post', 'page'];

        return in_array('product', $enabled_post_types, true)
            && $settings_manager->get('sitemap_include_products', 'ecommerce_seo', true);
    }
    
    /**
     * Get total pages
     *
     * @return int
     */
    public function get_total_pages(): int {
        $args = $this->get_query_args();
        $args['fields'] = 'ids';
        $args['posts_per_page'] = -1;
        
        $query = new \WP_Query($args);
        $total_items = is_array($query->posts) ? count($query->posts) : 0;
        
        return (int) ceil($total_items / $this->items_per_page);
    }
    
    /**
     * Get query args for products
     *
     * @return array
     */
    private function get_query_args(): array {
        $plugin = \ProRank\SEO\Plugin::get_instance();
        $settings_manager = $plugin->settings();
        
        $args = [
            'post_type' => 'product',
            'post_status' => 'publish',
            'orderby' => 'modified',
            'order' => 'DESC',
            'no_found_rows' => true,
            'update_post_term_cache' => false,
            'update_post_meta_cache' => false,
        ];
        
        // Exclude hidden products if setting is enabled
        $exclude_hidden = $settings_manager->get('sitemap_exclude_hidden_products', 'ecommerce_seo', true);
        if ($exclude_hidden) {
            // WooCommerce 3.0+ uses product_visibility taxonomy instead of _visibility meta
            $args['tax_query'][] = [
                'taxonomy' => 'product_visibility',
                'field' => 'slug',
                'terms' => ['exclude-from-catalog'],
                'operator' => 'NOT IN',
            ];
        }
        
        // Exclude out of stock products if setting is enabled
        $exclude_out_of_stock = $settings_manager->get('sitemap_exclude_out_of_stock', 'ecommerce_seo', false);
        if ($exclude_out_of_stock) {
            $args['meta_query'][] = [
                'key' => '_stock_status',
                'value' => 'outofstock',
                'compare' => '!=',
            ];
        }
        
        return $args;
    }
    
    /**
     * Generate sitemap
     *
     * @param int $page Page number.
     * @return string XML content.
     */
    public function generate_xml(int $page = 1): string {
        $args = $this->get_query_args();
        $args['posts_per_page'] = $this->items_per_page;
        $args['paged'] = $page;
        
        $query = new \WP_Query($args);
        
        $xml = new \XMLWriter();
        $xml->openMemory();
        $xml->setIndent(true);
        
        // Start XML document
        $xml->startDocument('1.0', 'UTF-8');
        
        // Add promotional comment
        $xml->writeComment(' Generated by ProRank SEO - https://prorank.io ');
        
        // Start urlset
        $xml->startElement('urlset');
        $xml->writeAttribute('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9');
        $xml->writeAttribute('xmlns:image', 'http://www.google.com/schemas/sitemap-image/1.1');
        
        $plugin = \ProRank\SEO\Plugin::get_instance();
        $settings_manager = $plugin->settings();
        $include_gallery = $settings_manager->get('sitemap_include_product_gallery', 'ecommerce_seo', true);
        
        // Add products
        if ($query->have_posts()) {
            while ($query->have_posts()) {
                $query->the_post();
                $post_id = get_the_ID();
                
                // Skip if excluded by SEO settings
                $noindex = get_post_meta($post_id, '_prorank_seo_noindex', true);
                if ($noindex) {
                    continue;
                }
                
                $product = wc_get_product($post_id);
                if (!$product) {
                    continue;
                }
                
                $xml->startElement('url');
                
                // Location
                $xml->writeElement('loc', get_permalink($post_id));
                
                // Last modified
                $post = get_post($post_id);
                $lastmod = get_post_modified_time('c', true, $post);
                if ($lastmod) {
                    $xml->writeElement('lastmod', $lastmod);
                }
                
                // Change frequency
                $xml->writeElement('changefreq', 'weekly');
                
                // Priority based on product type
                $priority = '0.7';
                if ($product->is_featured()) {
                    $priority = '0.9';
                } elseif ($product->is_on_sale()) {
                    $priority = '0.8';
                }
                $xml->writeElement('priority', $priority);
                
                // Add product images
                $this->add_product_images($xml, $product, $include_gallery);
                
                $xml->endElement(); // url
            }
            wp_reset_postdata();
        }
        
        $xml->endElement(); // urlset
        $xml->endDocument();
        
        return $xml->outputMemory();
    }
    
    /**
     * Add product images to sitemap
     *
     * @param \XMLWriter $xml XML writer instance.
     * @param \WC_Product $product Product object.
     * @param bool $include_gallery Include gallery images.
     * @return void
     */
    private function add_product_images(\XMLWriter $xml, \WC_Product $product, bool $include_gallery): void {
        // Add main image
        $image_id = (int) $product->get_image_id();
        if ($image_id > 0) {
            $this->add_image_to_xml($xml, $image_id, $product->get_name());
        }
        
        // Add gallery images if enabled
        if ($include_gallery) {
            $gallery_ids = array_map('intval', $product->get_gallery_image_ids());
            foreach ($gallery_ids as $gallery_id) {
                if ($gallery_id <= 0) {
                    continue;
                }
                $this->add_image_to_xml($xml, $gallery_id, $product->get_name());
            }
        }
    }
    
    /**
     * Add single image to XML
     *
     * @param \XMLWriter $xml XML writer instance.
     * @param int $image_id Attachment ID.
     * @param string $product_title Product title for caption.
     * @return void
     */
    private function add_image_to_xml(\XMLWriter $xml, int $image_id, string $product_title): void {
        $image_url = wp_get_attachment_url($image_id);
        if (!$image_url) {
            return;
        }
        
        $xml->startElement('image:image');
        $xml->writeElement('image:loc', $image_url);
        
        // Add title
        $image_title = get_the_title($image_id);
        if (empty($image_title)) {
            $image_title = $product_title;
        }
        $xml->writeElement('image:title', $this->clean_string($image_title));
        
        // Add caption if available
        $caption = wp_get_attachment_caption($image_id);
        if ($caption) {
            $xml->writeElement('image:caption', $this->clean_string($caption));
        }
        
        $xml->endElement(); // image:image
    }
    
    /**
     * Clean string for XML
     *
     * @param string $string String to clean.
     * @return string
     */
    private function clean_string(string $string): string {
        // Remove control characters and ensure valid UTF-8
        $string = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', '', $string);
        $string = mb_convert_encoding($string, 'UTF-8', 'UTF-8');
        
        // Escape XML entities
        return htmlspecialchars($string, ENT_XML1 | ENT_QUOTES, 'UTF-8');
    }
    
    /**
     * Get last modified date for sitemap
     *
     * @param int $page Page number
     * @return string ISO 8601 date
     */
    protected function get_last_modified(int $page = 1): string {
        global $wpdb;

        $last_modified = $wpdb->get_var(
            "SELECT MAX(post_modified_gmt)
            FROM {$wpdb->posts}
            WHERE post_type = 'product'
            AND post_status = 'publish'
            AND post_password = ''"
        );

        return $last_modified ? $this->format_date($last_modified) : $this->format_date('now');
    }

    /**
     * Get index entries for sitemap index
     *
     * @return array
     */
    public function get_index_entries(): array {
        $entries = [];
        $total_pages = $this->get_total_pages();
        
        for ($page = 1; $page <= $total_pages; $page++) {
            $loc = home_url('product-sitemap' . ($page > 1 ? $page : '') . '.xml');
            
            // Get last modified date
            $args = $this->get_query_args();
            $args['posts_per_page'] = 1;
            $args['paged'] = $page;
            $args['orderby'] = 'modified';
            $args['order'] = 'DESC';
            
            $query = new \WP_Query($args);
            $lastmod = null;
            
            if ($query->have_posts()) {
                $query->the_post();
                $lastmod = get_post_modified_time('c', true);
                wp_reset_postdata();
            }
            
            $entry = ['loc' => $loc];
            if ($lastmod) {
                $entry['lastmod'] = $lastmod;
            }
            
            $entries[] = $entry;
        }
        
        return $entries;
    }
}
