<?php

namespace WPSN\Modules\ImportExport;

defined('ABSPATH') || exit;

/**
 * Groups Import Assist warnings into readable source/plugin buckets.
 */
final class ImportAssistWarningGrouper
{
    /**
     * @param array<string,mixed> $entry
     * @return array<int,array{key:string,label:string,count:int,items:array<int,string>}>
     */
    public function group(array $entry): array
    {
        $items = isset($entry['items']) && is_array($entry['items']) ? $entry['items'] : [];
        $warnings = $this->normalizeStringList($items['warnings'] ?? []);
        if (empty($warnings)) {
            return [];
        }

        $source = isset($entry['source']) ? sanitize_key((string) $entry['source']) : '';
        $sourceLabel = isset($entry['source_label']) ? trim((string) $entry['source_label']) : '';

        $grouped = [];
        $order = [];
        foreach ($warnings as $warning) {
            [$key, $label] = $this->resolveGroup($warning, $source, $sourceLabel);
            if (!isset($grouped[$key])) {
                $grouped[$key] = [
                    'key' => $key,
                    'label' => $label,
                    'count' => 0,
                    'items' => [],
                ];
                $order[] = $key;
            }

            $grouped[$key]['items'][] = $warning;
            $grouped[$key]['count']++;
        }

        $out = [];
        foreach ($order as $key) {
            $group = $grouped[$key] ?? null;
            if (!is_array($group)) {
                continue;
            }
            $group['items'] = array_values(array_unique(array_map('strval', $group['items'])));
            $group['count'] = count($group['items']);
            $out[] = $group;
        }

        return $out;
    }

    /**
     * @param array<int,array{key:string,label:string,count:int,items:array<int,string>}> $groups
     */
    public function summary(array $groups, int $limit = 3): string
    {
        if (empty($groups)) {
            return '';
        }

        $parts = [];
        foreach (array_slice($groups, 0, max(1, $limit)) as $group) {
            if (!is_array($group)) {
                continue;
            }
            $label = isset($group['label']) ? trim((string) $group['label']) : '';
            $count = max(0, (int) ($group['count'] ?? 0));
            if ($label === '' || $count <= 0) {
                continue;
            }
            $parts[] = sprintf(
                /* translators: 1: warning group label, 2: count */
                __('%1$s %2$d', 'wp-seo-next'),
                $label,
                $count
            );
        }

        if (count($groups) > max(1, $limit)) {
            $parts[] = '…';
        }

        return implode(' · ', $parts);
    }

    /**
     * @return array{0:string,1:string}
     */
    private function resolveGroup(string $warning, string $source, string $sourceLabel): array
    {
        $warning = trim($warning);
        if ($warning === '') {
            return ['general', __('General', 'wp-seo-next')];
        }

        if (preg_match('/^\s*([^:]{2,80}?)(?:\s*\((?:site|file)\))?\s*:/u', $warning, $match)) {
            $prefix = $this->normalizeGroupLabel((string) ($match[1] ?? ''));
            if ($prefix !== '') {
                $key = 'source:' . sanitize_key($prefix);
                return [$key, $prefix];
            }
        }

        $haystack = strtolower($warning);
        $knownSourceLabel = $this->normalizeGroupLabel($sourceLabel);
        if ($knownSourceLabel !== '' && $this->mentionsLabel($haystack, $knownSourceLabel)) {
            return ['source:' . sanitize_key($knownSourceLabel), $knownSourceLabel];
        }

        $knownSourceFromSlug = $this->normalizeGroupLabel($source);
        if ($knownSourceFromSlug !== '' && $this->mentionsLabel($haystack, $knownSourceFromSlug)) {
            return ['source:' . sanitize_key($knownSourceFromSlug), $knownSourceFromSlug];
        }

        if ($this->containsAny($haystack, ['wp seo next', 'backup', 'rollback', 'snapshot', 'restore', 'undo run', 'destination'])) {
            return ['destination', __('WP SEO Next / destination', 'wp-seo-next')];
        }

        if ($this->containsAny($haystack, ['migration', 'meta', 'schema', 'post', 'term', 'redirect', 'robots', 'sitemap', 'archive', 'canonical', 'search', 'content'])) {
            return ['migration', __('Migration / content', 'wp-seo-next')];
        }

        return ['general', __('General', 'wp-seo-next')];
    }

    private function normalizeGroupLabel(string $label): string
    {
        $label = trim($label);
        if ($label === '') {
            return '';
        }

        $label = preg_replace('/\s+/', ' ', $label) ?: $label;
        $lower = strtolower($label);

        $map = [
            'yoast export' => 'Yoast',
            'yoast' => 'Yoast',
            'rank math export' => 'Rank Math',
            'rank math' => 'Rank Math',
            'seopress export' => 'SEOPress',
            'seopress' => 'SEOPress',
            'aioseo export' => 'AIOSEO',
            'aioseo' => 'AIOSEO',
            'wp seo next json settings' => 'WP SEO Next',
            'wp seo next' => 'WP SEO Next',
        ];

        foreach ($map as $needle => $normalized) {
            if ($lower === $needle || str_contains($lower, $needle)) {
                return $normalized;
            }
        }

        return ucwords($label);
    }

    private function mentionsLabel(string $haystack, string $label): bool
    {
        $label = strtolower($label);
        return $label !== '' && str_contains($haystack, $label);
    }

    /**
     * @param array<int,string> $needles
     */
    private function containsAny(string $haystack, array $needles): bool
    {
        foreach ($needles as $needle) {
            if ($needle !== '' && str_contains($haystack, strtolower($needle))) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param mixed $items
     * @return array<int,string>
     */
    private function normalizeStringList($items): array
    {
        if (!is_array($items)) {
            return [];
        }

        $out = [];
        foreach ($items as $item) {
            if (!is_scalar($item)) {
                continue;
            }
            $value = trim((string) $item);
            if ($value === '') {
                continue;
            }
            $out[] = $value;
        }

        return array_values(array_unique($out));
    }
}
