<?php
/**
 * Event countdown timer widget render layer.
 *
 * Shortcode: [rsmspark_event_countdown]
 *
 * Ingested from legacy SO Countdown Timer Widget (RSMSpark); integrated into Content Widgets.
 *
 * @package Rsmspark_Content_Widgets
 */

defined( 'ABSPATH' ) || exit;

/**
 * Rsmspark_Content_Widgets_Widget_Event_Countdown
 */
class Rsmspark_Content_Widgets_Widget_Event_Countdown {

	const REGISTRY_ID = 'event_countdown';

	/**
	 * @return void
	 */
	public static function register() {
		Rsmspark_Content_Widgets_Shortcodes::add(
			Rsmspark_Content_Widgets_Registry::shortcode_tag( self::REGISTRY_ID ),
			array( __CLASS__, 'shortcode' ),
			self::REGISTRY_ID
		);

		Rsmspark_Content_Widgets_Blocks::add(
			'event-countdown',
			array(
				'api_version'     => 3,
				'title'           => __( 'RSM Event Countdown', 'rsm-content-widgets' ),
				'description'     => __( 'Fixed-date countdown to an event — days, hours, minutes, and seconds.', 'rsm-content-widgets' ),
				'category'        => Rsmspark_Content_Widgets_Blocks::BLOCK_CATEGORY,
				'icon'            => 'clock',
				'keywords'        => array( 'countdown', 'timer', 'event', 'deadline', 'rsm' ),
				'supports'        => array( 'html' => false ),
				'attributes'      => self::block_attributes(),
				'render_callback' => array( __CLASS__, 'render_block' ),
			)
		);
	}

	/**
	 * @return array<string, array<string, mixed>>
	 */
	public static function block_attributes() {
		return array(
			'title'             => array( 'type' => 'string', 'default' => '' ),
			'end_date'          => array( 'type' => 'string', 'default' => '' ),
			'end_time'          => array( 'type' => 'string', 'default' => '00:00:00' ),
			'expired_message'   => array( 'type' => 'string', 'default' => '' ),
			'hide_when_expired' => array( 'type' => 'boolean', 'default' => false ),
			'background_color'  => array( 'type' => 'string', 'default' => '' ),
			'text_color'        => array( 'type' => 'string', 'default' => '' ),
			'title_color'       => array( 'type' => 'string', 'default' => '' ),
		);
	}

	/**
	 * @param array<string, mixed> $atts Shortcode attributes.
	 * @param string               $content Inner content.
	 * @return string
	 */
	public static function shortcode( $atts, $content = '' ) {
		unset( $content );
		return self::render( self::normalize_atts( is_array( $atts ) ? $atts : array() ) );
	}

	/**
	 * @param array<string, mixed> $attributes Block attributes.
	 * @param string               $content Inner content.
	 * @param WP_Block|null        $block Block instance.
	 * @return string
	 */
	public static function render_block( $attributes, $content = '', $block = null ) {
		unset( $content );
		$attributes = self::merge_block_attributes( $attributes, $block );
		return self::render( self::prepare_template_vars( self::normalize_atts( $attributes ) ) );
	}

	/**
	 * @param mixed         $attributes Render callback attributes.
	 * @param WP_Block|null $block      Block instance.
	 * @return array<string, mixed>
	 */
	public static function merge_block_attributes( $attributes, $block = null ) {
		$merged = is_array( $attributes ) ? $attributes : array();

		if ( $block instanceof WP_Block ) {
			if ( ! empty( $block->parsed_block['attrs'] ) && is_array( $block->parsed_block['attrs'] ) ) {
				$merged = array_merge( $merged, $block->parsed_block['attrs'] );
			}
			if ( ! empty( $block->attributes ) && is_array( $block->attributes ) ) {
				$merged = array_merge( $merged, $block->attributes );
			}
		}

		return $merged;
	}

