<?php

/**
 * Plugin Name:       Post Date Labels
 * Description:       A small plugin that adds context labels to post date block.
 * Requires at least: 5.8
 * Requires PHP:      7.0
 * Version:           1.0.3
 * Tested up to:      7.0
 * Author:            Small Plugins
 * Author URI:        https://smallplugins.com
 * License:           GPL-2.0-or-later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       post-date-labels
 *
 * @package           PostDateLabels
 */
if ( !defined( 'ABSPATH' ) ) {
    exit;
}
if ( !defined( 'SPPDL_DIR_PATH' ) ) {
    define( 'SPPDL_DIR_PATH', plugin_dir_path( __FILE__ ) );
}
if ( !defined( 'SPPDL_PLUGIN_URI' ) ) {
    define( 'SPPDL_PLUGIN_URI', plugins_url( '/', __FILE__ ) );
}
require_once SPPDL_DIR_PATH . '/vendor/autoload.php';
if ( !class_exists( 'SPPDL_Post_Date_Labels' ) ) {
    /**
     * Main Class.
     */
    class SPPDL_Post_Date_Labels {
        /**
         * Constructor.
         */
        public function __construct() {
            if ( !function_exists( 'sppdal_fs' ) ) {
                // Create a helper function for easy SDK access.
                function sppdal_fs() {
                    global $sppdal_fs;
                    if ( !isset( $sppdal_fs ) ) {
                        // Include Freemius SDK.
                        // SDK is auto-loaded through composer
                        $sppdal_fs = fs_dynamic_init( array(
                            'id'               => '18982',
                            'slug'             => 'post-date-labels',
                            'type'             => 'plugin',
                            'public_key'       => 'pk_a7243224e98e7ad0c18e7e99b2100',
                            'is_premium'       => false,
                            'premium_suffix'   => 'Pro',
                            'has_addons'       => false,
                            'has_paid_plans'   => true,
                            'menu'             => array(
                                'first-path' => 'plugins.php',
                                'support'    => false,
                            ),
                            'is_live'          => true,
                            'is_org_compliant' => true,
                        ) );
                    }
                    return $sppdal_fs;
                }

                // Init Freemius.
                sppdal_fs();
                // Signal that SDK was initiated.
                do_action( 'sppdal_fs_loaded' );
            }
            add_action( 'enqueue_block_assets', array($this, 'enqueue_styles') );
            add_action( 'enqueue_block_editor_assets', array($this, 'enqueue_assets') );
            add_action(
                'render_block_core/post-date',
                array($this, 'add_support'),
                10,
                3
            );
        }

        /**
         * Adds support for the post date block.
         *
         * @param string    $content The content of the block.
         * @param object    $block The block object.
         * @param \WP_Block $block_instance The block instance.
         * @return string The content of the block.
         */
        public function add_support( $content, $block, $block_instance ) {
            // Content is empty, so we don't need to add any labels (can happen when the block is rendered in the editor via SSR).
            if ( '' === $content ) {
                return $content;
            }
            $published_date_prefix = ( isset( $block['attrs']['sppdlPostDatePublishedDatePrefix'] ) ? $block['attrs']['sppdlPostDatePublishedDatePrefix'] : '' );
            $published_date_suffix = ( isset( $block['attrs']['sppdlPostDatePublishedDateSuffix'] ) ? $block['attrs']['sppdlPostDatePublishedDateSuffix'] : '' );
            $modified_date_prefix = ( isset( $block['attrs']['sppdlPostDateModifiedDatePrefix'] ) ? $block['attrs']['sppdlPostDateModifiedDatePrefix'] : '' );
            $modified_date_suffix = ( isset( $block['attrs']['sppdlPostDateModifiedDateSuffix'] ) ? $block['attrs']['sppdlPostDateModifiedDateSuffix'] : '' );
            // Determine which type of date is being displayed
            $date_type = $this->get_rendered_date_type( $content );
            // Set up arguments based on date type
            $args = array(
                'prefix'       => ( $date_type === 'modified' ? $modified_date_prefix : $published_date_prefix ),
                'suffix'       => ( $date_type === 'modified' ? $modified_date_suffix : $published_date_suffix ),
                'prefix_class' => ( $date_type === 'modified' ? 'post-date-affix post-date-prefix post-date-modified-prefix' : 'post-date-affix post-date-prefix post-date-published-prefix' ),
                'suffix_class' => ( $date_type === 'modified' ? 'post-date-affix post-date-suffix post-date-modified-suffix' : 'post-date-affix post-date-suffix post-date-published-suffix' ),
            );
            return $this->append_date_labels( $content, $args );
        }

        /**
         * Gets the rendered date from the post date block content as a timestamp.
         *
         * This method parses the rendered HTML content of the post date block
         * and extracts the datetime value from the time element, converting it
         * to a Unix timestamp for easy comparison.
         *
         * @since 1.0.0
         * @access private
         *
         * @param string $block_content The rendered HTML content of the post date block.
         * @return int|false Unix timestamp representing the date, or false on failure.
         */
        private function get_rendered_date( $block_content ) {
            // Suppress libxml errors
            $previous_value = libxml_use_internal_errors( true );
            // Create a DOMDocument instance
            $doc = new \DOMDocument();
            // Load the HTML content, using @ to suppress warnings
            @$doc->loadHTML( mb_convert_encoding( $block_content, 'HTML-ENTITIES', 'UTF-8' ), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD );
            // Clear any libxml errors
            libxml_clear_errors();
            // Find the time element
            $time_elements = $doc->getElementsByTagName( 'time' );
            // Restore previous error handling state
            libxml_use_internal_errors( $previous_value );
            // Check if we found a time element
            if ( $time_elements->length > 0 ) {
                $time_element = $time_elements->item( 0 );
                $datetime = $time_element->getAttribute( 'datetime' );
                // Convert the datetime to a timestamp
                return strtotime( $datetime );
            }
            return false;
        }

        /**
         * Determines whether the rendered date is the published or modified date.
         *
         * This method compares the rendered date from the block content with the post's
         * published and modified dates to determine which type of date is being displayed.
         *
         * @since 1.0.0
         * @access private
         *
         * @param string $block_content The rendered HTML content of the post date block.
         * @return string Returns 'modified' for modified date, 'published' for published date.
         */
        private function get_rendered_date_type( $block_content ) {
            $rendered_timestamp = $this->get_rendered_date( $block_content );
            // If we couldn't parse the rendered date, default to 'published'
            if ( false === $rendered_timestamp ) {
                return 'published';
            }
            $post_id = get_the_ID();
            $modified_timestamp = strtotime( get_post_modified_time( 'c', true, $post_id ) );
            $published_timestamp = strtotime( get_post_time( 'c', true, $post_id ) );
            /**
             * Compare the rendered timestamp with both modified and published timestamps
             */
            if ( $rendered_timestamp === $published_timestamp ) {
                return 'published';
            }
            if ( $rendered_timestamp === $modified_timestamp ) {
                return 'modified';
            }
            // Default to 'published' if it's not the modified date or if it matches published date
            return 'published';
        }

        /**
         * Appends labels to the post date content.
         *
         * This method takes the rendered post date block content and adds
         * prefix and/or suffix labels wrapped in span elements.
         *
         * @since 1.0.0
         * @access private
         *
         * @param string $content The rendered HTML content of the post date block.
         * @param array  $args {
         *     Optional. An array of arguments for configuring the date labels.
         *
         *     @type string $prefix       Text to add before the date. Default empty.
         *     @type string $suffix       Text to add after the date. Default empty.
         *     @type string $prefix_class CSS class for the prefix span. Default 'post-date-prefix'.
         *     @type string $suffix_class CSS class for the suffix span. Default 'post-date-suffix'.
         * }
         * @return string Modified HTML content with added labels.
         */
        private function append_date_labels( $content, $args = array() ) {
            // Parse defaults
            $args = wp_parse_args( $args, array(
                'prefix'       => '',
                'suffix'       => '',
                'prefix_class' => 'post-date-prefix',
                'suffix_class' => 'post-date-suffix',
            ) );
            // Suppress libxml errors
            $previous_value = libxml_use_internal_errors( true );
            // Create a DOMDocument instance
            $doc = new \DOMDocument();
            // Load the HTML content, using @ to suppress warnings
            @$doc->loadHTML( mb_convert_encoding( $content, 'HTML-ENTITIES', 'UTF-8' ), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD );
            // Clear any libxml errors
            libxml_clear_errors();
            // Find the container div
            $divs = $doc->getElementsByTagName( 'div' );
            if ( $divs->length > 0 ) {
                $container = $divs->item( 0 );
                // Add prefix if provided
                if ( !empty( $args['prefix'] ) ) {
                    $prefix_span = $doc->createElement( 'span', esc_html( $args['prefix'] ) );
                    $prefix_span->setAttribute( 'class', esc_attr( $args['prefix_class'] ) );
                    $container->insertBefore( $prefix_span, $container->firstChild );
                    // Add a line break for better HTML formatting
                    $container->insertBefore( $doc->createTextNode( "\n" ), $container->firstChild->nextSibling );
                }
                // Add suffix if provided
                if ( !empty( $args['suffix'] ) ) {
                    // Add a line break before suffix
                    $container->appendChild( $doc->createTextNode( "\n" ) );
                    $suffix_span = $doc->createElement( 'span', esc_html( $args['suffix'] ) );
                    $suffix_span->setAttribute( 'class', esc_attr( $args['suffix_class'] ) );
                    $container->appendChild( $suffix_span );
                }
            }
            // Restore previous error handling state
            libxml_use_internal_errors( $previous_value );
            // Get the modified HTML
            $modified_html = $doc->saveHTML();
            // Clean up the output by removing DOCTYPE and HTML/BODY tags
            $modified_html = preg_replace( '/^<!DOCTYPE.+?>/', '', str_replace( array(
                '<html>',
                '</html>',
                '<body>',
                '</body>'
            ), array(
                '',
                '',
                '',
                ''
            ), $modified_html ) );
            // Trim any whitespace
            return trim( $modified_html );
        }

        public function enqueue_styles() {
            wp_enqueue_style(
                'small-plugins-post-date-labels-style',
                SPPDL_PLUGIN_URI . 'dist/styling.css',
                array(),
                'initial'
            );
        }

        /**
         * Enqueues the necessary extension assets.
         *
         * @return void
         */
        public function enqueue_assets() {
            wp_enqueue_script(
                'small-plugins-post-date-labels',
                SPPDL_PLUGIN_URI . 'dist/plugin.js',
                array(
                    'lodash',
                    'wp-block-editor',
                    'wp-components',
                    'wp-compose',
                    'wp-data',
                    'wp-element',
                    'wp-hooks',
                    'wp-i18n',
                    'wp-dom'
                ),
                'initial',
                true
            );
        }

    }

    new SPPDL_Post_Date_Labels();
}