<?php

declare(strict_types=1);

namespace Signocore\Multilanguage\Application;

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

use Signocore\Multilanguage\Features\LanguageManager;
use Signocore\Multilanguage\Features\PostTranslation;
use Signocore\Multilanguage\Features\TaxonomyTranslation;
use Signocore\Multilanguage\Infrastructure\Database\TranslationRepository;
use Signocore\Multilanguage\Infrastructure\Traits\Singleton;
use Signocore\Multilanguage\Support\LanguageHelper;
use Signocore\Multilanguage\Support\TransientManager;
use Signocore\Multilanguage\Support\TranslationHelper;

final class Ajax
{
	use Singleton;

	protected function init(): void
	{
		add_action('wp_ajax_sml_add_language', [$this, 'handleAddLanguage']);
		add_action('wp_ajax_sml_remove_language', [$this, 'handleRemoveLanguage']);
		add_action('wp_ajax_sml_set_default_language', [$this, 'handleSetDefaultLanguage']);
		add_action('wp_ajax_sml_create_translation', [$this, 'handleCreateTranslation']);
		add_action('wp_ajax_sml_create_term_translation', [$this, 'handleCreateTermTranslation']);
		add_action('wp_ajax_sml_unlink_translation', [$this, 'handleUnlinkTranslation']);
		add_action('wp_ajax_sml_unlink_term_translation', [$this, 'handleUnlinkTermTranslation']);
		add_action('wp_ajax_sml_link_translation', [$this, 'handleLinkTranslation']);
		add_action('wp_ajax_sml_copy_source_content', [$this, 'handleCopySourceContent']);
		add_action('wp_ajax_sml_search_linkable_posts', [$this, 'handleSearchLinkablePosts']);
		add_action('wp_ajax_sml_purge_license', [$this, 'handlePurgeLicense']);
	}

	/**
	 * Handle adding a language.
	 */
	public function handleAddLanguage(): void
	{
		$this->verifyRequest();

		$code = sanitize_text_field($_POST['code'] ?? '');
		if ($code === '') {
			wp_send_json_error(__('Language code is required.', 'signocore-multilanguage'));
		}

		$lang = LanguageHelper::getByCode($code);
		if ($lang === null) {
			wp_send_json_error(__('Invalid language code.', 'signocore-multilanguage'));
		}

		$manager = LanguageManager::load();

		if ($manager->isActiveLanguage($code)) {
			wp_send_json_error(__('This language is already active.', 'signocore-multilanguage'));
		}

		if (!LicenseManager::load()->isPro() && count($manager->getActiveLanguages()) >= Entitlements::FREE_LANGUAGE_LIMIT) {
			wp_send_json_error(sprintf(
				/* translators: %d: maximum number of languages on the free plan */
				__('The free plan supports up to %d languages. Upgrade to Pro for unlimited languages.', 'signocore-multilanguage'),
				Entitlements::FREE_LANGUAGE_LIMIT
			));
		}

		$result = $manager->addLanguage([
			'code' => $lang['code'],
			'locale' => $lang['locale'],
			'name' => $lang['name'],
			'native_name' => $lang['native_name'],
			'is_rtl' => (int) $lang['rtl'],
		]);

		if ($result === false) {
			wp_send_json_error(__('Failed to add language.', 'signocore-multilanguage'));
		}

		wp_send_json_success([
			'code' => $lang['code'],
			'name' => $lang['name'],
		]);
	}

	/**
	 * Handle removing a language.
	 */
	public function handleRemoveLanguage(): void
	{
		$this->verifyRequest();

		$code = sanitize_text_field($_POST['code'] ?? '');
		if ($code === '') {
			wp_send_json_error(__('Language code is required.', 'signocore-multilanguage'));
		}

		$manager = LanguageManager::load();

		if (!$manager->isActiveLanguage($code)) {
			wp_send_json_error(__('Language not found.', 'signocore-multilanguage'));
		}

		$default = $manager->getDefaultLanguage();
		if ($default !== null && $default->code === $code) {
			wp_send_json_error(__('Cannot remove the default language. Set a different default language first.', 'signocore-multilanguage'));
		}

		$result = $manager->removeLanguage($code);

		if (!$result) {
			wp_send_json_error(__('Failed to remove language.', 'signocore-multilanguage'));
		}

		wp_send_json_success();
	}

