<?php

/**
 * Custom Post Type Sitemap Provider
 *
 * Generates sitemaps for public custom post types.
 *
 * @package ProRank\SEO\Modules\Indexing\Sitemaps
 * @since   2.1.0
 */

declare(strict_types=1);

namespace ProRank\SEO\Modules\Indexing\Sitemaps;

use ProRank\SEO\Core\SettingsManager;

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

class CustomPostTypeSitemapProvider extends BaseSitemapProvider {

    private string $post_type;

    private ?SettingsManager $settings_manager = null;

    public function __construct(string $post_type) {
        parent::__construct();

        $object = get_post_type_object($post_type);
        $this->post_type = $post_type;
        $this->slug = $post_type;
        $this->name = $object && !empty($object->labels->name)
            ? (string) $object->labels->name
            : ucfirst(str_replace(['-', '_'], ' ', $post_type));
    }

    public function is_enabled(): bool {
        $object = get_post_type_object($this->post_type);
        if (!$object || !$object->public || $object->name === 'attachment') {
            return false;
        }

        $settings = $this->resolve_sitemap_settings($this->settings_manager);
        $enabled_post_types = $settings['include_post_types'] ?? ['post', 'page'];

        return in_array($this->post_type, $enabled_post_types, true);
    }

    public function get_total_pages(): int {
        $settings = $this->resolve_sitemap_settings($this->settings_manager);
        $total_posts = count(
            $this->get_sitemap_post_ids(
                $this->post_type,
                $settings,
                [
                    'orderby' => 'modified',
                    'order' => 'DESC',
                ]
            )
        );

        return (int) ceil($total_posts / $this->max_entries);
    }

    public function generate_xml(int $page = 1): string {
        $offset = ($page - 1) * $this->max_entries;
        $settings = $this->resolve_sitemap_settings($this->settings_manager);
        $post_ids = $this->get_sitemap_post_ids(
            $this->post_type,
            $settings,
            [
                'orderby' => 'modified',
                'order' => 'DESC',
            ]
        );
        $post_ids = array_slice($post_ids, $offset, $this->max_entries);

        $urls = [];

        foreach ($post_ids as $post_id) {
            $post = get_post($post_id);
            if (!$post instanceof \WP_Post) {
                continue;
            }

            $url_data = [
                'loc' => get_permalink($post),
                'lastmod' => get_post_modified_time('c', true, $post),
                'changefreq' => $this->get_change_frequency((int) get_post_modified_time('U', true, $post)),
                'priority' => $this->resolve_post_priority($post, 0.6, $settings),
            ];

            $url_data = apply_filters('prorank_seo_sitemap_url_data', $url_data, $post, $this->post_type);
            $urls[] = $url_data;
        }

        return $this->generate_urlset_xml($urls);
    }

    protected function get_last_modified(int $page = 1): string {
        global $wpdb;

        $offset = ($page - 1) * $this->max_entries;
        $last_modified = $wpdb->get_var(
            $wpdb->prepare(
                "SELECT MAX(post_modified_gmt) FROM {$wpdb->posts}
                WHERE post_type = %s
                AND post_status = 'publish'
                AND post_password = ''
                ORDER BY post_modified_gmt DESC
                LIMIT %d OFFSET %d",
                $this->post_type,
                $this->max_entries,
                $offset
            )
        );

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

    private function get_change_frequency(int $last_modified): string {
        $days_ago = (time() - $last_modified) / DAY_IN_SECONDS;

        if ($days_ago < 1) {
            return 'hourly';
        }
        if ($days_ago < 7) {
            return 'daily';
        }
        if ($days_ago < 30) {
            return 'weekly';
        }
        if ($days_ago < 365) {
            return 'monthly';
        }

        return 'yearly';
    }
}
