<?php

namespace WPSN\SEO;

use WPSN\Core\Settings;
use WPSN\Domain\SeoDocumentContext;

defined('ABSPATH') || exit;

/**
 * Shared breadcrumbs builder.
 *
 * Used by:
 *  - SchemaGraphBuilder (BreadcrumbList)
 *  - Breadcrumbs module (frontend UI)
 */
final class BreadcrumbsBuilder
{
    private ?Settings $settings;

    public function __construct(?Settings $settings = null)
    {
        $this->settings = $settings;
    }

    /**
     * @return array<int, array{url: string, name: string}>
     */
    public function build(SeoDocumentContext $ctx, string $homeUrl = ''): array
    {
        if ($homeUrl === '') {
            $homeUrl = (string) home_url('/');
        }

        $items = [];

        $items[] = [
            'url'  => $homeUrl,
            'name' => $this->homeLabel(),
        ];

        if ($ctx->isSingular()) {
            $postId = (int) $ctx->objectId;
            $post = get_post($postId);
            if ($post instanceof \WP_Post) {
                $pt = $ctx->objectSubType ?: $post->post_type;

                // Pages: include parent pages.
                if ($pt === 'page') {
                    $ancestors = get_post_ancestors($post);
                    if (is_array($ancestors) && !empty($ancestors)) {
                        $ancestors = array_reverse(array_map('intval', $ancestors));
                        foreach ($ancestors as $ancestorId) {
                            $url = get_permalink($ancestorId);
                            $name = get_the_title($ancestorId);
                            if (is_string($url) && $url !== '' && is_string($name) && $name !== '') {
                                $items[] = ['url' => $url, 'name' => $name];
                            }
                        }
                    }
                } elseif ($pt === 'post') {
                    // Posts: include first category (and its ancestors).
                    $cats = get_the_category($postId);
                    if (is_array($cats) && !empty($cats)) {
                        $cat = $cats[0];
                        if ($cat instanceof \WP_Term) {
                            $ancestors = get_ancestors($cat->term_id, 'category');
                            if (is_array($ancestors) && !empty($ancestors)) {
                                $ancestors = array_reverse(array_map('intval', $ancestors));
                                foreach ($ancestors as $ancestorId) {
                                    $aTerm = get_term($ancestorId, 'category');
                                    if ($aTerm instanceof \WP_Term) {
                                        $url = get_term_link($aTerm);
                                        if (is_string($url) && $url !== '') {
                                            $items[] = ['url' => $url, 'name' => (string) $aTerm->name];
                                        }
                                    }
                                }
                            }

                            $url = get_category_link($cat);
                            if (is_string($url) && $url !== '') {
                                $items[] = ['url' => $url, 'name' => (string) $cat->name];
                            }
                        }
                    }
                } elseif ($pt === 'product') {
                    // WooCommerce products: Shop -> (deepest) Product category chain.
                    $obj = get_post_type_object($pt);
                    if ($obj && !empty($obj->has_archive)) {
                        $url = get_post_type_archive_link($pt);
                        if (is_string($url) && $url !== '') {
                            $label = isset($obj->labels, $obj->labels->name) ? (string) $obj->labels->name : $pt;
                            $items[] = ['url' => $url, 'name' => $label];
                        }
                    }

                    if (taxonomy_exists('product_cat')) {
                        $terms = get_the_terms($postId, 'product_cat');
                        if (is_array($terms) && !empty($terms)) {
                            $best = null;
                            $bestDepth = -1;
                            foreach ($terms as $t) {
                                if (!$t instanceof \WP_Term) {
                                    continue;
                                }
                                $anc = get_ancestors($t->term_id, 'product_cat');
                                $depth = is_array($anc) ? count($anc) : 0;
                                if ($depth > $bestDepth) {
                                    $best = $t;
                                    $bestDepth = $depth;
                                }
                            }

                            if ($best instanceof \WP_Term) {
                                $ancestors = get_ancestors($best->term_id, 'product_cat');
                                if (is_array($ancestors) && !empty($ancestors)) {
                                    $ancestors = array_reverse(array_map('intval', $ancestors));
                                    foreach ($ancestors as $ancestorId) {
                                        $aTerm = get_term($ancestorId, 'product_cat');
                                        if ($aTerm instanceof \WP_Term) {
                                            $url = get_term_link($aTerm);
                                            if (is_string($url) && $url !== '') {
                                                $items[] = ['url' => $url, 'name' => (string) $aTerm->name];
                                            }
                                        }
                                    }
                                }

                                $url = get_term_link($best);
                                if (is_string($url) && $url !== '') {
                                    $items[] = ['url' => $url, 'name' => (string) $best->name];
                                }
                            }
                        }
                    }
                } else {
                    // CPT: include post type archive if exists.
                    $obj = get_post_type_object($pt);
                    if ($obj && !empty($obj->has_archive)) {
                        $url = get_post_type_archive_link($pt);
                        if (is_string($url) && $url !== '') {
                            $label = isset($obj->labels, $obj->labels->name) ? (string) $obj->labels->name : $pt;
                            $items[] = ['url' => $url, 'name' => $label];
                        }
                    }
                }

                $title = get_the_title($postId);
                $url = get_permalink($postId);
                if (is_string($url) && $url !== '' && is_string($title) && $title !== '') {
                    $items[] = ['url' => $url, 'name' => $title];
                }
            }
        } elseif ($ctx->isTerm()) {
            $tax = $ctx->objectSubType ?: '';
            $term = get_term((int) $ctx->objectId, $tax);
            if ($term instanceof \WP_Term) {
                // Add ancestor chain for hierarchical taxonomies.
                if ($tax !== '' && function_exists('is_taxonomy_hierarchical') && is_taxonomy_hierarchical($tax)) {
                    $ancestors = get_ancestors($term->term_id, $tax);
                    if (is_array($ancestors) && !empty($ancestors)) {
                        $ancestors = array_reverse(array_map('intval', $ancestors));
                        foreach ($ancestors as $ancestorId) {
                            $aTerm = get_term($ancestorId, $tax);
                            if ($aTerm instanceof \WP_Term) {
                                $url = get_term_link($aTerm);
                                if (is_string($url) && $url !== '') {
                                    $items[] = ['url' => $url, 'name' => (string) $aTerm->name];
                                }
                            }
                        }
                    }
                }

                $url = get_term_link($term);
                if (is_string($url) && $url !== '') {
                    $items[] = ['url' => $url, 'name' => (string) $term->name];
                }
            }
        } elseif ($ctx->type === SeoDocumentContext::TYPE_ARCHIVE) {
            $title = get_the_archive_title();
            $title = is_string($title) ? trim(wp_strip_all_tags($title)) : '';
            $url = $ctx->url;
            $label = $this->archiveLabel($title);
            if ($label !== '' && $url !== '') {
                $items[] = ['url' => $url, 'name' => $label];
            }
        } elseif ($ctx->type === SeoDocumentContext::TYPE_SEARCH) {
            $q = get_search_query();
            $q = is_string($q) ? $q : '';
            $label = $this->searchLabel($q);
            if ($label !== '' && $ctx->url !== '') {
                $items[] = ['url' => $ctx->url, 'name' => $label];
            }
        } elseif ($ctx->type === SeoDocumentContext::TYPE_404) {
            $label = $this->notFoundLabel();
            if ($ctx->url !== '' && $label !== '') {
                $items[] = ['url' => $ctx->url, 'name' => $label];
            }
        }

        /**
         * Allow 3rd-parties / site owners to customize breadcrumbs items.
         *
         * @param array<int, array{url: string, name: string}> $items
         */
        $items = apply_filters('wpsn/breadcrumbs/items', $items, $ctx, $homeUrl);

        return is_array($items) ? $items : [];
    }

