<?php
declare(strict_types=1);

namespace Ekesto\CacheWarmup\Multilingual\Adapters;

use Ekesto\CacheWarmup\Multilingual\LanguageAdapterInterface;
use Ekesto\CacheWarmup\Multilingual\LanguageContextSwitcherInterface;

/**
 * WPML language adapter for multilingual URL and post-language resolution.
 */
class WpmlAdapter implements LanguageAdapterInterface, LanguageContextSwitcherInterface
{
    /**
     * Detect whether WPML language filters are available.
     *
     * @return bool
     */
    public function isActive(): bool
    {
        return defined('ICL_SITEPRESS_VERSION')
            // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML third-party hook name.
            && has_filter('wpml_default_language')
            // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML third-party hook name.
            && has_filter('wpml_element_language_code');
    }

    /**
     * Return the stable adapter key.
     *
     * @return string
     */
    public function getKey(): string
    {
        return 'wpml';
    }

    /**
     * Resolve the WPML default language code.
     *
     * @return string|null Lowercased language code, or null when unavailable.
     */
    public function getDefaultLanguage(): ?string
    {
        if (!$this->isActive()) {
            return null;
        }

        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML third-party hook name.
        return $this->normalizeLanguage(apply_filters('wpml_default_language', null));
    }

    /**
     * Resolve the WPML language code for a specific post.
     *
     * @param int $postId WordPress post ID.
     * @param string $postType Post type key.
     * @return string|null Lowercased language code, or null when unavailable.
     */
    public function getPostLanguage(int $postId, string $postType): ?string
    {
        if (!$this->isActive() || $postId <= 0 || $postType === '') {
            return null;
        }

        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML third-party hook name.
        $lang = apply_filters('wpml_element_language_code', null, [
            'element_id' => $postId,
            'element_type' => 'post_' . $postType,
        ]);

        return $this->normalizeLanguage($lang);
    }

    /**
     * Resolve the WPML language code for a URL.
     *
     * @param string $url Absolute URL.
     * @return string|null Lowercased language code, or null when unavailable.
     */
    public function getUrlLanguage(string $url): ?string
    {
        if (!$this->isActive() || $url === '') {
            return null;
        }

        $postId = url_to_postid($url);
        if ($postId <= 0) {
            return null;
        }

        $postType = get_post_type($postId);
        if (!is_string($postType) || $postType === '') {
            return null;
        }

        return $this->getPostLanguage($postId, $postType);
    }

    /**
     * Resolve a post permalink in the post's own WPML language.
     *
     * @param int $postId WordPress post ID.
     * @param string $postType Post type key.
     * @param string $url Raw permalink URL.
     * @return string Localized permalink URL.
     */
    public function resolvePostPermalink(int $postId, string $postType, string $url): string
    {
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML third-party hook name.
        if ($url === '' || !$this->isActive() || !has_filter('wpml_permalink')) {
            return $url;
        }

        $lang = $this->getPostLanguage($postId, $postType);
        if ($lang === null) {
            return $url;
        }

        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML third-party hook name.
        $localized = apply_filters('wpml_permalink', $url, $lang);
        return is_string($localized) && $localized !== '' ? $localized : $url;
    }

    /**
     * Switch the WPML request context, or restore the original context with null.
     *
     * @param string|null $language Language code or null.
     * @return void
     */
    public function switchLanguage(?string $language): void
    {
        if (!$this->isActive()) {
            return;
        }

        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML third-party hook name.
        do_action('wpml_switch_language', $language);
    }

    /**
     * Normalize language values from third-party filters.
     *
     * @param mixed $language
     * @return string|null
     */
    private function normalizeLanguage($language): ?string
    {
        if (!is_string($language)) {
            return null;
        }

        $language = trim($language);
        return $language !== '' ? strtolower($language) : null;
    }
}
