<?php

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

/**
 * @package Linguator
 */

/**
 * Class Linguator_Admin_Site_Health to add debug info in WP Site Health.
 *
 * @see https://make.wordpress.org/core/2019/04/25/site-health-check-in-5-2/ since WordPress 5.2
 *
 * @since 2.8
 */
class Linguator_Admin_Site_Health {
	/**
	 * A reference to the Linguator_Model instance.
	 *
	 *
	 * @var Linguator_Model
	 */
	protected $model;

	/**
	 * A reference to the Linguator_Admin_Static_Pages instance.
	 *
	 *
	 * @var Linguator_Admin_Static_Pages|null
	 */
	protected $static_pages;

	/**
	 * Linguator_Admin_Site_Health constructor.
	 *
	 *
	 * @param object $linguator The Linguator object.
	 */
	public function __construct( &$linguator ) {
		$this->model = &$linguator->model;
		$this->static_pages = &$linguator->static_pages;

		// Information tab.
		add_filter( 'debug_information', array( $this, 'info_options' ), 15 );
		add_filter( 'debug_information', array( $this, 'info_languages' ), 15 );
		add_filter( 'debug_information', array( $this, 'info' ), 15 );

		// Tests Tab.
		add_filter( 'site_status_tests', array( $this, 'status_tests' ) );
	}

	/**
	 * Returns a list of keys to exclude from the site health information.
	 *
	 *
	 * @return string[] List of option keys to ignore.
	 */
	protected function exclude_options_keys() {
		return array(
			'uninstall',
			'first_activation',
		);
	}

	/**
	 * Returns a list of keys to exclude from the site health information.
	 *
	 *
	 * @return string[] List of language keys to ignore.
	 */
	protected function exclude_language_keys() {
		return array(
			'flag',
			'host',
			'taxonomy',
			'description',
			'parent',
			'filter',
			'custom_flag',
		);
	}

	/**
	 * Add Linguator Options to Site Health Information tab.
	 *
	 * @param array $debug_info The debug information to be added to the core information page.
	 *
	 * @return array
	 */
	public function info_options( $debug_info ) {
		$fields = $this->model->options->get_site_health_info();

		// Get effective translated post types and taxonomies. The options doesn't show all translated ones.
		if ( ! empty( $this->model->get_translated_post_types() ) ) {
			$fields['cpt']['label'] = __( 'Post Types', 'translate-words' );
			$fields['cpt']['value'] = implode( ', ', $this->model->get_translated_post_types() );
		}
		if ( ! empty( $this->model->get_translated_taxonomies() ) ) {
			$fields['taxonomies']['label'] = __( 'Custom Taxonomies', 'translate-words' );
			$fields['taxonomies']['value'] = implode( ', ', $this->model->get_translated_taxonomies() );
		}

		$fields = $this->normalize_site_health_fields( $fields );

		$debug_info['lmat_options'] = array(
			/* translators: placeholder is the plugin name */
			'label'  => sprintf( __( '%s options', 'translate-words' ), LINGUATOR ),
			'fields' => $fields,
		);

		return $debug_info;
	}

	/**
	 * Adds Linguator Languages settings to Site Health Information tab.
	 *
	 *
	 * @param array $debug_info The debug information to be added to the core information page.
	 * @return array
	 */
	public function info_languages( $debug_info ) {
		foreach ( $this->model->get_languages_list() as $language ) {
			$fields = array();

			foreach ( $language->to_array() as $key => $value ) {
				if ( in_array( $key, $this->exclude_language_keys(), true ) ) {
					continue;
				}

				$fields[ $key ]['label'] = $key;

				if ( 'term_props' === $key && is_array( $value ) ) {
					$fields[ $key ]['value'] = $this->get_info_term_props( $value );
				} else {
					$fields[ $key ]['value'] = $this->normalize_site_health_value( $value );
				}

				if ( 'term_group' === $key ) {
					$fields[ $key ]['label'] = 'order'; // Changed for readability but not translated as other keys are not.
				}
			}

			$fields = $this->normalize_site_health_fields( $fields );

			$debug_info[ 'lmat_language_' . $language->slug ] = array(
				/* translators: %1$s placeholder is the language name, %2$s is the language code */
				'label'  => sprintf( __( 'Language: %1$s - %2$s', 'translate-words' ), $language->name, $language->slug ),
				/* translators: placeholder is the flag image */
				'description' => sprintf( esc_html__( 'Flag used in the language switcher: %s', 'translate-words' ), $this->get_flag( $language ) ),
				'fields' => $fields,
			);
		}

		return $debug_info;
	}

