<?php
/**
 * Rendering helpers for the email admin UI.
 *
 * @package MailDoctor
 */

namespace MailDoctor;

use MailDoctor\Admin;
use MailDoctor\Email\MergeTagRegistry;
use MailDoctor\Email\TemplateRepository;
use WP_Post;

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

/**
 * View rendering responsibilities for the email admin controller.
 */
trait WPMD_Email_Admin_Render {


	/**
	 * Render main templates page (list + editor).
	 *
	 * @return void
	 */
	public static function render_templates_page(): void {
		self::render_templates_screen( false );
	}

	/**
	 * Render templates UI inside Mail Doctor tab navigation.
	 *
	 * @return void
	 */
	public static function render_templates_tab(): void {
		self::render_templates_screen( true );
	}

	/**
	 * Shared renderer for email templates screen.
	 *
	 * @param bool $embedded Whether UI is embedded within Mail Doctor tabs.
	 * @return void
	 */
	protected static function render_templates_screen( bool $embedded ): void {
		if ( ! current_user_can( self::capability() ) ) {
			wp_die( esc_html__( 'You do not have permission to manage email templates.', 'mail-doctor' ) );
		}

		$view        = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : self::MENU_SLUG; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only.
		$email_types = WPMD_Email_Templates::get_supported_email_types();
		$email_type  = isset( $_GET['email_type'] ) ? sanitize_key( wp_unslash( $_GET['email_type'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only.
		$email_type  = $email_type && isset( $email_types[ $email_type ] ) ? $email_type : key( $email_types );

		$active_panel = isset( $_GET['mail_doctor_panel'] ) ? sanitize_key( wp_unslash( $_GET['mail_doctor_panel'] ) ) : 'editor'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only.
		if ( ! in_array( $active_panel, array( 'editor', 'history' ), true ) ) {
			$active_panel = 'editor';
		}

		$is_add_screen    = ( self::ADD_SLUG === $view ) || ( isset( $_GET['mail_doctor_action'] ) && 'add' === sanitize_key( wp_unslash( $_GET['mail_doctor_action'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only.
		$current_template = self::prepare_template_for_view( $email_type, ! $is_add_screen );
		$defaults         = WPMD_Email_Templates::get_default_template_data( $email_type );
		$context_value    = $embedded ? 'maildoctor' : 'standalone';
		$selected_label   = $email_types[ $email_type ] ?? $email_type;
		$tabs             = $embedded ? Admin::get_tabs() : array();
		$history_limit    = 20;
		$history_entries  = array();
		$history_has_more = false;
		$history_source   = ! empty( $current_template['id'] ) ? (int) $current_template['id'] : $email_type;
		$editor_mode_pref = self::get_editor_mode_preference();

		$history_repository = new TemplateRepository();
		$raw_history        = $history_repository->list_history( $history_source, $history_limit + 1 );
		if ( ! empty( $raw_history ) ) {
			$history_has_more = count( $raw_history ) > $history_limit;
			$history_entries  = array_slice( $raw_history, 0, $history_limit );
		}

		$notices_html = self::capture_output(
			static function () {
				self::render_notices();
			}
		);

		self::render_view(
			'templates-screen',
			array(
				'embedded'         => $embedded,
				'tabs'             => $tabs,
				'view'             => $view,
				'email_types'      => $email_types,
				'email_type'       => $email_type,
				'selected_label'   => $selected_label,
				'current_template' => $current_template,
				'defaults'         => $defaults,
				'context_value'    => $context_value,
				'notices_html'     => $notices_html,
				'add_link_url'     => self::add_link( $email_type ),
				'editor_mode'      => $current_template['editor_mode'] ?? $editor_mode_pref,
				'locale_options'   => self::get_locale_options(),
				'selected_locale'  => get_locale(),
				'status_options'   => self::get_status_options(),
				'merge_tags'       => self::get_merge_tag_catalog(),
				'active_panel'     => $active_panel,
				'history_entries'  => $history_entries,
				'history_has_more' => $history_has_more,
				'history_limit'    => $history_limit,
			)
		);
	}

	/**
	 * Render notices from query args.
	 *
	 * @return void
	 */
	protected static function render_notices(): void {
		$notice = isset( $_GET['mail_doctor_notice'] ) ? sanitize_key( wp_unslash( $_GET['mail_doctor_notice'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only.

		switch ( $notice ) {
			case 'saved':
				self::render_notice( esc_html__( 'Template saved successfully.', 'mail-doctor' ), 'success' );
				break;
			case 'error':
				self::render_notice( esc_html__( 'Something went wrong. Please try again.', 'mail-doctor' ), 'error' );
				break;
			case 'toggle':
				self::render_notice( esc_html__( 'Template settings updated.', 'mail-doctor' ), 'success' );
				break;
			case 'reset':
				self::render_notice( esc_html__( 'Template reset to plugin defaults.', 'mail-doctor' ), 'success' );
				break;
			case 'restored':
				$version_id = isset( $_GET['mail_doctor_version'] ) ? sanitize_text_field( wp_unslash( $_GET['mail_doctor_version'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only.
				$message    = esc_html__( 'Template restored successfully.', 'mail-doctor' );
				if ( $version_id ) {
					$version_details = ( new TemplateRepository() )->get_version( $version_id );
					if ( $version_details ) {
						$timestamp = isset( $version_details['timestamp'] ) ? wp_date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), (int) $version_details['timestamp'] ) : esc_html__( 'an earlier revision', 'mail-doctor' );
						$author    = $version_details['author_name'] ?? esc_html__( 'Unknown', 'mail-doctor' );
						/* translators: 1: timestamp, 2: author name */
						$message = sprintf( esc_html__( 'Template restored to %1$s by %2$s.', 'mail-doctor' ), $timestamp, $author );
					}
				}
				self::render_notice( $message, 'success' );
				break;
			case 'restore_error':
				self::render_notice( esc_html__( 'Unable to restore the selected version.', 'mail-doctor' ), 'error' );
				break;
		}
	}

	/**
	 * Output a notice banner.
	 *
	 * @param string $message Notice message.
	 * @param string $type    Notice class.
	 * @return void
	 */
	protected static function render_notice( string $message, string $type = 'success' ): void {
		?>
		<div class="notice notice-<?php echo esc_attr( $type ); ?> is-dismissible">
			<p><?php echo esc_html( $message ); ?></p>
		</div>
		<?php
	}

	/**
	 * Build view payload for templates.
	 *
	 * @return array<string, mixed>
	 */
	protected static function get_templates_payload(): array {
		$email_types = WPMD_Email_Templates::get_supported_email_types();
		$payload     = array();

		foreach ( $email_types as $type => $label ) {
			$current          = self::prepare_template_for_view( $type, true );
			$payload[ $type ] = array(
				'label'    => $label,
				'isActive' => ! empty( $current['is_active'] ),
				'editUrl'  => self::edit_link( $type ),
				'addUrl'   => self::add_link( $type ),
			);
		}

		return $payload;
	}

	/**
	 * Prepare template data for view rendering.
	 *
	 * @param string $email_type Email identifier.
	 * @param bool   $prefer_custom Whether custom template is required.
	 * @return array<string, mixed>
	 */
	protected static function prepare_template_for_view( string $email_type, bool $prefer_custom ): array {
		$template     = $prefer_custom ? self::get_template_post( $email_type ) : null;
		$default_mode = self::get_editor_mode_preference();

		if ( $template instanceof WP_Post ) {
			$stored_mode = (string) get_post_meta( $template->ID, '_mail_doctor_editor_mode', true ) ?: '';
			return array(
				'id'          => $template->ID,
				'post_id'     => $template->ID,
				'is_active'   => (bool) get_post_meta( $template->ID, WPMD_Email_Templates::META_ACTIVE, true ),
				'subject'     => (string) get_post_meta( $template->ID, WPMD_Email_Templates::META_SUBJECT, true ),
				'heading'     => (string) get_post_meta( $template->ID, WPMD_Email_Templates::META_HEADING, true ),
				'html'        => (string) get_post_meta( $template->ID, WPMD_Email_Templates::META_BODY, true ),
				'css'         => (string) get_post_meta( $template->ID, WPMD_Email_Templates::META_CSS, true ),
				'modified'    => get_post_field( 'post_modified', $template->ID ),
				'editor_mode' => $stored_mode ?: $default_mode,
			);
		}

		$defaults = WPMD_Email_Templates::get_default_template_data( $email_type );

		return array(
			'id'          => 0,
			'is_active'   => false,
			'subject'     => $defaults['subject'] ?? '',
			'heading'     => $defaults['heading'] ?? '',
			'html'        => $defaults['html'] ?? '',
			'css'         => $defaults['css'] ?? '',
			'modified'    => '',
			'editor_mode' => $default_mode,
		);
	}

	/**
	 * Determine preferred editor mode for the current user.
	 *
	 * @return string
	 */
	protected static function get_editor_mode_preference(): string {
		$mode = get_user_meta( get_current_user_id(), '_mail_doctor_last_editor_mode', true );

		return in_array( $mode, array( 'visual', 'code' ), true ) ? $mode : 'visual';
	}

	/**
	 * Fetch template post for email type.
	 *
	 * @param string $email_type Email identifier.
	 * @return WP_Post|null
	 */
	protected static function get_template_post( string $email_type ): ?WP_Post {
		// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key, WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- Required for template lookup by email type.
		$query = new \WP_Query(
			array(
				'post_type'      => WPMD_Email_Templates::CPT,
				'post_status'    => 'publish',
				'posts_per_page' => 1,
				'meta_key'       => WPMD_Email_Templates::META_EMAIL_TYPE, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
				'meta_value'     => $email_type, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
			)
		);

		if ( ! empty( $query->posts ) ) {
			return $query->posts[0];
		}

		return null;
	}

	/**
	 * Provide merge tag catalog grouped for the sidebar UI.
	 *
	 * @return array<int, array<string, mixed>>
	 */
	protected static function get_merge_tag_catalog(): array {
		$definitions = MergeTagRegistry::get_definitions();
		$groups      = array();

		foreach ( $definitions as $tag => $definition ) {
			if ( isset( $definition['alias_of'] ) ) {
				continue;
			}

			$group_key = isset( $definition['group'] ) ? (string) $definition['group'] : 'general';

			if ( ! isset( $groups[ $group_key ] ) ) {
				$groups[ $group_key ] = array(
					'key'   => $group_key,
					'label' => self::label_for_tag_group( $group_key ),
					'tags'  => array(),
				);
			}

			$groups[ $group_key ]['tags'][] = array(
				'tag'         => (string) $tag,
				'label'       => isset( $definition['label'] ) ? (string) $definition['label'] : (string) $tag,
				'description' => isset( $definition['description'] ) ? (string) $definition['description'] : '',
			);
		}

		foreach ( $groups as &$group ) {
			usort(
				$group['tags'],
				static function ( array $a, array $b ): int {
					return strcasecmp( $a['label'], $b['label'] );
				}
			);
		}
		unset( $group );

		uksort(
			$groups,
			static function ( string $a, string $b ): int {
				return strcasecmp( $a, $b );
			}
		);

		$catalog = array_values( $groups );

		/**
		 * Filter merge tag catalog used by the admin UI.
		 *
		 * @param array<int, array<string, mixed>> $catalog Tag catalog.
		 */
		return apply_filters( 'mail_doctor_merge_tag_catalog', $catalog );
	}

	/**
	 * Derive locale options for dropdowns.
	 *
	 * @return array<int, array<string, string>>
	 */
	protected static function get_locale_options(): array {
		$options = array();
		$locales = array();

		$site_locale = get_locale();
		if ( $site_locale ) {
			$locales[] = $site_locale;
		}

		if ( function_exists( 'get_user_locale' ) ) {
			$user_locale = get_user_locale();
			if ( $user_locale ) {
				$locales[] = $user_locale;
			}
		}

		if ( function_exists( 'get_available_languages' ) ) {
			$locales = array_merge( $locales, get_available_languages() );
		}

		$locales = array_values( array_unique( array_filter( $locales ) ) );

		foreach ( $locales as $locale ) {
			$options[] = array(
				'value' => $locale,
				'label' => self::format_locale_label( $locale ),
			);
		}

		/**
		 * Filter locale options for the editor UI.
		 *
		 * @param array<int, array<string, string>> $options Locale options.
		 */
		return apply_filters( 'mail_doctor_editor_locale_options', $options );
	}

	/**
	 * Provide order status options for preview controls.
	 *
	 * @return array<int, array<string, string>>
	 */
	protected static function get_status_options(): array {
		$options  = array();
		$statuses = function_exists( 'wc_get_order_statuses' ) ? wc_get_order_statuses() : array(
			'pending'    => esc_html__( 'Pending payment', 'mail-doctor' ),
			'processing' => esc_html__( 'Processing', 'mail-doctor' ),
			'completed'  => esc_html__( 'Completed', 'mail-doctor' ),
			'cancelled'  => esc_html__( 'Cancelled', 'mail-doctor' ),
			'refunded'   => esc_html__( 'Refunded', 'mail-doctor' ),
		);

		foreach ( $statuses as $key => $label ) {
			$key       = preg_replace( '/^wc-/', '', (string) $key );
			$options[] = array(
				'value' => (string) $key,
				'label' => (string) $label,
			);
		}

		/**
		 * Filter status options for preview controls.
		 *
		 * @param array<int, array<string, string>> $options Status options.
		 */
		return apply_filters( 'mail_doctor_editor_status_options', $options );
	}

	/**
	 * Resolve a readable label for a merge tag group.
	 *
	 * @param string $group_key Group identifier.
	 * @return string
	 */
	protected static function label_for_tag_group( string $group_key ): string {
		switch ( $group_key ) {
			case 'order':
				return esc_html__( 'Order', 'mail-doctor' );
			case 'customer':
				return esc_html__( 'Customer', 'mail-doctor' );
			case 'store':
				return esc_html__( 'Store', 'mail-doctor' );
			case 'email':
				return esc_html__( 'Email', 'mail-doctor' );
			case 'account':
				return esc_html__( 'Account', 'mail-doctor' );
			default:
				return ucwords( str_replace( '_', ' ', $group_key ) );
		}
	}

	/**
	 * Format locale label with readable name when possible.
	 *
	 * @param string $locale Locale code.
	 * @return string
	 */
	protected static function format_locale_label( string $locale ): string {
		$locale = (string) $locale;
		$label  = $locale;

		if ( function_exists( 'locale_get_display_name' ) ) {
			$display = locale_get_display_name( $locale, $locale );
			if ( $display ) {
				$label = $display . ' (' . $locale . ')';
			}
		}

		return $label;
	}

	/**
	 * Generate edit link for an email type.
	 *
	 * @param string $email_type Email identifier.
	 * @return string
	 */
	protected static function edit_link( string $email_type ): string {
		return add_query_arg(
			array(
				'page'       => self::MENU_SLUG,
				'email_type' => $email_type,
			),
			admin_url( 'admin.php' )
		);
	}

	/**
	 * Generate add-new link for an email type.
	 *
	 * @param string $email_type Email identifier.
	 * @return string
	 */
	protected static function add_link( string $email_type ): string {
		return add_query_arg(
			array(
				'page'        => self::ADD_SLUG,
				'email_type'  => $email_type,
				'mail_doctor_action' => 'add',
			),
			admin_url( 'admin.php' )
		);
	}

	/**
	 * Render a view template with extracted data.
	 *
	 * @param string               $template Template slug.
	 * @param array<string, mixed> $data     Template data.
	 * @return void
	 */
	protected static function render_view( string $template, array $data = array() ): void {
		$file = __DIR__ . '/views/' . $template . '.php';
		if ( ! file_exists( $file ) ) {
			return;
		}

		extract( $data, EXTR_SKIP );
		include $file;
	}

	/**
	 * Capture callback output into a string.
	 *
	 * @param callable $callback Callback to execute.
	 * @return string
	 */
	protected static function capture_output( callable $callback ): string {
		ob_start();
		$callback();

		return (string) ob_get_clean();
	}
}
