<?php
/**
 * Canonical and hreflang helpers for the front-end.
 */

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

require_once __DIR__ . '/seo-tags-helpers.php';

/**
 * Register SEO tag hooks.
 */
function wpr_translate_bootstrap_seo_tags() {
    static $bootstrapped = false;

    if ( $bootstrapped ) {
        // Avoid registering hooks multiple times.
        return;
    }

    $bootstrapped = true;

    // Attach SEO-related actions and filters.
    add_action( 'wp_head', 'wpr_translate_render_seo_link_tags', 2 );
    add_filter( 'wpseo_hreflang_links', 'wpr_translate_filter_wpseo_hreflang_links' );
    add_filter( 'wpseo_canonical', 'wpr_translate_filter_wpseo_canonical' );
    add_filter( 'rank_math/frontend/seo/hreflang', 'wpr_translate_filter_rank_math_hreflang' );
    add_filter( 'rank_math/frontend/canonical', 'wpr_translate_filter_rank_math_canonical' );
}

/**
 * Echo link tags in wp_head.
 */
function wpr_translate_render_seo_link_tags() {
    if ( is_admin() || is_feed() || is_preview() ) {
        // Do not output tags in admin, feeds, or previews.
        return;
    }

    // Gather canonical and alternate URLs for the current view.
    $data = wpr_translate_seo_collect_urls();

    if ( empty( $data['alternates'] ) ) {
        // No alternates means nothing to render.
        return;
    }

    foreach ( $data['alternates'] as $hreflang => $url ) {
        if ( '' === $hreflang || '' === $url ) {
            continue;
        }

        // Output the hreflang alternate tag.
        printf('<link rel="alternate" hreflang="%s" href="%s" />' . "\n", esc_attr( $hreflang ), esc_url( $url ) );
    }

    if ( ! empty( $data['canonical'] ) ) {
        // Output the canonical URL tag.
        printf('<link rel="canonical" href="%s" />' . "\n", esc_url( $data['canonical'] ) );
    }
}

/**
 * Supply Yoast SEO with hreflang URLs.
 */
function wpr_translate_filter_wpseo_hreflang_links( $links ) {
    $data = wpr_translate_seo_collect_urls();
    return empty( $data['alternates'] ) ? $links : $data['alternates'];
}

/**
 * Supply Yoast SEO with the canonical URL.
 */
function wpr_translate_filter_wpseo_canonical( $url ) {
    $data = wpr_translate_seo_collect_urls();
    return empty( $data['canonical'] ) ? $url : $data['canonical'];
}

/**
 * Provide Rank Math with hreflang URLs.
 */
function wpr_translate_filter_rank_math_hreflang( $links ) {
    $data = wpr_translate_seo_collect_urls();
    return empty( $data['alternates'] ) ? $links : $data['alternates'];
}

/**
 * Provide Rank Math with the canonical URL.
 */
function wpr_translate_filter_rank_math_canonical( $url ) {
    $data = wpr_translate_seo_collect_urls();
    return empty( $data['canonical'] ) ? $url : $data['canonical'];
}

/**
 * Build canonical and alternate URLs for the current view.
 */
function wpr_translate_seo_collect_urls() {
    static $cache = null;

    if ( null !== $cache ) {
        // Reuse the cached result for this request.
        return $cache;
    }

    $cache = array( 'canonical' => '', 'alternates' => array() );

    if ( is_admin() || wp_doing_ajax() || ! function_exists( 'wpr_translate_get_active_languages' ) ) {
        // Do not build SEO tags in admin or when languages are unavailable.
        return $cache;
    }

    $languages = wpr_translate_get_active_languages();

    if ( empty( $languages ) ) {
        // No active languages means no SEO alternates.
        return $cache;
    }

    // Detect the current query context and build URLs accordingly.
    $context = wpr_translate_seo_resolve_context();

    if ( 'singular' === $context['type'] && $context['object'] instanceof WP_Post ) {
        $cache = wpr_translate_seo_build_post_urls( $context['object'], $languages );
    } elseif ( 'term' === $context['type'] && $context['object'] instanceof WP_Term ) {
        $cache = wpr_translate_seo_build_term_urls( $context['object'], $languages );
    } else {
        $cache = wpr_translate_seo_build_archive_urls( $languages );
    }

    return $cache;
}

/**
 * Identify the current query context.
 */
function wpr_translate_seo_resolve_context() {
    if ( is_singular() ) {
        $object = get_queried_object();

        if ( $object instanceof WP_Post ) {
            // Post singular views use the post context.
            return array( 'type' => 'singular', 'object' => $object );
        }
    }

    if ( is_category() || is_tag() || is_tax() ) {
        $object = get_queried_object();

        if ( $object instanceof WP_Term ) {
            // Term archives use the term context.
            return array( 'type' => 'term', 'object' => $object );
        }
    }

    // Default to archive context for other screens.
    return array( 'type' => 'archive', 'object' => null );
}

/**
 * Build post canonical and alternate URLs.
 */