	/**
	 * Handle setting the default language.
	 */
	public function handleSetDefaultLanguage(): void
	{
		$this->verifyRequest();

		$code = sanitize_text_field($_POST['code'] ?? '');
		if ($code === '') {
			wp_send_json_error(__('Language code is required.', 'signocore-multilanguage'));
		}

		$manager = LanguageManager::load();

		if (!$manager->isActiveLanguage($code)) {
			wp_send_json_error(__('Language not found.', 'signocore-multilanguage'));
		}

		$result = $manager->setDefaultLanguage($code);

		if (!$result) {
			wp_send_json_error(__('Failed to set default language.', 'signocore-multilanguage'));
		}

		wp_send_json_success();
	}

	/**
	 * Create a translation of a post and redirect to its editor.
	 */
	public function handleCreateTranslation(): void
	{
		$this->verifyRequest();

		$sourceId = (int) ($_GET['source_id'] ?? 0);
		$langCode = sanitize_text_field($_GET['lang'] ?? '');

		if ($sourceId <= 0 || $langCode === '') {
			wp_die(esc_html__('Invalid request parameters.', 'signocore-multilanguage'));
		}

		$newPostId = PostTranslation::load()->createTranslation($sourceId, $langCode);

		if ($newPostId === false) {
			$postType = get_post_type($sourceId) ?: 'post';
			$redirectUrl = ($postType === 'attachment')
				? admin_url('upload.php?sml_error=create_failed')
				: admin_url('edit.php?post_type=' . $postType . '&sml_error=create_failed');
			wp_safe_redirect($redirectUrl);
			exit;
		}

		$editUrl = get_edit_post_link($newPostId, 'raw');
		if ($editUrl === null) {
			$editUrl = admin_url('post.php?post=' . $newPostId . '&action=edit');
		}

		wp_safe_redirect($editUrl);
		exit;
	}

	/**
	 * Create a translation of a taxonomy term and redirect to its editor.
	 */
	public function handleCreateTermTranslation(): void
	{
		$this->verifyRequest();

		$sourceId = (int) ($_GET['source_id'] ?? 0);
		$taxonomy = sanitize_text_field($_GET['taxonomy'] ?? '');
		$langCode = sanitize_text_field($_GET['lang'] ?? '');

		if ($sourceId <= 0 || $taxonomy === '' || $langCode === '') {
			wp_die(esc_html__('Invalid request parameters.', 'signocore-multilanguage'));
		}

		$newTermId = TaxonomyTranslation::load()->createTranslation($sourceId, $taxonomy, $langCode);

		if ($newTermId === false) {
			wp_safe_redirect(admin_url('edit-tags.php?taxonomy=' . $taxonomy . '&sml_error=create_failed'));
			exit;
		}

		$editUrl = get_edit_term_link($newTermId, $taxonomy);
		if ($editUrl === null) {
			$editUrl = admin_url('edit-tags.php?taxonomy=' . $taxonomy);
		}

		wp_safe_redirect($editUrl);
		exit;
	}

	/**
	 * Unlink a translation from its group and redirect back.
	 */
	public function handleUnlinkTranslation(): void
	{
		$this->verifyRequest();

		$postId = (int) ($_GET['post_id'] ?? 0);
		$langCode = sanitize_text_field($_GET['lang'] ?? '');

		if ($postId <= 0 || $langCode === '') {
			wp_die(esc_html__('Invalid request parameters.', 'signocore-multilanguage'));
		}

		PostTranslation::load()->unlinkTranslation($postId, $langCode);

		$referer = wp_get_referer();
		wp_safe_redirect($referer ?: admin_url('edit.php'));
		exit;
	}