	/**
	 * @param array<string, mixed> $atts Raw attributes.
	 * @return array<string, mixed>
	 */
	public static function normalize_atts( array $atts ) {
		$defaults = array(
			'title'             => '',
			'end_date'          => '',
			'end_time'          => '00:00:00',
			'expired_message'   => __( 'Countdown expired', 'rsm-content-widgets' ),
			'hide_when_expired' => false,
			'background_color'  => '',
			'text_color'        => '',
			'title_color'       => '',
		);

		$parsed = Rsmspark_Content_Widgets_Render::parse_atts( $atts, $defaults, 'rsmspark_event_countdown' );

		$raw_end_date = trim( (string) $parsed['end_date'] );
		$raw_end_time = trim( (string) $parsed['end_time'] );
		if ( '' === $raw_end_time ) {
			$raw_end_time = '00:00:00';
		}

		$sanitized_date = self::sanitize_date( $raw_end_date );
		$sanitized_time = self::sanitize_time( $raw_end_time );

		return array(
			'title'             => sanitize_text_field( (string) $parsed['title'] ),
			'raw_end_date'      => $raw_end_date,
			'raw_end_time'      => $raw_end_time,
			'end_date'          => $sanitized_date,
			'end_time'          => $sanitized_time,
			'expired_message'   => sanitize_text_field( (string) $parsed['expired_message'] ),
			'hide_when_expired' => Rsmspark_Content_Widgets_Render::boolish( $parsed['hide_when_expired'] ?? false ),
			'background_color'  => sanitize_hex_color( (string) $parsed['background_color'] ) ?: '',
			'text_color'        => sanitize_hex_color( (string) $parsed['text_color'] ) ?: '',
			'title_color'       => sanitize_hex_color( (string) $parsed['title_color'] ) ?: '',
		);
	}

	/**
	 * @param string $date Raw YYYY-MM-DD.
	 * @return string
	 */
	public static function sanitize_date( $date ) {
		$date = trim( (string) $date );
		if ( '' === $date ) {
			return '';
		}

		$dt = DateTimeImmutable::createFromFormat( 'Y-m-d', $date, wp_timezone() );
		if ( false === $dt || $dt->format( 'Y-m-d' ) !== $date ) {
			return '';
		}

		return $date;
	}

	/**
	 * @param string $time Raw HH:MM or HH:MM:SS.
	 * @return string
	 */
	public static function sanitize_time( $time ) {
		$time = trim( (string) $time );
		if ( '' === $time ) {
			return '00:00:00';
		}

		if ( preg_match( '/^\d{2}:\d{2}$/', $time ) ) {
			$time .= ':00';
		}

		if ( ! preg_match( '/^\d{2}:\d{2}:\d{2}$/', $time ) ) {
			return '00:00:00';
		}

		list( $hour, $minute, $second ) = array_map( 'intval', explode( ':', $time ) );
		if ( $hour > 23 || $minute > 59 || $second > 59 ) {
			return '00:00:00';
		}

		return $time;
	}

	/**
	 * @param array<string, mixed> $args Normalized widget args.
	 * @return string Empty when valid.
	 */
	public static function get_validation_error( array $args ) {
		$raw_date = trim( (string) ( $args['raw_end_date'] ?? $args['end_date'] ?? '' ) );
		$raw_time = trim( (string) ( $args['raw_end_time'] ?? $args['end_time'] ?? '' ) );

		if ( '' === $raw_date ) {
			return __( 'Set an end date (YYYY-MM-DD) in your site timezone.', 'rsm-content-widgets' );
		}

		if ( '' === (string) ( $args['end_date'] ?? '' ) ) {
			return __( 'End date must use YYYY-MM-DD format (site timezone).', 'rsm-content-widgets' );
		}

		if ( '' !== $raw_time && ! self::is_valid_time( $raw_time ) ) {
			return __( 'End time must use HH:MM or HH:MM:SS format.', 'rsm-content-widgets' );
		}

		return '';
	}

	/**
	 * @param string $time Raw time string.
	 * @return bool
	 */
	public static function is_valid_time( $time ) {
		$time = trim( (string) $time );
		if ( '' === $time ) {
			return true;
		}

		if ( ! preg_match( '/^\d{2}:\d{2}(:\d{2})?$/', $time ) ) {
			return false;
		}

		if ( preg_match( '/^\d{2}:\d{2}$/', $time ) ) {
			$time .= ':00';
		}

		list( $hour, $minute, $second ) = array_map( 'intval', explode( ':', $time ) );

		return $hour <= 23 && $minute <= 59 && $second <= 59;
	}

	/**
	 * @return bool
	 */
	public static function should_show_validation_notice() {
		if ( is_admin() ) {
			return true;
		}

		if ( class_exists( 'Rsmspark_Content_Widgets_Blocks', false ) && Rsmspark_Content_Widgets_Blocks::is_editor_preview_request() ) {
			return true;
		}

		return (bool) ( Rsmspark_Content_Widgets_Settings::get()['debug_mode'] ?? false );
	}

