<?php

namespace WeglotWP\Services;

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

use Weglot\Client\Api\LanguageEntry;
use Weglot\Client\Api\LanguageCollection;
use Weglot\Client\Endpoint\LanguagesList;


/**
 * Language service
 *
 * @since 2.0
 */
class Language_Service_Weglot {
	/**
	 * @var LanguageCollection|null
	 */
	protected $languages = null;
	/**
	 * @var Option_Service_Weglot
	 */
	private $option_services;

	/**
	 * @since 2.0
	 */
	public function __construct() {
		$this->option_services = weglot_get_service( Option_Service_Weglot::class );
	}

	/**
	 * @param array<string,mixed> $a
	 * @param array<string,mixed> $b
	 *
	 * @return int
	 * @since 2.0.6
	 */
	protected function compare_language( $a, $b ) {
		return strcmp( $a['english'], $b['english'] );
	}

	/**
	 * Get defaults languages available from API
	 *
	 * @param array<string|int,mixed> $params
	 *
	 * @return LanguageCollection
	 * @since 2.0
	 * @version 2.0.6
	 */
	public function get_languages_available( $params = array() ) {
		if ( null !== $this->languages ) {
			return $this->languages;
		}

		$client = weglot_get_service( Parser_Service_Weglot::class )->get_client();

		$languages = new LanguagesList( $client );

		$this->languages = $languages->handle();

		// We add the weglot_language_code_replace filter for custom code
		$this->languages = $this->languages->jsonSerialize();

		if ( isset( $params['sort'] ) && $params['sort'] ) {
			usort( $this->languages, array( $this, 'compare_language' ) );
		}

		$language_collection = new LanguageCollection();
		foreach ( $this->languages as $language ) {
			$language_code_rewrited = apply_filters( 'weglot_language_code_replace', array() );
			$external_code          = array_key_exists( $language['internal_code'], $language_code_rewrited ) ? $language_code_rewrited[ $language['internal_code'] ] : $language['external_code'];
			$entry                  = new LanguageEntry( $language['internal_code'], $external_code, $language['english'], $language['local'], $language['rtl'] );
			$language_collection->addOne( $entry );
		}
		$this->languages = $language_collection;

		return $this->languages;
	}

	/**
	 * Adds a language to the language collection
	 *
	 * @param string $internal_code
	 * @param string $external_code
	 * @param LanguageEntry|string $english_name
	 * @param LanguageEntry|string $local_name
	 * @param bool $is_rtl
	 *
	 * @return LanguageCollection|null
	 */
	public function add_language( $internal_code, $external_code, $english_name, $local_name, $is_rtl = false ) {
		if ($english_name instanceof LanguageEntry) {
			$english_name = $english_name->getEnglishName();
		}

		if ($local_name instanceof LanguageEntry) {
			$local_name = $local_name->getLocalName();
		}

		$entry = new LanguageEntry( $internal_code, $external_code, $english_name, $local_name, $is_rtl );
		$this->languages->addOne( $entry );

		return $this->languages;
	}

	/**
	 * Get all languages : list of 109 default languages merged with custom languages taken from options
	 *
	 * @return LanguageCollection|null
	 * @throws \Exception
	 */

	public function get_all_languages() {
		if ( null !== $this->languages ) {
			return $this->languages;
		}

		$this->languages       = $this->get_languages_available( array( 'sort' => true ) );
		$original_language     = $this->get_original_language();

		if ( null !== $original_language && ! empty( $this->get_original_language_name_custom() ) ) {
			$this->languages = $this->add_language( $original_language->getInternalCode(), $original_language->getExternalCode(), $this->get_original_language_name_custom(), $this->get_original_language_name_custom(), $original_language->isRtl() );
		}

		$destination_languages = $this->option_services->get_destination_languages();
		if ( ! is_array( $destination_languages ) ) {
			$destination_languages = array();
		}

		foreach ( $destination_languages as $d ) {
			/** @var LanguageEntry|null $language_data */
			$language_data = $this->languages->getCode( $d['language_to'] );
			if ( ! $language_data ) {
				$this->languages = $this->add_language( $d['language_to'], $d['custom_code'], $d['custom_name'], $d['custom_local_name'] );
			} else {
				$external_code = $d['custom_code'] ?? $language_data->getExternalCode();
				$custom_local_name = $d['custom_name'] ?? $language_data->getLocalName(); // TODO: remove after core fixes bug.
				$custom_local_name = $d['custom_local_name'] ?? $custom_local_name;
				$custom_name       = $d['custom_name'] ?? $language_data->getEnglishName();
				$this->languages   = $this->add_language( $d['language_to'], $external_code, $custom_name, $custom_local_name, $language_data->isRtl() ); // côté core : il faut que le nom qui change soit le custom_local_name.
			}
		}

		return $this->languages;
	}