	/**
	 * Unlink a term translation from its group and redirect back.
	 */
	public function handleUnlinkTermTranslation(): void
	{
		$this->verifyRequest();

		$termId = (int) ($_GET['term_id'] ?? 0);
		$taxonomy = sanitize_text_field($_GET['taxonomy'] ?? '');
		$langCode = sanitize_text_field($_GET['lang'] ?? '');

		if ($termId <= 0 || $taxonomy === '' || $langCode === '') {
			wp_die(esc_html__('Invalid request parameters.', 'signocore-multilanguage'));
		}

		TaxonomyTranslation::load()->unlinkTranslation($termId, $taxonomy, $langCode);

		$referer = wp_get_referer();
		wp_safe_redirect($referer ?: admin_url('edit-tags.php?taxonomy=' . $taxonomy));
		exit;
	}

	/**
	 * Link an existing post as a translation of the source post.
	 */
	public function handleLinkTranslation(): void
	{
		$this->verifyRequest();

		$sourceId = (int) ($_POST['source_id'] ?? 0);
		$targetId = (int) ($_POST['target_id'] ?? 0);
		$langCode = sanitize_text_field($_POST['lang'] ?? '');

		if ($sourceId <= 0 || $targetId <= 0 || $langCode === '') {
			wp_send_json_error(__('Invalid parameters.', 'signocore-multilanguage'));
		}

		$result = PostTranslation::load()->linkTranslation($sourceId, $targetId, $langCode);

		if (!$result) {
			wp_send_json_error(__('Could not link post. It may already belong to another translation group.', 'signocore-multilanguage'));
		}

		wp_send_json_success([
			'post_id' => $targetId,
			'edit_url' => get_edit_post_link($targetId, 'raw'),
		]);
	}

	/**
	 * Copy content from a source post into the target post.
	 */
	public function handleCopySourceContent(): void
	{
		$this->verifyRequest();

		$targetPostId = (int) ($_POST['target_post_id'] ?? 0);
		$sourcePostId = (int) ($_POST['source_post_id'] ?? 0);

		if ($targetPostId <= 0 || $sourcePostId <= 0) {
			wp_send_json_error(__('Invalid parameters.', 'signocore-multilanguage'));
		}

		$sourcePost = get_post($sourcePostId);
		if ($sourcePost === null) {
			wp_send_json_error(__('Source post not found.', 'signocore-multilanguage'));
		}

		// Verify posts are in the same translation group
		$sourceType = TranslationHelper::resolveElementType($sourcePostId);
		$targetType = TranslationHelper::resolveElementType($targetPostId);
		$sourceGroupId = TranslationRepository::getGroupId($sourcePostId, $sourceType);
		$targetGroupId = TranslationRepository::getGroupId($targetPostId, $targetType);

		if ($sourceGroupId === null || $targetGroupId === null || $sourceGroupId !== $targetGroupId) {
			wp_send_json_error(__('Posts are not in the same translation group.', 'signocore-multilanguage'));
		}

		$result = wp_update_post([
			'ID' => $targetPostId,
			'post_title' => $sourcePost->post_title,
			'post_content' => $sourcePost->post_content,
			'post_excerpt' => $sourcePost->post_excerpt,
		], true);

		if (is_wp_error($result)) {
			wp_send_json_error($result->get_error_message());
		}

		// Copy post meta from source to target
		$this->copyPostMeta($sourcePostId, $targetPostId);

		wp_send_json_success([
			'title' => $sourcePost->post_title,
			'content' => $sourcePost->post_content,
			'excerpt' => $sourcePost->post_excerpt,
		]);
	}

