<?php
/**
 * Inject language slugs into generated permalinks.
 *
 * @package WPR_Translate
 */

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

require_once __DIR__ . '/language-manager.php';

$GLOBALS['wpr_translate_language_cache'] = $GLOBALS['wpr_translate_language_cache'] ?? array();

/**
 * Hook into the various permalink filters to inject language-aware URLs.
 *
 * @return void
 */
function wpr_translate_language_permalinks_bootstrap() {
    // Mirror WordPress permalink filters so every generated link is localized consistently.
    add_filter( 'post_type_link', 'wpr_translate_filter_post_type_link', 10, 4 );
    add_filter( 'post_link', 'wpr_translate_filter_post_link', 10, 3 );
    add_filter( 'page_link', 'wpr_translate_filter_page_link', 10, 3 );
    add_filter( 'term_link', 'wpr_translate_filter_term_link', 10, 3 );
    add_filter( 'post_type_archive_link', 'wpr_translate_filter_post_type_archive_link', 10, 2 );
    add_filter( 'home_url', 'wpr_translate_filter_home_url', 10, 4 );
}

/**
 * Apply language prefixes to custom post type permalinks.
 *
 * @param string  $permalink Generated permalink.
 * @param WP_Post $post      Post object associated with the link.
 * @param bool    $leavename Whether the post name should remain unaltered.
 * @param bool    $sample    Whether the link represents a sample preview.
 *
 * @return string Localized permalink.
 */
function wpr_translate_filter_post_type_link( $permalink, $post, $leavename, $sample ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter
    return wpr_translate_localise_post_link( $permalink, $post );
}

/**
 * Apply language prefixes to standard post permalinks.
 *
 * @param string  $permalink Generated permalink.
 * @param WP_Post $post      Post object for the link.
 * @param bool    $leavename Whether to keep the post name placeholder.
 *
 * @return string Localized permalink.
 */
function wpr_translate_filter_post_link( $permalink, $post, $leavename ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter
    return wpr_translate_localise_post_link( $permalink, $post );
}

/**
 * Apply language prefixes to page permalinks.
 *
 * @param string $link     Generated permalink.
 * @param int    $post_id  Page identifier.
 * @param bool   $sample   Whether this is a sample URL.
 *
 * @return string Localized permalink.
 */
function wpr_translate_filter_page_link( $link, $post_id, $sample ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter
    // Unlike posts, page filters provide an ID rather than the post object, so retrieve it manually.
    $post = get_post( $post_id );

    // Reuse the post link localization helper.
    return wpr_translate_localise_post_link( $link, $post );
}

/**
 * Apply language prefixes to taxonomy term links.
 *
 * @param string  $url      Generated term URL.
 * @param WP_Term $term     Term object.
 * @param string  $taxonomy Taxonomy slug.
 *
 * @return string Filtered URL pointing to the correct language context.
 */
function wpr_translate_filter_term_link( $url, $term, $taxonomy ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter
    if ( ! $term instanceof WP_Term ) {
        // Skip when the term payload is invalid.
        return $url;
    }

    // Resolve the term's language mapping first.
    $language = wpr_translate_get_element_language( $term->term_id, 'tax_' . $term->taxonomy );

    if ( null === $language ) {
        // Fall back to the visitor's current language before ultimately defaulting to the global language.
        $language = wpr_translate_get_current_language();

        if ( null === $language ) {
            $language = wpr_translate_get_default_language();
        }
    }

    return wpr_translate_prefix_url_for_language( $url, $language );
}

/**
 * Apply language prefixes to custom post type archive links.
 *
 * @param string $link      Generated archive URL.
 * @param string $post_type Post type slug.
 *
 * @return string Localized archive link.
 */
function wpr_translate_filter_post_type_archive_link( $link, $post_type ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter
    // Use the current language context for archive links.
    $language = wpr_translate_get_current_language();

    if ( null === $language ) {
        $language = wpr_translate_get_default_language();
    }

    return wpr_translate_prefix_url_for_language( $link, $language );
}

/**
 * Apply language prefixes to the site home URL helper.
 *
 * @param string      $url        Generated home URL.
 * @param string      $path       Requested path.
 * @param string|null $orig_scheme Scheme being used.
 * @param int|null    $blog_id    Optional multisite blog identifier.
 *
 * @return string Localized home URL.
 */
function wpr_translate_filter_home_url( $url, $path, $orig_scheme, $blog_id ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter
    // Use the active language for home URLs.
    $language = wpr_translate_get_current_language();

    if ( null === $language ) {
        $language = wpr_translate_get_default_language();
    }

    return wpr_translate_prefix_url_for_language( $url, $language );
}

/**
 * Localize any post URL by inspecting its translation metadata.
 *
 * @param string $permalink Generated permalink.
 * @param WP_Post|false $post Post object referenced by the URL.
 *
 * @return string Filtered permalink.
 */
function wpr_translate_localise_post_link( $permalink, $post ) {
    if ( empty( $permalink ) || ! $post instanceof WP_Post ) {
        // Bail out when there is no post or permalink to localize.
        return $permalink;
    }

    // Resolve the post-specific language code to ensure the URL matches its translation group.
    $language = wpr_translate_get_element_language( $post->ID, 'post_' . $post->post_type );

    if ( null === $language ) {
        // Fall back to the default language when no mapping exists.
        $language = wpr_translate_get_default_language();
    }

    return wpr_translate_prefix_url_for_language( $permalink, $language );
}

/**
 * Resolve the language metadata for a given element using cached lookups.
 *
 * @param int    $element_id   Element identifier.
 * @param string $element_type Element type string (post_ or tax_ prefix).
 *
 * @return array|null Language payload or null when not mapped.
 */
function wpr_translate_get_element_language( $element_id, $element_type ) {
    global $wpdb, $wpr_translate_language_cache;

    // Cache lookups by element type and ID.
    $cache_key = $element_type . ':' . absint( $element_id );

    if ( array_key_exists( $cache_key, $wpr_translate_language_cache ) ) {
        return $wpr_translate_language_cache[ $cache_key ];
    }

    $table = $wpdb->prefix . 'wpestate_translation_translations';
    // Load the language code from the translations table.
    $code  = $wpdb->get_var(
        $wpdb->prepare(
            "SELECT language_code FROM {$table} WHERE element_id = %d AND element_type = %s LIMIT 1",
            $element_id,
            $element_type
        )
    );

    if ( ! empty( $code ) ) {
        // Resolve the language payload from the stored code.
        $language = wpr_translate_get_language( $code );

        if ( null !== $language ) {
            // Cache successful lookups to avoid repeated database work during the same request.
            $wpr_translate_language_cache[ $cache_key ] = $language;

            return $language;
        }
    }

    // Cache misses as null so repeated lookups short-circuit quickly.
    $wpr_translate_language_cache[ $cache_key ] = null;

    return null;
}

/**
 * Prepend the appropriate language slug to a URL.
 *
 * @param string     $url      URL to filter.
 * @param array|null $language Language payload.
 *
 * @return string Either the localized URL or the original when localization is not possible.
 */
function wpr_translate_prefix_url_for_language( $url, $language ) {
    if ( empty( $url ) || empty( $language ) || ! is_array( $language ) ) {
        // Return the original URL when localization is not possible.
        return $url;
    }

    // Delegate to the shared URL localization helper.
    return wpr_translation_localize_url_for_language( $url, $language );
}