	/**
	 * @param string $message Validation message.
	 * @return string
	 */
	public static function render_validation_notice( $message ) {
		return sprintf(
			'<div class="rsmspark-cw rsmspark-cw-event-countdown rsmspark-cw-event-countdown--invalid" role="alert"><p class="rsmspark-cw-event-countdown__validation">%s</p></div>',
			esc_html( (string) $message )
		);
	}

	/**
	 * @param string $end_date YYYY-MM-DD.
	 * @param string $end_time HH:MM:SS.
	 * @return int Unix timestamp or 0 when invalid.
	 */
	public static function target_timestamp( $end_date, $end_time ) {
		$end_date = self::sanitize_date( (string) $end_date );
		if ( '' === $end_date ) {
			return 0;
		}

		$end_time = self::sanitize_time( (string) $end_time );

		try {
			$dt = new DateTimeImmutable( $end_date . ' ' . $end_time, wp_timezone() );
			return $dt->getTimestamp();
		} catch ( Exception $e ) {
			return 0;
		}
	}

	/**
	 * @param int $timestamp Target unix time.
	 * @return string
	 */
	public static function format_target_label( $timestamp ) {
		$timestamp = (int) $timestamp;
		if ( $timestamp <= 0 ) {
			return '';
		}

		$format = trim( (string) get_option( 'date_format' ) . ' ' . (string) get_option( 'time_format' ) );
		if ( '' === $format ) {
			$format = 'F j, Y g:i a';
		}

		return wp_date( $format, $timestamp, wp_timezone() );
	}

	/**
	 * @param int $timestamp Target unix time.
	 * @return array<string, int>
	 */
	public static function compute_units( $timestamp ) {
		$timestamp = (int) $timestamp;
		$diff      = max( 0, $timestamp - time() );

		return array(
			'days'    => (int) floor( $diff / DAY_IN_SECONDS ),
			'hours'   => (int) floor( ( $diff % DAY_IN_SECONDS ) / HOUR_IN_SECONDS ),
			'minutes' => (int) floor( ( $diff % HOUR_IN_SECONDS ) / MINUTE_IN_SECONDS ),
			'seconds' => (int) ( $diff % MINUTE_IN_SECONDS ),
		);
	}

	/**
	 * @param string $background_color Optional hex.
	 * @param string $text_color       Optional hex.
	 * @param string $title_color      Optional hex for the headline only.
	 * @return string
	 */
	public static function build_style_vars( $background_color = '', $text_color = '', $title_color = '' ) {
		$parts = array( Rsmspark_Content_Widgets_Render::inline_style_vars() );

		$background_color = sanitize_hex_color( (string) $background_color );
		if ( $background_color ) {
			$parts[] = '--rsmspark-cw-event-countdown-bg:' . $background_color . ';';
		}

		$text_color = sanitize_hex_color( (string) $text_color );
		if ( $text_color ) {
			$parts[] = '--rsmspark-cw-event-countdown-text:' . $text_color . ';';
		}

		$title_color = sanitize_hex_color( (string) $title_color );
		if ( $title_color ) {
			$parts[] = '--rsmspark-cw-event-countdown-title:' . $title_color . ';';
		}

		return implode( '', $parts );
	}

	/**
	 * @param array<string, mixed> $instance SiteOrigin widget instance.
	 * @return array<string, mixed>
	 */
	public static function prepare_from_siteorigin_instance( array $instance ) {
		$content    = Rsmspark_Content_Widgets_Render::section_instance( $instance, 'content' );
		$appearance = Rsmspark_Content_Widgets_Render::section_instance( $instance, 'appearance' );

		return self::normalize_atts( array_merge( $content, $appearance ) );
	}

	/**
	 * @param array<string, mixed> $attrs Block attributes or flat config.
	 * @return array<string, mixed>
	 */
	public static function instance_from_atts( array $attrs ) {
		$normalized = self::normalize_atts( $attrs );

		return array(
			'content' => array(
				'title'             => $normalized['title'],
				'end_date'          => $normalized['raw_end_date'],
				'end_time'          => $normalized['raw_end_time'],
				'expired_message'   => $normalized['expired_message'],
				'hide_when_expired' => $normalized['hide_when_expired'],
			),
			'appearance' => array(
				'background_color' => $normalized['background_color'],
				'text_color'       => $normalized['text_color'],
				'title_color'      => $normalized['title_color'],
			),
		);
	}