	/**
	 * Copy all post meta from one post to another, excluding internal keys.
	 */
	private function copyPostMeta(int $sourcePostId, int $targetPostId): void
	{
		$excludedKeys = [
			'_edit_lock',
			'_edit_last',
			'_wp_old_slug',
			'_wp_trash_meta_status',
			'_wp_trash_meta_time',
			'_wp_trash_meta_comments',
			'_wp_trash_meta_reason',
			'_encloseme',
			'_pingme',
		];

		/** @var array<string, array<int, mixed>> $sourceMeta */
		$sourceMeta = get_post_meta($sourcePostId);

		/** @var array<string> $excludedKeys */
		$excludedKeys = apply_filters('sml_copy_content_excluded_meta_keys', $excludedKeys, $sourcePostId, $targetPostId);

		foreach ($sourceMeta as $key => $values) {
			if (in_array($key, $excludedKeys, true)) {
				continue;
			}

			delete_post_meta($targetPostId, $key);

			foreach ($values as $value) {
				add_post_meta($targetPostId, $key, maybe_unserialize($value));
			}
		}
	}

	/**
	 * Search for posts that can be linked as translations.
	 */
	public function handleSearchLinkablePosts(): void
	{
		$this->verifyRequest();

		$search = sanitize_text_field($_POST['search'] ?? '');
		$postType = sanitize_text_field($_POST['post_type'] ?? 'post');
		$excludePostId = (int) ($_POST['exclude_post_id'] ?? 0);

		if ($search === '') {
			wp_send_json_success(['posts' => []]);
		}

		global $wpdb;

		$table = $wpdb->prefix . Constants::TABLE_TRANSLATIONS;
		$elementType = 'post_' . $postType;

		// Find posts of the same type that are NOT already in a multi-post translation group.
		// A post is linkable if:
		// 1. It has no translation record at all, OR
		// 2. It has a standalone record (sole member of its group)
		/** @var array<int, object> $posts */
		$posts = $wpdb->get_results($wpdb->prepare(
			"SELECT p.ID, p.post_title, p.post_status
			 FROM {$wpdb->posts} p
			 LEFT JOIN `{$table}` t ON p.ID = t.element_id AND t.element_type = %s
			 WHERE p.post_type = %s
			   AND p.post_status IN ('publish', 'draft')
			   AND p.post_title LIKE %s
			   AND p.ID != %d
			   AND (
			     t.id IS NULL
			     OR t.group_id IN (
			       SELECT g.group_id FROM `{$table}` g
			       WHERE g.group_id = t.group_id
			       GROUP BY g.group_id HAVING COUNT(*) = 1
			     )
			   )
			 ORDER BY p.post_title ASC
			 LIMIT 10",
			$elementType,
			$postType,
			'%' . $wpdb->esc_like($search) . '%',
			$excludePostId
		));

		$results = [];
		foreach ($posts as $post) {
			$results[] = [
				'id' => (int) $post->ID,
				'title' => $post->post_title,
				'status' => $post->post_status,
			];
		}

		wp_send_json_success(['posts' => $results]);
	}

	/**
	 * Purge (disable) the license key and email.
	 */
	public function handlePurgeLicense(): void
	{
		$this->verifyRequest();

		delete_option(Constants::OPTION_PREFIX . 'license_key');
		delete_option(Constants::OPTION_PREFIX . 'license_email');

		LicenseManager::load()->purge();
		TransientManager::invalidatePluginUpdate();

		wp_send_json_success();
	}

	/**
	 * Verify nonce and user capability.
	 */
	private function verifyRequest(): void
	{
		if (!check_ajax_referer(Constants::AJAX_NONCE, '_ajax_nonce', false)) {
			wp_send_json_error(__('Security check failed.', 'signocore-multilanguage'), 403);
		}

		if (!current_user_can('manage_options')) {
			wp_send_json_error(__('You do not have permission to perform this action.', 'signocore-multilanguage'), 403);
		}
	}
}