	/**
	 * Ensures a Site Health "fields" array matches WP expected shape.
	 *
	 * Each field must be an array with at least 'label' and 'value' keys.
	 * If a value isn't an array, it is converted into a field array.
	 *
	 * @param array $fields
	 * @return array
	 */
	private function normalize_site_health_fields( array $fields ) {
		foreach ( $fields as $key => $field ) {
			if ( ! is_array( $field ) ) {
				$fields[ $key ] = array(
					'label' => (string) $key,
					'value' => $this->normalize_site_health_value( $field ),
				);
				continue;
			}

			if ( ! array_key_exists( 'label', $field ) ) {
				$fields[ $key ]['label'] = (string) $key;
			}

			if ( ! array_key_exists( 'value', $field ) ) {
				$fields[ $key ]['value'] = '';
			} else {
				$fields[ $key ]['value'] = $this->normalize_site_health_value( $field['value'] );
			}
		}

		return $fields;
	}

	/**
	 * Normalizes a field value into a scalar/string for Site Health display.
	 *
	 * @param mixed $value
	 * @return string
	 */
	private function normalize_site_health_value( $value ) {
		if ( null === $value || false === $value || '' === $value ) {
			return '0';
		}

		if ( true === $value ) {
			return '1';
		}

		if ( is_scalar( $value ) ) {
			return (string) $value;
		}

		if ( is_array( $value ) ) {
			return $this->format_array_for_site_health_info( $value );
		}

		// Objects/resources: fall back to JSON-ish string for debug visibility.
		return (string) wp_json_encode( $value );
	}

	/**
	 * Adds term props data to the info languages array.
	 *
	 * @param array $value The term props data.
	 * @return array The term props data formatted for the info languages tab.
	 */
	protected function get_info_term_props( $value ) {
		$return_value = array();

		foreach ( $value as $language_taxonomy => $item ) {
			$language_taxonomy_array = array_fill( 0, count( $item ), $language_taxonomy );

			$keys_with_language_taxonomy = array_map(
				function ( $key, $language_taxonomy ) {
					return "{$language_taxonomy}/{$key}";
				},
				array_keys( $item ),
				$language_taxonomy_array
			);

			$value = array_combine( $keys_with_language_taxonomy, $item );
			if ( is_array( $value ) ) {
				$return_value = array_merge( $return_value, $value );
			}
		}
		return $this->format_array_for_site_health_info( $return_value );
	}

	/**
	 * Formats an associative array for Site Health display.
	 *
	 * WordPress expects each field to be an array containing at least 'label' and 'value'.
	 * When a field value itself is an array, it should be formatted as a string to avoid
	 * WP interpreting it as nested fields.
	 *
	 * @param array $array
	 * @return string
	 */
	private function format_array_for_site_health_info( array $array ) {
		array_walk(
			$array,
			function ( &$value, $key ) {
				if ( is_array( $value ) ) {
					$ids   = implode( ' , ', $value );
					$value = "$key: $ids";
				} else {
					$value = "$key: $value";
				}
			}
		);

		return implode( ' | ', $array );
	}

	/**
	 * Returns the flag used in the language switcher.
	 *
	 *
	 * @param Linguator_Language $language Language object.
	 * @return string
	 */
	protected function get_flag( $language ) {
		$flag = $language->get_display_flag();
		return empty( $flag )
			? '<span>' . esc_html__( 'Undefined', 'translate-words' ) . '</span>'
			: wp_kses(
				(string) $flag,
				array(
					'img'  => array(
						'src'      => true,
						'alt'      => true,
						'class'    => true,
						'width'    => true,
						'height'   => true,
						'style'    => true,
						'decoding' => true,
						'loading'  => true,
						'title'    => true,
					),
					'span' => array( 'class' => true, 'style' => true ),
				),
				array_merge( wp_allowed_protocols(), array( 'data' ) )
			);
	}

	/**
	 * Add a Site Health test on homepage translation.
	 *
	 *
	 * @param array $tests Array with tests declaration data.
	 * @return array
	 */
	public function status_tests( $tests ) {
		// Add the test only if the homepage displays static page.
		if ( 'page' === get_option( 'show_on_front' ) && get_option( 'page_on_front' ) ) {
			$tests['direct']['lmat_homepage'] = array(
				'label' => esc_html__( 'Homepage translated', 'translate-words' ),
				'test'  => array( $this, 'homepage_test' ),
			);
		}
		return $tests;
	}