function wpr_translate_seo_build_post_urls( $post, $languages ) {
    if ( ! function_exists( 'wpr_translation_get_translation_group' ) || ! function_exists( 'wpr_translation_get_element_type' ) ) {
        $url = get_permalink( $post );
        return $url ? array( 'canonical' => $url, 'alternates' => array( 'x-default' => $url ) ) : array( 'canonical' => '', 'alternates' => array() );
    }

    // Resolve the translation group for the post.
    $group       = wpr_translation_get_translation_group( $post->ID, wpr_translation_get_element_type( $post->post_type ) );
    $source_code = isset( $group['source_language'] ) ? sanitize_key( $group['source_language'] ) : '';

    if ( '' === $source_code ) {
        $default_language = wpr_translate_seo_get_default_language( $languages );
        if ( isset( $default_language['code'] ) ) {
            $source_code = sanitize_key( $default_language['code'] );
        }
    }

    // Build canonical and alternate URLs from the translation map.
    return wpr_translate_seo_prepare_group_urls(
        isset( $group['translations'] ) ? $group['translations'] : array(),
        $source_code,
        $post->ID,
        $languages,
        static function( $id, $code ) {
            return wpr_translate_seo_get_post_url( $id, $code );
        },
        static function( $id ) {
            return get_permalink( $id );
        }
    );
}

/**
 * Build term canonical and alternate URLs.
 */
function wpr_translate_seo_build_term_urls( $term, $languages ) {
    if ( ! function_exists( 'wpr_translate_lookup_translated_term_id_strict' ) ) {
        $url = get_term_link( $term );
        return ( $url && ! is_wp_error( $url ) ) ? array( 'canonical' => $url, 'alternates' => array( 'x-default' => $url ) ) : array( 'canonical' => '', 'alternates' => array() );
    }

    $taxonomy          = sanitize_key( $term->taxonomy );
    $canonical_term_id = absint( $term->term_id );

    if ( function_exists( 'wpr_translate_resolve_original_term_id' ) ) {
        $resolved_canonical = absint( wpr_translate_resolve_original_term_id( $canonical_term_id, $taxonomy ) );

        if ( $resolved_canonical > 0 ) {
            $canonical_term_id = $resolved_canonical;
        }
    }

    $default_language = wpr_translate_seo_get_default_language( $languages );
    $source_code      = isset( $default_language['code'] ) ? sanitize_key( $default_language['code'] ) : '';
    $term_map         = array();

    foreach ( $languages as $language ) {
        if ( empty( $language['code'] ) ) {
            continue;
        }

        $language_code = sanitize_key( $language['code'] );
        $target_id     = 0;

        if ( '' !== $source_code && $language_code === $source_code ) {
            $target_id = $canonical_term_id;
        } else {
            $strict_target = wpr_translate_lookup_translated_term_id_strict( $canonical_term_id, $taxonomy, $language_code );

            if ( is_numeric( $strict_target ) ) {
                $target_id = absint( $strict_target );
            }
        }

        if ( $target_id <= 0 ) {
            continue;
        }

        $term_object = get_term( $target_id, $taxonomy );

        if ( ! ( $term_object instanceof WP_Term ) ) {
            continue;
        }

        $term_map[ $language_code ] = $target_id;
    }

    if ( empty( $term_map ) ) {
        $url = get_term_link( $canonical_term_id, $taxonomy );
        return ( $url && ! is_wp_error( $url ) ) ? array( 'canonical' => $url, 'alternates' => array( 'x-default' => $url ) ) : array( 'canonical' => '', 'alternates' => array() );
    }

    // Build canonical and alternate URLs for each language.
    return wpr_translate_seo_prepare_group_urls(
        $term_map,
        $source_code,
        $canonical_term_id,
        $languages,
        static function( $id ) use ( $taxonomy ) {
            return wpr_translate_seo_get_term_url( $id, $taxonomy );
        },
        static function( $id ) use ( $taxonomy ) {
            $link = get_term_link( (int) $id, $taxonomy );
            return ( $link && ! is_wp_error( $link ) ) ? $link : '';
        }
    );
}

/**
 * Build archive canonical and alternate URLs.
 */
function wpr_translate_seo_build_archive_urls( $languages ) {
    // Resolve the default language for the canonical URL.
    $default_language = wpr_translate_seo_get_default_language( $languages );

    if ( function_exists( 'wpr_translate_language_router_get_switch_url' ) && ! empty( $default_language ) ) {
        $canonical = wpr_translate_language_router_get_switch_url( $default_language );
    } else {
        $canonical = home_url( isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '/' );
    }

    if ( '' === $canonical ) {
        // No canonical means no alternates.
        return array( 'canonical' => '', 'alternates' => array() );
    }

    // Seed alternates with the default language.
    $alternates = array( 'x-default' => $canonical );

    if ( function_exists( 'wpr_translate_language_router_get_switch_url' ) ) {
        foreach ( $languages as $language ) {
            if ( empty( $language['code'] ) ) {
                continue;
            }

            $hreflang = wpr_translate_seo_get_hreflang_value( $language );
            $url      = wpr_translate_language_router_get_switch_url( $language );

            if ( '' === $url || '' === $hreflang ) {
                continue;
            }

            $alternates[ $hreflang ] = $url;
        }
    }

    return array( 'canonical' => $canonical, 'alternates' => $alternates );
}