    private function homeLabel(): string
    {
        if ($this->settings) {
            $custom = (string) $this->settings->get('breadcrumbs.home_label', '');
            $custom = trim($custom);
            if ($custom !== '') {
                return $custom;
            }
        }

        return __('Home', 'wp-seo-next');
    }

    private function archiveLabel(string $title): string
    {
        $title = trim(wp_strip_all_tags($title));
        if ($title === '') {
            return '';
        }

        $prefix = $this->settingText('breadcrumbs.archive_prefix');
        if ($prefix === '') {
            return $title;
        }

        if (stripos($title, $prefix) === 0) {
            return $title;
        }

        $stripped = $this->stripArchiveTitlePrefix($title);
        if (strpos($prefix, '%s') !== false) {
            return trim(sprintf($prefix, $stripped));
        }

        return trim($prefix . ' ' . $stripped);
    }

    private function searchLabel(string $query): string
    {
        $query = trim(wp_strip_all_tags($query));
        if ($query === '') {
            return '';
        }

        $prefix = $this->settingText('breadcrumbs.search_prefix');
        if ($prefix !== '') {
            if (strpos($prefix, '%s') !== false) {
                return trim(sprintf($prefix, $query));
            }

            return trim($prefix . ' "' . $query . '"');
        }

        return sprintf(__('Search results for "%s"', 'wp-seo-next'), $query);
    }

    private function notFoundLabel(): string
    {
        $custom = $this->settingText('breadcrumbs.not_found_label');
        if ($custom !== '') {
            return $custom;
        }

        return __('Not found', 'wp-seo-next');
    }

    private function stripArchiveTitlePrefix(string $title): string
    {
        $title = trim(wp_strip_all_tags($title));
        if ($title === '') {
            return '';
        }

        if (preg_match('/^[^:]{1,40}:\s+(.+)$/u', $title, $m)) {
            $stripped = isset($m[1]) ? trim((string) $m[1]) : '';
            if ($stripped !== '') {
                return $stripped;
            }
        }

        return $title;
    }

    private function settingText(string $path): string
    {
        if (!$this->settings) {
            return '';
        }

        $value = (string) $this->settings->get($path, '');
        return trim($value);
    }

}