	/**
	 * Test if the home page is translated or not.
	 *
	 *
	 * @return array $result Array with test results.
	 */
	public function homepage_test() {
		$result = array(
			'label'       => __( 'All languages have a translated homepage', 'translate-words' ),
			'status'      => 'good',
			'badge'       => array(
				'label' => LINGUATOR,
				'color' => 'blue',
			),
			'description' => sprintf(
				'<p>%s</p>',
				esc_html__( 'It is mandatory to translate the static front page in all languages.', 'translate-words' )
			),
			'actions'     => '',
			'test'        => 'lmat_homepage',
		);

		$message = $this->static_pages->get_must_translate_message();

		if ( ! empty( $message ) ) {
			$result['status']      = 'critical';
			$result['label']       = __( 'The homepage is not translated in all languages', 'translate-words' );
			$result['description'] = sprintf( '<p>%s</p>', wp_kses_post( $message ) );
		}
		return $result;
	}

	/**
	 * Add Linguator Warnings to Site Health Information tab.
	 *
	 *
	 * @param array $debug_info The debug information to be added to the core information page.
	 * @return array
	 */
	public function info( $debug_info ) {
		$fields = array();

		// Add Post Types without languages.
		$posts_no_lang = $this->get_post_ids_without_lang();

		if ( ! empty( $posts_no_lang ) ) {
			$fields['post-no-lang']['label'] = __( 'Posts without language', 'translate-words' );
			$fields['post-no-lang']['value'] = $this->format_array_for_site_health_info( $posts_no_lang );
		}

		$terms_no_lang = $this->get_term_ids_without_lang();

		if ( ! empty( $terms_no_lang ) ) {
			$fields['term-no-lang']['label'] = __( 'Terms without language', 'translate-words' );
			$fields['term-no-lang']['value'] = $this->format_array_for_site_health_info( $terms_no_lang );
		}

		// Multisite
		if ( is_multisite() ) {
			if ( is_plugin_active_for_network( LINGUATOR_BASENAME ) ) {
				$network_activated = __( 'Yes', 'translate-words' );
			} else {
				$network_activated = __( 'No', 'translate-words' );
			}
			$fields['network_activated'] = array(
				'label' => __( 'Network activated', 'translate-words' ),
				'value' => $network_activated,
			);
		}

		// Create the section.
		if ( ! empty( $fields ) ) {
			$debug_info['lmat_warnings'] = array(
				/* translators: placeholder is the plugin name */
				'label'  => sprintf( __( '%s information', 'translate-words' ), LINGUATOR ),
				'fields' => $fields,
			);
		}

		return $debug_info;
	}

	/**
	 * Get an array with post_type as key and post ids as value.
	 *
	 *
	 * @param int $limit Max number of posts to show per post type. `-1` to return all of them. Default is 5.
	 *
	 * @return array An associative array where the keys are post types and the values
	 *                are comma-separated strings of post IDs without a language.
	 *
	 * @phpstan-param -1|positive-int $limit     *
	 */
	public function get_post_ids_without_lang( $limit = 5 ) {
		$posts = array();

		foreach ( $this->model->get_translated_post_types() as $post_type ) {
			$post_ids_with_no_language = $this->model->get_posts_with_no_lang( $post_type, $limit );

			if ( ! empty( $post_ids_with_no_language ) ) {
					$posts[ $post_type ] = implode( ',', $post_ids_with_no_language );
			}
		}
		return $posts;
	}

	/**
	 * Get an array with taxonomy as key and term ids as value.
	 *
	 * @param int $limit Max number of terms to show per post type. `-1` to return all of them. Default is 5.
	 *
	 * @return array An associative array where the keys are post types and the values
	 *                 are comma-separated strings of post IDs without a language.
	 * @phpstan-param -1|positive-int $limit
	 */
	public function get_term_ids_without_lang( $limit = 5 ) {
		$terms = array();

		foreach ( $this->model->get_translated_taxonomies() as $taxonomy ) {
			$term_ids_with_no_language = $this->model->get_terms_with_no_lang( $taxonomy, $limit );

			if ( ! empty( $term_ids_with_no_language ) ) {
				$terms[ $taxonomy ] = implode( ',', $term_ids_with_no_language );
			}
		}
		return $terms;
	}
}

