<?php

declare(strict_types=1);

namespace Signocore\Multilanguage\Support;

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

use Signocore\Multilanguage\Application\Constants;
use Signocore\Multilanguage\Features\LanguageDetection;
use Signocore\Multilanguage\Features\LanguageManager;
use Signocore\Multilanguage\Features\UrlRewriting;
use Signocore\Multilanguage\Infrastructure\Database\LanguageRepository;

final class UrlHelper
{
	/**
	 * Get the URL for the current page in a different language.
	 */
	public static function getUrlForLanguage(string $langCode, ?string $url = null): string
	{
		if ($url === null) {
			$url = self::getCurrentUrl();
		}

		$strategy = get_option(Constants::OPTION_PREFIX . 'url_strategy', Constants::URL_STRATEGY_DIRECTORY);

		$result = match ($strategy) {
			Constants::URL_STRATEGY_DIRECTORY => self::switchDirectoryLanguage($url, $langCode),
			Constants::URL_STRATEGY_SUBDOMAIN => self::switchSubdomainLanguage($url, $langCode),
			Constants::URL_STRATEGY_DOMAIN => self::switchDomainLanguage($url, $langCode),
			default => $url,
		};

		/** @var string */
		return apply_filters('signocore_ml_url_for_language', $result, $langCode, $url);
	}

	/**
	 * Get the current full URL.
	 */
	public static function getCurrentUrl(): string
	{
		$protocol = is_ssl() ? 'https://' : 'http://';
		$host = $_SERVER['HTTP_HOST'] ?? '';

		// Use the original URI (before language prefix stripping) so the
		// returned URL represents what the visitor actually sees.
		$originalUri = UrlRewriting::getOriginalRequestUri();
		$uri = $originalUri !== '' ? $originalUri : ($_SERVER['REQUEST_URI'] ?? '/');

		return $protocol . $host . $uri;
	}

	/**
	 * Remove any language prefix from a URL.
	 */
	public static function removeLanguageFromUrl(string $url): string
	{
		$strategy = get_option(Constants::OPTION_PREFIX . 'url_strategy', Constants::URL_STRATEGY_DIRECTORY);

		if ($strategy === Constants::URL_STRATEGY_DIRECTORY) {
			return self::removeDirectoryPrefix($url);
		}

		return $url;
	}

	/**
	 * Get the language code from a URL.
	 */
	public static function getLanguageFromUrl(string $url): ?string
	{
		$strategy = get_option(Constants::OPTION_PREFIX . 'url_strategy', Constants::URL_STRATEGY_DIRECTORY);

		return match ($strategy) {
			Constants::URL_STRATEGY_DIRECTORY => self::getDirectoryLanguage($url),
			Constants::URL_STRATEGY_SUBDOMAIN => self::getSubdomainLanguage($url),
			Constants::URL_STRATEGY_DOMAIN => self::getDomainLanguage($url),
			default => null,
		};
	}

	/**
	 * Switch the directory language prefix in a URL.
	 */
	private static function switchDirectoryLanguage(string $url, string $newLangCode): string
	{
		$langManager = LanguageManager::load();
		$defaultCode = $langManager->getDefaultLanguageCode();
		$hideDefault = (bool) get_option(Constants::OPTION_PREFIX . 'hide_default_language_prefix', '1');

		// First, remove any existing language prefix
		$cleanUrl = self::removeDirectoryPrefix($url);

		// If target is default and we hide default prefix, return clean URL
		if ($hideDefault && $newLangCode === $defaultCode) {
			return $cleanUrl;
		}

		// Use get_option('home') to avoid the home_url filter that adds language prefix
		$homePath = wp_parse_url(get_option('home'), PHP_URL_PATH);
		$homePath = is_string($homePath) ? rtrim($homePath, '/') : '';

		$parsed = wp_parse_url($cleanUrl);
		if (!is_array($parsed)) {
			return $url;
		}

		$path = $parsed['path'] ?? '/';
		$relativePath = substr($path, strlen($homePath));
		if ($relativePath === '' || $relativePath === false) {
			$relativePath = '/';
		}

		$newPath = $homePath . '/' . $newLangCode . $relativePath;

		$scheme = ($parsed['scheme'] ?? 'https') . '://';
		$host = $parsed['host'] ?? '';
		$port = isset($parsed['port']) ? ':' . $parsed['port'] : '';
		$query = isset($parsed['query']) ? '?' . $parsed['query'] : '';
		$fragment = isset($parsed['fragment']) ? '#' . $parsed['fragment'] : '';

		return $scheme . $host . $port . $newPath . $query . $fragment;
	}

	/**
	 * Remove a directory language prefix from a URL.
	 */
	private static function removeDirectoryPrefix(string $url): string
	{
		$parsed = wp_parse_url($url);
		if (!is_array($parsed) || !isset($parsed['path'])) {
			return $url;
		}

		// Use get_option('home') to avoid the home_url filter that adds language prefix
		$homePath = wp_parse_url(get_option('home'), PHP_URL_PATH);
		$homePath = is_string($homePath) ? rtrim($homePath, '/') : '';
		$path = $parsed['path'];
		$relativePath = substr($path, strlen($homePath));

		$langManager = LanguageManager::load();
		$languages = $langManager->getActiveLanguages();

		foreach ($languages as $lang) {
			$prefix = '/' . $lang->code . '/';
			$prefixNoSlash = '/' . $lang->code;

			if (str_starts_with($relativePath, $prefix)) {
				$newRelative = substr($relativePath, strlen($prefix) - 1);
				$path = $homePath . $newRelative;
				break;
			}

			if ($relativePath === $prefixNoSlash) {
				$path = $homePath . '/';
				break;
			}
		}

		$scheme = ($parsed['scheme'] ?? 'https') . '://';
		$host = $parsed['host'] ?? '';
		$port = isset($parsed['port']) ? ':' . $parsed['port'] : '';
		$query = isset($parsed['query']) ? '?' . $parsed['query'] : '';
		$fragment = isset($parsed['fragment']) ? '#' . $parsed['fragment'] : '';

		return $scheme . $host . $port . $path . $query . $fragment;
	}