	/**
	 * @param array<string, mixed> $instance SiteOrigin or import instance.
	 * @return array<string, mixed>
	 */
	public static function sanitize_instance( array $instance ) {
		unset( $instance['import_export'], $instance['panels_info'] );

		if ( isset( $instance['content'] ) || isset( $instance['appearance'] ) ) {
			return self::prepare_from_siteorigin_instance( $instance );
		}

		return self::normalize_atts( $instance );
	}

	/**
	 * @param array<string, mixed> $content SiteOrigin content section.
	 * @return array<string, mixed>
	 */
	public static function prepare_from_instance( array $content ) {
		$normalized = self::normalize_atts( $content );

		if ( isset( $content['appearance'] ) && is_array( $content['appearance'] ) ) {
			$appearance = $content['appearance'];
			if ( isset( $appearance['background_color'] ) ) {
				$normalized['background_color'] = sanitize_hex_color( (string) $appearance['background_color'] ) ?: '';
			}
			if ( isset( $appearance['text_color'] ) ) {
				$normalized['text_color'] = sanitize_hex_color( (string) $appearance['text_color'] ) ?: '';
			}
			if ( isset( $appearance['title_color'] ) ) {
				$normalized['title_color'] = sanitize_hex_color( (string) $appearance['title_color'] ) ?: '';
			}
		}

		return $normalized;
	}

	/**
	 * @param array<string, mixed> $args Normalized args.
	 * @return array<string, mixed>
	 */
	public static function prepare_template_vars( array $args ) {
		$validation_error = self::get_validation_error( $args );
		$target_ts        = self::target_timestamp(
			(string) ( $args['end_date'] ?? '' ),
			(string) ( $args['end_time'] ?? '00:00:00' )
		);
		$expired          = $target_ts > 0 && $target_ts <= time();
		$units            = $target_ts > 0 ? self::compute_units( $target_ts ) : array(
			'days'    => 0,
			'hours'   => 0,
			'minutes' => 0,
			'seconds' => 0,
		);

		$static_message = '';
		if ( $target_ts > 0 ) {
			if ( $expired ) {
				$static_message = (string) ( $args['expired_message'] ?? '' );
			} else {
				$static_message = sprintf(
					/* translators: %s: formatted event date/time */
					__( 'Event on %s', 'rsm-content-widgets' ),
					self::format_target_label( $target_ts )
				);
			}
		}

		return array(
			'title'             => $args['title'] ?? '',
			'end_date'          => $args['end_date'] ?? '',
			'end_time'          => $args['end_time'] ?? '00:00:00',
			'expired_message'   => $args['expired_message'] ?? '',
			'hide_when_expired' => ! empty( $args['hide_when_expired'] ),
			'target_ts'         => $target_ts,
			'is_expired'        => $expired,
			'units'             => $units,
			'validation_error'  => $validation_error,
			'static_message'    => $static_message,
			'style_vars'        => self::build_style_vars(
				(string) ( $args['background_color'] ?? '' ),
				(string) ( $args['text_color'] ?? '' ),
				(string) ( $args['title_color'] ?? '' )
			),
		);
	}

	/**
	 * @param array<string, mixed> $args Template variables.
	 * @return string
	 */
	public static function render( array $args ) {
		$vars = isset( $args['units'] ) ? $args : self::prepare_template_vars( $args );

		if ( ! empty( $vars['validation_error'] ) ) {
			if ( self::should_show_validation_notice() ) {
				return self::render_validation_notice( $vars['validation_error'] );
			}
			return '';
		}

		if ( empty( $vars['end_date'] ) || empty( $vars['target_ts'] ) ) {
			return '';
		}

		if ( ! empty( $vars['is_expired'] ) && ! empty( $vars['hide_when_expired'] ) ) {
			return '';
		}

		return Rsmspark_Content_Widgets_Render::render_template( self::REGISTRY_ID, 'event-countdown.php', $vars );
	}
}

/**
 * @param array<string, mixed> $args Widget arguments.
 * @return string
 */
function rsmspark_cw_render_event_countdown( array $args = array() ) {
	return Rsmspark_Content_Widgets_Widget_Event_Countdown::render(
		Rsmspark_Content_Widgets_Widget_Event_Countdown::prepare_template_vars(
			Rsmspark_Content_Widgets_Widget_Event_Countdown::normalize_atts( $args )
		)
	);
}
