<?php

if (! defined('ABSPATH')) {
    exit;
} final class PufferGo_Nav_Menu_Translator
{
    public const LOCATIONS = array('puffergo_primary' => 'PufferGo Header', 'puffergo_footer' => 'PufferGo Footer', );

    public const STATUS_OPTION = 'puffergo_nav_menu_status';

    public const STATUS_NONE = 'none';

    public const STATUS_TRANSLATED = 'translated';

    public const STATUS_PARTIAL = 'partial';

    public const STATUS_FAILED = 'failed';

    public const STATUS_SKIPPED = 'skipped';

    public const STATUS_STALE = 'stale';

    public const META_SRCHASH = '_puffergo_menu_srchash';

    public static function init(): void
    {
        add_action('admin_notices', array(__CLASS__, 'maybe_render_admin_notice'));
    }

    public static function maybe_render_admin_notice(): void
    {
        $screen = function_exists('get_current_screen') ? get_current_screen() : null;

        if (! $screen || 'nav-menus' !== $screen->id) {
            return;
        } $has_managed = false;

        foreach (wp_get_nav_menus() as $menu) {
            if ('' !== (string) get_term_meta($menu->term_id, '_puffergo_menu_lang', true)) {
                $has_managed = true;
                break;
            }
        }

if (! $has_managed) {
            return;
        } echo '<div class="notice notice-info"><p>';
        echo esc_html__('Menus labelled “(… · auto-managed)” are generated by PufferGo from the Translate tab. Edits made here are overwritten the next time you refresh that language’s translation.', 'puffergo');
        echo '</p></div>';
    }

    public static function status_for(string $lang): array
    {
        $all = get_option(self::STATUS_OPTION, array());

        if (is_array($all) && isset($all[ $lang ]) && is_array($all[ $lang ])) {
            return $all[ $lang ];
        }

return array('status' => self::STATUS_NONE, 'translatedAt' => '', 'locations' => array(), );
    }

    public static function current_status(string $lang): array
    {
        $record = self::status_for($lang);

        if (self::STATUS_NONE === $record['status']) {
            return $record;
        } $locations = isset($record['locations']) && is_array($record['locations']) ? $record['locations'] : array();
        $changed = false;

        foreach ($locations as $location => $location_status) {
            if (self::STATUS_TRANSLATED !== $location_status) {
                continue;
            }

if (self::is_location_stale($location, $lang)) {
                $locations[ $location ] = self::STATUS_STALE;
                $changed = true;
            }
        }

if ($changed) {
            $record['locations'] = $locations;
            $record['status'] = self::aggregate_status($locations);
        }

return $record;
    }

    private static function is_location_stale(string $location, string $lang): bool
    {
        $source_menu_id = self::ensure_source_menu($location);

        if ($source_menu_id <= 0) {
            return false;
        } $target_menu_id = self::find_managed_menu($lang, $source_menu_id);

        if ($target_menu_id <= 0) {
            return false;
        } $stored = (string) get_term_meta($target_menu_id, self::META_SRCHASH, true);

        if ('' === $stored) {
            return false;
        } $source_items = wp_get_nav_menu_items($source_menu_id);
        $source_items = is_array($source_items) ? $source_items : array();

        return $stored !== self::source_signature($source_items, $lang);
    }

    private static function source_signature(array $source_items, string $lang): string
    {
        $parts = array();

        foreach ($source_items as $item) {
            $page_id = self::linked_page_id($item);
            $resolved = $page_id > 0 ? self::published_translation($page_id, $lang) : 0;
            $resolved_title = $resolved > 0 ? (string) get_post_field('post_title', $resolved) : '';
            $parts[] = implode('|', array((int) $item->ID, (string) $item->title, (int) $item->menu_item_parent, (string) $item->type, (string) $item->object, (int) $item->object_id, (int) $resolved, $resolved_title, (int) $item->menu_order, (string) $item->url, ));
        }

return md5(implode("\n", $parts));
    }

    public static function translate_menus(string $lang): array
    {
        $locations = array();

        if (! PufferGo_Polylang::is_active() || '' === $lang) {
            $record = array('status' => self::STATUS_FAILED, 'translatedAt' => current_time('mysql', true), 'locations' => array(), );
            self::save_status($lang, $record);

            return $record;
        } $default = PufferGo_Polylang::default_language();

        foreach (self::LOCATIONS as $location => $menu_name) {
            try {
                $source_menu_id = self::ensure_source_menu($location);

                if ($source_menu_id <= 0) {
                    $locations[ $location ] = self::STATUS_FAILED;
                    continue;
                }

if ('' !== $default) {
                    PufferGo_Polylang::set_nav_menu_location($location, $default, $source_menu_id);
                } $existing_target = PufferGo_Polylang::get_nav_menu_location($location, $lang);

                if ($existing_target > 0 && $existing_target !== $source_menu_id && '' === (string) get_term_meta($existing_target, '_puffergo_menu_lang', true)) {
                    $locations[ $location ] = self::STATUS_SKIPPED;
                    continue;
                } $target_menu_id = self::translate_one_menu($source_menu_id, $lang, $menu_name);

                if ($target_menu_id > 0) {
                    PufferGo_Polylang::set_nav_menu_location($location, $lang, $target_menu_id);
                    $locations[ $location ] = self::STATUS_TRANSLATED;
                } else {
                    $locations[ $location ] = self::STATUS_FAILED;
                }
            } catch (Exception $e) {
                if (function_exists('puffergo_log')) {
                    puffergo_log('Nav menu translate failed for ' . $location . '/' . $lang . ': ' . $e->getMessage());
                } $locations[ $location ] = 'failed';
            }
        } $record = array('status' => self::aggregate_status($locations), 'translatedAt' => current_time('mysql', true), 'locations' => $locations, );
        self::save_status($lang, $record);

        return $record;
    }

    private static function translate_one_menu(int $source_menu_id, string $lang, string $base_menu_name): int
    {
        $source_items = wp_get_nav_menu_items($source_menu_id);

        if (! is_array($source_items)) {
            $source_items = array();
        } $items = array();

        foreach ($source_items as $item) {
            if (self::linked_page_id($item) > 0) {
                continue;
            } $items[] = array('id' => 'i' . $item->ID, 'text' => (string) $item->title, 'contentType' => 'title', );
        } $translations = array();

        if (! empty($items)) {
            $default = PufferGo_Polylang::default_language();
            $result = PufferGo_Translation_Client::translate($items, PufferGo_Polylang::locale_for($lang), PufferGo_Polylang::locale_for($default));
            $translations = $result['translations'];
        } $target_menu_id = self::ensure_target_menu($base_menu_name, $lang, $source_menu_id);
        $existing_by_source = array();
        $existing_items = wp_get_nav_menu_items($target_menu_id);

        if (is_array($existing_items)) {
            foreach ($existing_items as $eitem) {
                if (! get_post_meta($eitem->ID, '_puffergo_managed', true)) {
                    continue;
                } $src = (int) get_post_meta($eitem->ID, '_puffergo_menu_source', true);

                if ($src > 0) {
                    $existing_by_source[ $src ] = (int) $eitem->ID;
                } else {
                    wp_delete_post((int) $eitem->ID, true);
                }
            }
        } $id_map = array();
        $seen = array();
        $pending = $source_items;
        $guard = count($pending) + 1;

        while (! empty($pending) && $guard-- > 0) {
            $next = array();

            foreach ($pending as $item) {
                $old_parent = (int) $item->menu_item_parent;

                if ($old_parent > 0 && ! isset($id_map[ $old_parent ])) {
                    $next[] = $item;
                    continue;
                } $parent_new = $old_parent > 0 ? $id_map[ $old_parent ] : 0;
                $existing_id = $existing_by_source[ (int) $item->ID ] ?? 0;
                $new_id = self::upsert_target_item($target_menu_id, $item, $lang, $translations, $parent_new, $existing_id);

                if ($new_id > 0) {
                    $id_map[ (int) $item->ID ] = $new_id;
                    $seen[ (int) $item->ID ] = true;
                }
            }

if (count($next) === count($pending)) {
                foreach ($next as $item) {
                    $existing_id = $existing_by_source[ (int) $item->ID ] ?? 0;
                    $new_id = self::upsert_target_item($target_menu_id, $item, $lang, $translations, 0, $existing_id);

                    if ($new_id > 0) {
                        $id_map[ (int) $item->ID ] = $new_id;
                        $seen[ (int) $item->ID ] = true;
                    }
                } break;
            } $pending = $next;
        }

foreach ($existing_by_source as $src_id => $target_item_id) {
            if (empty($seen[ $src_id ])) {
                wp_delete_post($target_item_id, true);
            }
        } update_term_meta($target_menu_id, self::META_SRCHASH, self::source_signature($source_items, $lang));
        self::flush_menu_caches(array($source_menu_id, $target_menu_id));

        return $target_menu_id;
    }

    private static function upsert_target_item(int $menu_id, $item, string $lang, array $translations, int $parent_new_id, int $existing_item_id): int
    {
        $type = (string) $item->type;
        $page_id = self::linked_page_id($item);
        $translated = $page_id > 0 ? self::published_translation($page_id, $lang) : 0;

        if ($page_id > 0) {
            $title = $translated > 0 ? self::page_title_or($translated, (string) $item->title) : (string) $item->title;
        } else {
            $key = 'i' . $item->ID;
            $title = (isset($translations[ $key ]) && '' !== $translations[ $key ]) ? (string) $translations[ $key ] : (string) $item->title;
        } $args = array('menu-item-type' => $type, 'menu-item-status' => 'publish', 'menu-item-position' => (int) $item->menu_order, 'menu-item-parent-id' => $parent_new_id, );

        if ('custom' === $type) {
            $args['menu-item-url'] = $translated > 0 ? (string) get_permalink($translated) : (string) $item->url;
        } else {
            $args['menu-item-object'] = (string) $item->object;
            $args['menu-item-object-id'] = $translated > 0 ? $translated : (int) $item->object_id;
        } $args['menu-item-title'] = $title;
        $new_id = wp_update_nav_menu_item($menu_id, $existing_item_id, $args);

        if (is_wp_error($new_id)) {
            return 0;
        } $new_id = (int) $new_id;
        update_post_meta($new_id, '_puffergo_managed', true);
        update_post_meta($new_id, '_puffergo_menu_lang', $lang);
        update_post_meta($new_id, '_puffergo_menu_source', (int) $item->ID);

        return $new_id;
    }

    private static function linked_page_id($item): int
    {
        $type = (string) $item->type;

        if ('post_type' === $type) {
            return (int) $item->object_id;
        }

if ('custom' === $type) {
            $url = (string) $item->url;
            $front_id = (int) get_option('page_on_front', 0);

            if ($front_id > 0 && trailingslashit($url) === trailingslashit(home_url('/'))) {
                return $front_id;
            }

return (int) url_to_postid($url);
        }

return 0;
    }

    private static function published_translation(int $page_id, string $lang): int
    {
        if ($page_id <= 0) {
            return 0;
        } $translated = PufferGo_Polylang::get_translation($page_id, $lang);

        return ($translated > 0 && 'publish' === get_post_status($translated)) ? $translated : 0;
    }

    private static function page_title_or(int $post_id, string $fallback): string
    {
        $title = (string) get_post_field('post_title', $post_id);

        return '' !== $title ? $title : $fallback;
    }

    private static function ensure_target_menu(string $base_name, string $lang, int $source_menu_id): int
    {
        $label = self::managed_menu_label($base_name, $lang);
        $menu_id = self::find_managed_menu($lang, $source_menu_id);

        if ($menu_id <= 0) {
            $existing = wp_get_nav_menu_object($label);

            if (! $existing || ! isset($existing->term_id)) {
                $existing = wp_get_nav_menu_object($base_name . ' ' . strtoupper($lang));
            }

if ($existing && isset($existing->term_id)) {
                $menu_id = (int) $existing->term_id;
            } else {
                $created = wp_create_nav_menu($label);

                if (is_wp_error($created)) {
                    throw new Exception($created->get_error_message());
                } $menu_id = (int) $created;
            }
        } $current = wp_get_nav_menu_object($menu_id);

        if ($current && isset($current->name) && $current->name !== $label) {
            wp_update_nav_menu_object($menu_id, array('menu-name' => $label));
        } update_term_meta($menu_id, '_puffergo_menu_lang', $lang);
        update_term_meta($menu_id, '_puffergo_menu_source', $source_menu_id);
        update_term_meta($menu_id, '_puffergo_managed', 1);

        return $menu_id;
    }

    private static function find_managed_menu(string $lang, int $source_menu_id): int
    {
        $terms = get_terms(array('taxonomy' => 'nav_menu', 'hide_empty' => false, 'number' => 1, 'fields' => 'ids', 'meta_query' => array('relation' => 'AND', array('key' => '_puffergo_menu_lang', 'value' => $lang, ), array('key' => '_puffergo_menu_source', 'value' => $source_menu_id, ), ), ));

        if (is_array($terms) && ! empty($terms)) {
            return (int) $terms[0];
        }

return 0;
    }

    private static function managed_menu_label(string $base_name, string $lang): string
    {
        return sprintf(__('%1$s (%2$s · auto-managed)', 'puffergo'), $base_name, strtoupper($lang));
    }

    private static function flush_menu_caches(array $menu_ids): void
    {
        foreach (array_unique(array_filter(array_map('intval', $menu_ids))) as $menu_id) {
            clean_term_cache($menu_id, 'nav_menu');
        }

if (function_exists('wp_cache_set_posts_last_changed')) {
            wp_cache_set_posts_last_changed();
        }
    }

    private static function ensure_source_menu(string $location): int
    {
        $menu_name = self::LOCATIONS[ $location ] ?? '';

        if ('' !== $menu_name) {
            $source = wp_get_nav_menu_object($menu_name);

            if ($source && isset($source->term_id)) {
                return (int) $source->term_id;
            }
        }

if ('puffergo_footer' === $location) {
            return (int) puffergo_ensure_footer_menu();
        }

return (int) puffergo_ensure_primary_menu();
    }

    private static function aggregate_status(array $locations): string
    {
        if (empty($locations)) {
            return self::STATUS_FAILED;
        } $has_translated = false;
        $has_failed = false;
        $has_skipped = false;
        $has_stale = false;

        foreach ($locations as $status) {
            switch ($status) {
                case self::STATUS_FAILED: $has_failed = true;
                    break;
                case self::STATUS_SKIPPED: $has_skipped = true;
                    break;
                case self::STATUS_STALE: $has_stale = true;
                    break;
                default: $has_translated = true;
                    break;
            }
        }

if ($has_failed && ($has_translated || $has_stale)) {
            return self::STATUS_PARTIAL;
        }

if ($has_failed) {
            return self::STATUS_FAILED;
        }

if ($has_stale) {
            return self::STATUS_STALE;
        }

if ($has_translated) {
            return self::STATUS_TRANSLATED;
        }

return $has_skipped ? self::STATUS_SKIPPED : self::STATUS_FAILED;
    }

    private static function save_status(string $lang, array $record): void
    {
        $all = get_option(self::STATUS_OPTION, array());

        if (! is_array($all)) {
            $all = array();
        } $all[ $lang ] = $record;
        update_option(self::STATUS_OPTION, $all, false);
    }
}