	/**
	 * Get the language from a directory-prefixed URL.
	 */
	private static function getDirectoryLanguage(string $url): ?string
	{
		$parsed = wp_parse_url($url);
		$path = $parsed['path'] ?? '/';

		$homePath = wp_parse_url(get_option('home'), PHP_URL_PATH);
		$homePath = is_string($homePath) ? rtrim($homePath, '/') : '';
		$relativePath = ltrim(substr($path, strlen($homePath)), '/');

		$segments = explode('/', $relativePath);
		$firstSegment = $segments[0] ?? '';

		$langManager = LanguageManager::load();
		return $langManager->isActiveLanguage($firstSegment) ? $firstSegment : null;
	}

	/**
	 * Switch subdomain language in a URL.
	 */
	private static function switchSubdomainLanguage(string $url, string $newLangCode): string
	{
		$parsed = wp_parse_url($url);
		if (!is_array($parsed) || !isset($parsed['host'])) {
			return $url;
		}

		$siteHost = (string) wp_parse_url(get_option('home'), PHP_URL_HOST);
		if ($siteHost === '') {
			return $url;
		}

		$scheme = ($parsed['scheme'] ?? 'https') . '://';
		$path = $parsed['path'] ?? '/';
		$port = isset($parsed['port']) ? ':' . $parsed['port'] : '';
		$query = isset($parsed['query']) ? '?' . $parsed['query'] : '';

		$defaultCode = LanguageManager::load()->getDefaultLanguageCode();
		$hideDefault = (bool) get_option(Constants::OPTION_PREFIX . 'hide_default_language_prefix', '1');

		if ($hideDefault && $newLangCode === $defaultCode) {
			return $scheme . $siteHost . $port . $path . $query;
		}

		return $scheme . $newLangCode . '.' . $siteHost . $port . $path . $query;
	}

	/**
	 * Get language from subdomain URL.
	 */
	private static function getSubdomainLanguage(string $url): ?string
	{
		$parsed = wp_parse_url($url);
		$host = $parsed['host'] ?? '';
		$siteHost = (string) wp_parse_url(get_option('home'), PHP_URL_HOST);

		if (!is_string($siteHost) || !str_ends_with($host, '.' . $siteHost)) {
			return null;
		}

		$subdomain = str_replace('.' . $siteHost, '', $host);
		$langManager = LanguageManager::load();

		return $langManager->isActiveLanguage($subdomain) ? $subdomain : null;
	}

	/**
	 * Switch custom domain language in a URL.
	 */
	private static function switchDomainLanguage(string $url, string $newLangCode): string
	{
		$lang = LanguageRepository::getByCode($newLangCode);
		if ($lang === null || empty($lang->domain)) {
			return $url;
		}

		$parsed = wp_parse_url($url);
		if (!is_array($parsed)) {
			return $url;
		}

		$scheme = ($parsed['scheme'] ?? 'https') . '://';
		$path = $parsed['path'] ?? '/';
		$port = isset($parsed['port']) ? ':' . $parsed['port'] : '';
		$query = isset($parsed['query']) ? '?' . $parsed['query'] : '';

		return $scheme . $lang->domain . $port . $path . $query;
	}

	/**
	 * Check if a language is reachable via URL in the current strategy.
	 *
	 * For domain strategy, languages without a configured domain are unreachable.
	 * Directory and subdomain strategies generate URLs automatically.
	 */
	public static function isLanguageReachable(string $langCode): bool
	{
		$strategy = get_option(Constants::OPTION_PREFIX . 'url_strategy', Constants::URL_STRATEGY_DIRECTORY);

		if ($strategy !== Constants::URL_STRATEGY_DOMAIN) {
			return true;
		}

		$lang = LanguageRepository::getByCode($langCode);
		return $lang !== null && !empty($lang->domain);
	}

	/**
	 * Get the backing page ID for the current page-backed archive.
	 *
	 * Returns null if the current page is not a page-backed archive.
	 */
	public static function getPageBackedArchiveId(): ?int
	{
		if (is_home() && get_option('show_on_front') === 'page') {
			$id = (int) get_option('page_for_posts');
			return $id > 0 ? $id : null;
		}

		if (is_front_page() && get_option('show_on_front') === 'page') {
			$id = (int) get_option('page_on_front');
			return $id > 0 ? $id : null;
		}

		if (function_exists('is_shop') && is_shop()) {
			$id = (int) get_option('woocommerce_shop_page_id');
			return $id > 0 ? $id : null;
		}

		return null;
	}

	/**
	 * Get language from custom domain URL.
	 */
	private static function getDomainLanguage(string $url): ?string
	{
		$parsed = wp_parse_url($url);
		$host = $parsed['host'] ?? '';

		if ($host === '') {
			return null;
		}

		$lang = LanguageRepository::getByDomain($host);
		return $lang !== null ? $lang->code : null;
	}
}