	/**
	 * Get language entry from the internal code
	 *
	 * @param string $internal_code
	 *
	 * @return LanguageEntry|null
	 * @since 3.2.1
	 */
	public function get_language_from_internal( $internal_code ) {
		$languages = $this->get_all_languages();
		if ( ! $languages ) {
			return null;
		}

		return $languages->getCode( $internal_code );
	}

	/**
	 * Get language entry from the external code
	 *
	 * @param string $external_code
	 *
	 * @return LanguageEntry|null
	 * @since 3.2.1
	 */
	public function get_language_from_external( $external_code ) {
		foreach ( $this->get_all_languages() as $language ) {
			if ( $language->getExternalCode() === $external_code ) {
				return $language;
			}
		}

		return null;
	}

	/**
	 * Get destination languages as language entries
	 *
	 * @param bool $allowed_private
	 * @param array<string> $excluded_languages
	 *
	 * @return LanguageEntry[]
	 */
	public function get_destination_languages( $allowed_private = false, array $excluded_languages = array() ) {
		$destination_languages_as_array = $this->option_services->get_destination_languages();
		if ( ! is_array( $destination_languages_as_array ) ) {
			$destination_languages_as_array = array();
		}
		$destination_languages = array();
		foreach ( $destination_languages_as_array as $destination_language_as_array ) {
			$lang_code       = $destination_language_as_array['language_to'];
			$is_public       = ! empty( $destination_language_as_array['public'] ) || $allowed_private;
			$is_not_excluded = $allowed_private || ! in_array( $lang_code, $excluded_languages, true );
			if ( $is_public && $is_not_excluded ) {
				$language = $this->get_language_from_internal( $lang_code );
				if ( $language ) {
					$destination_languages[] = $language;
				}
			}
		}

		return $destination_languages;
	}

	/**
	 * Get destination languages as language entries
	 *
	 * @param bool $allowed_private
	 * @param array<string> $excluded_languages
	 *
	 * @return string[]
	 */
	public function get_destination_languages_external( $allowed_private = false, array $excluded_languages = array() ) {
		return array_map(
			function ( $l ) {
				return $l->getExternalCode();
			},
			$this->get_destination_languages( $allowed_private, $excluded_languages )
		);
	}

	/**
	 * Get original language as language entry
	 *
	 * @return LanguageEntry|null
	 */
	public function get_original_language() {
		$original_language_code = $this->option_services->get_option( 'original_language' );
		if(null === $original_language_code){
			$original_language_code = $this->option_services->get_option( 'language_from' );
		}

		if ( ! is_string( $original_language_code ) || '' === trim( $original_language_code ) ) {
			return null;
		}

		return $this->get_language_from_internal( $original_language_code );
	}

	/**
	 * @return string|null
	 */
	public function get_original_language_name_custom() {
		return $this->option_services->get_option( 'language_from_custom_name' );
	}

	/**
	 * Get original language and destination languages as language entries
	 *
	 * @param bool     $allowed_private
	 * @param string[] $excluded_languages
	 *
	 * @return LanguageEntry[]
	 */
	public function get_original_and_destination_languages( $allowed_private = false, $excluded_languages = array() ) {
		$languages = $this->get_destination_languages( $allowed_private, $excluded_languages );
		array_unshift( $languages, $this->get_original_language() );

		return $languages;
	}
}
