<?php
declare(strict_types=1);

namespace Help4BuilderSuite\Modules\SEO;

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

/**
 * Normalizes international discovery and ownership metadata without inventing
 * platform-specific tags where the open standards already provide coverage.
 */
final class DiscoveryStandards
{
    /**
     * @param array<string, mixed> $settings
     * @return array<int, array{name: string, content: string}>
     */
    public function verificationMeta(array $settings): array
    {
        $presets = [
            'google_site_verification'   => 'google-site-verification',
            'bing_site_verification'     => 'msvalidate.01',
            'yandex_site_verification'   => 'yandex-verification',
            'baidu_site_verification'    => 'baidu-site-verification',
            'naver_site_verification'    => 'naver-site-verification',
            'pinterest_site_verification'=> 'p:domain_verify',
            'facebook_domain_verification' => 'facebook-domain-verification',
        ];
        $tags = [];
        foreach ($presets as $settingKey => $metaName) {
            $content = $this->sanitizeVerificationContent((string) ($settings[$settingKey] ?? ''));
            if ($content !== '') {
                $tags[] = ['name' => $metaName, 'content' => $content];
            }
        }

        $tags = array_merge($tags, $this->sanitizeCustomVerificationMeta($settings['custom_site_verification_meta'] ?? []));
        $filtered = apply_filters('h4bs_verification_meta_tags', $tags, $settings);
        return $this->sanitizeVerificationTagArray(is_array($filtered) ? $filtered : $tags);
    }

    /**
     * Accepts one `meta-name=value` ownership tag per line or normalized rows.
     *
     * @param mixed $raw
     * @return array<int, array{name: string, content: string}>
     */
    public function sanitizeCustomVerificationMeta($raw): array
    {
        if (is_string($raw)) {
            $rows = [];
            foreach (preg_split('/\r\n|\r|\n/', $raw) ?: [] as $line) {
                $parts = preg_split('/\s*[=|]\s*/', trim((string) $line), 2) ?: [];
                if (count($parts) === 2) {
                    $rows[] = ['name' => $parts[0], 'content' => $parts[1]];
                }
            }
            $raw = $rows;
        }

        return $this->sanitizeVerificationTagArray(is_array($raw) ? $raw : []);
    }

    /**
     * @param array<int, array{name?: mixed, content?: mixed}> $tags
     * @return array<int, array{name: string, content: string}>
     */
    private function sanitizeVerificationTagArray(array $tags): array
    {
        $clean = [];
        foreach ($tags as $tag) {
            if (! is_array($tag)) {
                continue;
            }
            $name = strtolower(trim((string) ($tag['name'] ?? '')));
            $content = $this->sanitizeVerificationContent((string) ($tag['content'] ?? ''));
            if (
                $content === ''
                || preg_match('/^[a-z0-9][a-z0-9._:-]{0,79}$/', $name) !== 1
                || (
                    preg_match('/(verif(?:y|ication)|domain[_-]?verify)/i', $name) !== 1
                    && $name !== 'msvalidate.01'
                )
            ) {
                continue;
            }
            $clean[$name] = ['name' => $name, 'content' => $content];
        }

        return array_slice(array_values($clean), 0, 30);
    }

    private function sanitizeVerificationContent(string $value): string
    {
        $value = sanitize_text_field($value);
        $value = preg_replace('/[\x00-\x1F\x7F]/', '', $value) ?? '';
        return substr(trim($value), 0, 255);
    }

    /**
     * @param array<int, array{name?: mixed, content?: mixed}> $tags
     */
    public function formatVerificationMeta(array $tags): string
    {
        $lines = [];
        foreach ($this->sanitizeVerificationTagArray($tags) as $tag) {
            $lines[] = $tag['name'] . '=' . $tag['content'];
        }
        return implode("\n", $lines);
    }

    /**
     * @param mixed $raw
     * @return array<string, string>
     */
    public function sanitizeHreflangAlternates($raw): array
    {
        if (is_string($raw)) {
            $rows = [];
            foreach (preg_split('/\r\n|\r|\n/', $raw) ?: [] as $line) {
                $parts = preg_split('/\s*[=|]\s*/', trim((string) $line), 2) ?: [];
                if (count($parts) === 2) {
                    $rows[(string) $parts[0]] = (string) $parts[1];
                }
            }
            $raw = $rows;
        }

        $clean = [];
        foreach (is_array($raw) ? $raw : [] as $language => $url) {
            if (is_array($url)) {
                $language = (string) ($url['language'] ?? $url['hreflang'] ?? '');
                $url = (string) ($url['url'] ?? $url['href'] ?? '');
            }
            $language = $this->normalizeLanguageTag((string) $language);
            $url = esc_url_raw(trim((string) $url), ['http', 'https']);
            if ($language !== '' && $url !== '') {
                $clean[$language] = $url;
            }
        }

        return array_slice($clean, 0, 50, true);
    }

    /**
     * @param array<string, string> $alternates
     */
    public function formatHreflangAlternates(array $alternates): string
    {
        $lines = [];
        foreach ($this->sanitizeHreflangAlternates($alternates) as $language => $url) {
            $lines[] = $language . '=' . $url;
        }
        return implode("\n", $lines);
    }

    public function normalizeLanguageTag(string $language): string
    {
        $language = trim(str_replace('_', '-', $language));
        if (strcasecmp($language, 'x-default') === 0) {
            return 'x-default';
        }
        $parts = explode('-', $language);
        if (! isset($parts[0]) || preg_match('/^[A-Za-z]{2,3}$/', $parts[0]) !== 1) {
            return '';
        }
        $normalized = [strtolower($parts[0])];
        $index = 1;
        if (isset($parts[$index]) && preg_match('/^[A-Za-z]{4}$/', $parts[$index]) === 1) {
            $normalized[] = ucfirst(strtolower($parts[$index]));
            $index++;
        }
        if (isset($parts[$index])) {
            $region = (string) $parts[$index];
            if (preg_match('/^[A-Za-z]{2}$/', $region) === 1) {
                $normalized[] = strtoupper($region);
            } elseif (preg_match('/^[0-9]{3}$/', $region) === 1) {
                $normalized[] = $region;
            } else {
                return '';
            }
            $index++;
        }
        if (count($parts) !== $index) {
            return '';
        }

        return implode('-', $normalized);
    }

    public function currentLanguageTag(): string
    {
        $tag = $this->normalizeLanguageTag((string) get_locale());
        return $tag !== '' ? $tag : 'en-US';
    }

    public function openGraphLocale(string $language = ''): string
    {
        $language = $this->normalizeLanguageTag($language !== '' ? $language : $this->currentLanguageTag());
        if ($language === '' || $language === 'x-default') {
            return 'en_US';
        }
        return str_replace('-', '_', $language);
    }

    /**
     * @param array<string, mixed> $settings
     * @param array<string, mixed> $health
     * @return array{summary: array<string, int>, search: array<int, array<string, mixed>>, sharing: array<int, array<string, mixed>>, standards: array<int, array<string, mixed>>}
     */
    public function coverageReport(array $settings, array $health): array
    {
        $missing = is_array($health['missing'] ?? null) ? $health['missing'] : [];
        $scanned = absint($health['scanned'] ?? 0);
        $sitemaps = is_array($health['sitemaps'] ?? null) ? $health['sitemaps'] : [];
        $sitemapReady = $sitemaps === [];
        foreach ($sitemaps as $sitemap) {
            if (is_array($sitemap) && (int) ($sitemap['status'] ?? 0) === 200) {
                $sitemapReady = true;
                break;
            }
        }
        $metadataReady = $scanned === 0 || (
            absint($missing['title'] ?? 0) === 0
            && absint($missing['description'] ?? 0) === 0
            && absint($missing['canonical'] ?? 0) === 0
        );
        $socialReady = $metadataReady && ($scanned === 0 || absint($missing['social_image'] ?? 0) === 0);
        $indexable = (string) get_option('blog_public', '1') === '1';
        $indexNow = rest_sanitize_boolean($settings['indexnow_enabled'] ?? false)
            && strlen((string) ($settings['indexnow_key'] ?? '')) >= 8;
        $verificationNames = array_column($this->verificationMeta($settings), 'name');
        $has = static fn (string $name): bool => in_array($name, $verificationNames, true);

        $standards = [
            $this->coverageRow('robots', __('Robots directives', 'help4-builder-suite'), $indexable, __('All standards-compliant crawlers, including DuckDuckBot', 'help4-builder-suite'), __('WordPress robots rules plus per-content index, follow, archive, snippet, and image-preview controls.', 'help4-builder-suite')),
            $this->coverageRow('sitemaps', __('XML sitemaps', 'help4-builder-suite'), $sitemapReady, __('Global search discovery', 'help4-builder-suite'), __('Core XML sitemap index, post-type pages, status validation, and robots.txt discovery.', 'help4-builder-suite')),
            $this->coverageRow('canonical', __('Canonical URLs', 'help4-builder-suite'), $metadataReady, __('Duplicate-content consolidation across major engines', 'help4-builder-suite'), __('Absolute canonical output with per-content overrides.', 'help4-builder-suite')),
            $this->coverageRow('schema', __('Schema.org JSON-LD', 'help4-builder-suite'), true, __('Google, Bing, Yandex, Yahoo, assistants, and other structured-data consumers', 'help4-builder-suite'), __('Organization, WebSite, breadcrumb, author, article, service, product, local business, real-estate, FAQ, and extensible graph nodes.', 'help4-builder-suite')),
            $this->coverageRow('hreflang', __('hreflang and x-default', 'help4-builder-suite'), true, __('International language and regional targeting, including en-AU and zh-CN', 'help4-builder-suite'), __('Validated per-content absolute alternates, current-language self reference, x-default support, and matching Open Graph locale alternates.', 'help4-builder-suite')),
            $this->coverageRow('indexnow', __('IndexNow change delivery', 'help4-builder-suite'), $indexNow, __('Bing, Naver, Seznam.cz, Yandex, Yep, Amazon, and participating engines', 'help4-builder-suite'), __('Opt-in add, update, and delete queue with batching, retries, response history, and staging protection.', 'help4-builder-suite'), true),
        ];

        $search = [
            $this->coverageRow('google', __('Google Search', 'help4-builder-suite'), $metadataReady && $sitemapReady, __('Worldwide', 'help4-builder-suite'), __('Robots, sitemap, canonical, hreflang, Schema.org, and optional Search Console verification.', 'help4-builder-suite'), false, $has('google-site-verification')),
            $this->coverageRow('bing', __('Microsoft Bing', 'help4-builder-suite'), $metadataReady && $sitemapReady, __('Worldwide', 'help4-builder-suite'), __('Core discovery standards, optional Webmaster Tools verification, and IndexNow.', 'help4-builder-suite'), false, $has('msvalidate.01')),
            $this->coverageRow('duckduckgo', __('DuckDuckGo', 'help4-builder-suite'), $metadataReady && $sitemapReady, __('Worldwide', 'help4-builder-suite'), __('DuckDuckBot-compatible robots and metadata plus Bing ecosystem coverage for many traditional links and images. No fake DuckDuckGo verification tag.', 'help4-builder-suite')),
            $this->coverageRow('yahoo_ecosia', __('Yahoo and Ecosia discovery', 'help4-builder-suite'), $metadataReady && $sitemapReady, __('Worldwide and Europe', 'help4-builder-suite'), __('Standards-based crawlability and Bing ecosystem discovery where those services source Bing results.', 'help4-builder-suite')),
            $this->coverageRow('yandex', __('Yandex', 'help4-builder-suite'), $metadataReady && $sitemapReady, __('Russia, CIS, and international', 'help4-builder-suite'), __('Robots, sitemap, canonical, hreflang, Schema.org, optional Webmaster verification, and IndexNow.', 'help4-builder-suite'), false, $has('yandex-verification')),
            $this->coverageRow('baidu', __('Baidu', 'help4-builder-suite'), $metadataReady && $sitemapReady, __('China', 'help4-builder-suite'), __('Crawlable HTML, canonical URLs, sitemaps, Open Graph article context, and optional Search Resource Platform ownership metadata.', 'help4-builder-suite'), false, $has('baidu-site-verification')),
            $this->coverageRow('naver', __('Naver', 'help4-builder-suite'), $metadataReady && $sitemapReady, __('South Korea', 'help4-builder-suite'), __('Core discovery standards, Search Advisor ownership metadata, and IndexNow.', 'help4-builder-suite'), false, $has('naver-site-verification')),
            $this->coverageRow('seznam', __('Seznam.cz', 'help4-builder-suite'), $metadataReady && $sitemapReady, __('Czech Republic and Central Europe', 'help4-builder-suite'), __('Core discovery standards and IndexNow; custom ownership tags can be registered when issued.', 'help4-builder-suite')),
            $this->coverageRow('brave_mojeek', __('Brave Search and Mojeek', 'help4-builder-suite'), $metadataReady && $sitemapReady, __('Worldwide and Europe', 'help4-builder-suite'), __('Independent-crawler-friendly robots, XML sitemaps, canonical URLs, semantic HTML, and structured data.', 'help4-builder-suite')),
            $this->coverageRow('qwant', __('Qwant', 'help4-builder-suite'), $metadataReady && $sitemapReady, __('France and Europe', 'help4-builder-suite'), __('Portable crawl, canonical, sitemap, language, and structured-data standards without vendor-only markup.', 'help4-builder-suite')),
            $this->coverageRow('china_regional', __('Sogou and 360 Search', 'help4-builder-suite'), $metadataReady && $sitemapReady, __('China', 'help4-builder-suite'), __('Crawlable server-rendered metadata, canonical URLs, XML sitemaps, Chinese language targeting, and safe custom ownership tags when issued.', 'help4-builder-suite')),
            $this->coverageRow('japan_regional', __('Yahoo Japan and regional Japanese discovery', 'help4-builder-suite'), $metadataReady && $sitemapReady, __('Japan', 'help4-builder-suite'), __('Standards-based crawlability, canonical URLs, XML sitemaps, ja/ja-JP targeting, and Schema.org data.', 'help4-builder-suite')),
            $this->coverageRow('yep_kagi', __('Yep, Kagi, and emerging search clients', 'help4-builder-suite'), $metadataReady && $sitemapReady, __('Worldwide', 'help4-builder-suite'), __('Portable metadata and structured content; Yep also receives participating IndexNow notifications.', 'help4-builder-suite')),
            $this->coverageRow('regional', __('Other regional engines', 'help4-builder-suite'), $metadataReady && $sitemapReady, __('Australia, Asia-Pacific, Europe, Africa, and the Americas', 'help4-builder-suite'), __('Standards-first robots, sitemap, canonical, localized URLs, structured data, and a validated custom verification registry for regional webmaster portals.', 'help4-builder-suite')),
        ];

        $sharing = [
            $this->coverageRow('open_graph', __('Universal Open Graph preview', 'help4-builder-suite'), $socialReady, __('Standards-compatible social, messaging, and collaboration clients', 'help4-builder-suite'), __('Title, description, type, URL, site name, locale, locale alternates, image, secure URL, dimensions, MIME type, and accessible image alt.', 'help4-builder-suite')),
            $this->coverageRow('x_cards', __('X Cards', 'help4-builder-suite'), $socialReady, __('X', 'help4-builder-suite'), __('Card type, title, description, site, creator, image, and image alt with Open Graph fallbacks.', 'help4-builder-suite')),
            $this->coverageRow('meta_social', __('Facebook, Instagram, and Threads sharing', 'help4-builder-suite'), $socialReady, __('Meta social surfaces', 'help4-builder-suite'), __('Full Open Graph object, locale, image, and article metadata plus optional Meta domain verification.', 'help4-builder-suite'), false, $has('facebook-domain-verification')),
            $this->coverageRow('meta_messaging', __('Messenger and WhatsApp previews', 'help4-builder-suite'), $socialReady, __('Meta messaging surfaces', 'help4-builder-suite'), __('Standards-based Open Graph preview metadata. Apps may cache, crop, or transform the final card.', 'help4-builder-suite')),
            $this->coverageRow('linkedin', __('LinkedIn', 'help4-builder-suite'), $socialReady, __('Professional sharing worldwide', 'help4-builder-suite'), __('Portable Open Graph title, description, canonical URL, object type, and image metadata.', 'help4-builder-suite')),
            $this->coverageRow('pinterest', __('Pinterest', 'help4-builder-suite'), $socialReady, __('Worldwide', 'help4-builder-suite'), __('Open Graph sharing metadata plus optional domain ownership verification.', 'help4-builder-suite'), false, $has('p:domain_verify')),
            $this->coverageRow('reddit', __('Reddit and Tumblr', 'help4-builder-suite'), $socialReady, __('Community and publishing previews', 'help4-builder-suite'), __('Open Graph title, description, canonical URL, image, and object context.', 'help4-builder-suite')),
            $this->coverageRow('fediverse', __('Mastodon, Bluesky, and federated sharing', 'help4-builder-suite'), $socialReady, __('Open and decentralized social clients', 'help4-builder-suite'), __('Portable Open Graph cards backed by canonical and Schema.org identity metadata.', 'help4-builder-suite')),
            $this->coverageRow('telegram', __('Telegram', 'help4-builder-suite'), $socialReady, __('Messaging worldwide', 'help4-builder-suite'), __('Open Graph title, description, URL, image, dimensions, locale, and accessible image text.', 'help4-builder-suite')),
            $this->coverageRow('regional_messaging', __('LINE, WeChat, and KakaoTalk', 'help4-builder-suite'), $socialReady, __('Japan, China, South Korea, and Asia-Pacific', 'help4-builder-suite'), __('Portable server-rendered Open Graph metadata and language-aware canonical URLs. Client preview behavior can vary.', 'help4-builder-suite')),
            $this->coverageRow('apple_signal', __('Apple Messages and Signal', 'help4-builder-suite'), $socialReady, __('Private messaging clients', 'help4-builder-suite'), __('Portable title, description, canonical URL, and image metadata with no tracking script required.', 'help4-builder-suite')),
            $this->coverageRow('vk', __('VK', 'help4-builder-suite'), $socialReady, __('Russia and CIS social sharing', 'help4-builder-suite'), __('Open Graph object, localized title and description, canonical URL, and image metadata.', 'help4-builder-suite')),
            $this->coverageRow('collaboration', __('Slack, Discord, and Microsoft Teams', 'help4-builder-suite'), $socialReady, __('Work and community link previews', 'help4-builder-suite'), __('Portable Open Graph metadata with X and Schema.org fallbacks. Individual clients control caching and rendering.', 'help4-builder-suite')),
            $this->coverageRow('generic_preview', __('Future and custom preview consumers', 'help4-builder-suite'), $socialReady, __('Any standards-compatible client', 'help4-builder-suite'), __('Filterable metadata and reporting contracts allow extensions without injecting unsafe arbitrary HTML.', 'help4-builder-suite')),
        ];

        $report = [
            'summary' => [
                'standards_ready' => count(array_filter($standards, static fn (array $row): bool => ! empty($row['ready']))),
                'standards_total' => count($standards),
                'search_ready' => count(array_filter($search, static fn (array $row): bool => ! empty($row['ready']))),
                'search_total' => count($search),
                'sharing_ready' => count(array_filter($sharing, static fn (array $row): bool => ! empty($row['ready']))),
                'sharing_total' => count($sharing),
            ],
            'search' => $search,
            'sharing' => $sharing,
            'standards' => $standards,
        ];
        $filtered = apply_filters('h4bs_international_discovery_report', $report, $settings, $health);
        return is_array($filtered) ? $filtered : $report;
    }

    /**
     * @return array<string, mixed>
     */
    private function coverageRow(
        string $key,
        string $label,
        bool $ready,
        string $reach,
        string $coverage,
        bool $requiredSetup = false,
        ?bool $verified = null
    ): array {
        return [
            'key' => $key,
            'label' => $label,
            'ready' => $ready,
            'reach' => $reach,
            'coverage' => $coverage,
            'required_setup' => $requiredSetup,
            'verification_configured' => $verified,
        ];
    }
}
