<?php

namespace WPSN\Modules\ImportExport;

defined('ABSPATH') || exit;

final class ImportAssistSummaryBuilder
{
    /**
     * @param array<string,mixed> $report
     * @return array<string,mixed>
     */
    public function fromSettingsJsonRestore(array $report): array
    {
        $reportId = isset($report['id']) ? sanitize_text_field((string) $report['id']) : '';
        $items = [
            'source_found' => [__('Settings JSON payload detected.', 'wp-seo-next')],
            'applied' => [__('Settings JSON imported into WP SEO Next.', 'wp-seo-next')],
            'skipped' => [],
            'not_mapped' => [],
            'warnings' => [],
            'manual_actions' => [],
        ];

        if (!empty($report['snapshot_id'])) {
            $items['applied'][] = __('Automatic backup snapshot created before import.', 'wp-seo-next');
        } else {
            $items['warnings'][] = __('Backup snapshot was not created before the JSON import.', 'wp-seo-next');
            $items['manual_actions'][] = __('Review critical settings manually because no automatic rollback snapshot was recorded for this JSON import.', 'wp-seo-next');
        }

        return [
            'kind' => 'settings_restore',
            'source' => 'wp_seo_next',
            'source_label' => __('WP SEO Next JSON settings', 'wp-seo-next'),
            'context' => 'file',
            'title' => __('Settings JSON import', 'wp-seo-next'),
            'created_at' => isset($report['created_at']) ? (string) $report['created_at'] : gmdate('c'),
            'summary' => [
                'found' => count($items['source_found']),
                'applied' => count($items['applied']),
                'skipped' => 0,
                'not_mapped' => 0,
                'warnings' => count($items['warnings']),
                'manual' => count($items['manual_actions']),
                'posts_updated' => 0,
                'terms_updated' => 0,
            ],
            'items' => $items,
            'meta' => [
                'report_id' => $reportId,
                'snapshot_id' => isset($report['snapshot_id']) ? (int) $report['snapshot_id'] : 0,
                'undo_run_id' => isset($report['undo_run_id']) ? (string) $report['undo_run_id'] : '',
            ],
        ];
    }

    /**
     * @param array<string,mixed> $report
     * @return array<string,mixed>
     */
    public function fromSettingsImport(string $source, array $report, string $context = 'file'): array
    {
        $source = sanitize_key($source);
        $context = $context === 'site' ? 'site' : 'file';

        $applied = $this->normalizeStringList($report['applied'] ?? []);
        $skipped = $this->normalizeStringList($report['skipped'] ?? []);
        $notMapped = $this->normalizeStringList($report['not_mapped'] ?? []);
        $warnings = $this->normalizeStringList($report['warnings'] ?? []);
        $sourceFoundRaw = $this->normalizeStringList($report['source_found'] ?? []);
        if (empty($sourceFoundRaw)) {
            $sourceFoundRaw = array_values(array_unique(array_merge($applied, $skipped, $notMapped)));
        }
        $manual = $this->manualActionsForSettingsImport($source, $applied, $skipped, $warnings, $notMapped);

        if (!empty($report['snapshot_id'])) {
            $applied[] = __('Automatic backup snapshot created before import.', 'wp-seo-next');
        }

        return [
            'kind' => 'settings_import',
            'source' => $source,
            'source_label' => $this->sourceLabel($source),
            'context' => $context,
            'title' => sprintf(
                /* translators: %s is plugin/source name */
                __('%s settings import', 'wp-seo-next'),
                $this->sourceLabel($source)
            ),
            'created_at' => isset($report['imported_at']) ? (string) $report['imported_at'] : gmdate('c'),
            'summary' => [
                'found' => count($sourceFoundRaw),
                'applied' => count($applied),
                'skipped' => count($skipped),
                'not_mapped' => count($notMapped),
                'warnings' => count($warnings),
                'manual' => count($manual),
                'posts_updated' => 0,
                'terms_updated' => 0,
            ],
            'items' => [
                'source_found' => $this->friendlySettingsPaths($sourceFoundRaw),
                'applied' => $this->friendlySettingsPaths($applied),
                'skipped' => $this->friendlySettingsPaths($skipped),
                'not_mapped' => $notMapped,
                'warnings' => $warnings,
                'manual_actions' => $manual,
            ],
            'meta' => [
                'report_id' => isset($report['id']) ? sanitize_text_field((string) $report['id']) : '',
                'snapshot_id' => isset($report['snapshot_id']) ? (int) $report['snapshot_id'] : 0,
                'undo_run_id' => isset($report['undo_run_id']) ? (string) $report['undo_run_id'] : '',
                'overwrite' => !empty($report['overwrite']),
            ],
        ];
    }

    /**
     * @param array<string,mixed> $report
     * @return array<string,mixed>
     */
    public function fromMigrationReport(array $report): array
    {
        $job = isset($report['job']) && is_array($report['job']) ? $report['job'] : [];
        $counts = isset($report['counts']) && is_array($report['counts']) ? $report['counts'] : [];
        $fields = isset($report['fields']) && is_array($report['fields']) ? $report['fields'] : [];
        $terms = isset($report['terms']) && is_array($report['terms']) ? $report['terms'] : [];
        $caps = isset($report['capabilities']) && is_array($report['capabilities']) ? $report['capabilities'] : [];

        $sourceFound = [];
        $applied = [];
        $skipped = [];
        $warnings = [];
        $manual = [];

        foreach ($fields as $slug => $row) {
            if (!is_array($row)) {
                continue;
            }
            $label = $this->fieldLabel((string) $slug);
            $imported = isset($row['imported']) ? (int) $row['imported'] : 0;
            $overwritten = isset($row['overwritten']) ? (int) $row['overwritten'] : 0;
            $skippedExisting = isset($row['skipped_existing']) ? (int) $row['skipped_existing'] : 0;
            $missingSource = isset($row['missing_source']) ? (int) $row['missing_source'] : 0;

            if ($imported > 0 || $overwritten > 0 || $skippedExisting > 0 || $missingSource > 0) {
                $sourceFound[] = $label;
            }

            if ($imported > 0 || $overwritten > 0) {
                $parts = [];
                if ($imported > 0) {
                    $parts[] = sprintf(__('imported %d', 'wp-seo-next'), $imported);
                }
                if ($overwritten > 0) {
                    $parts[] = sprintf(__('overwrote %d', 'wp-seo-next'), $overwritten);
                }
                $applied[] = $label . ': ' . implode(', ', $parts);
            }
            if ($skippedExisting > 0) {
                $skipped[] = $label . ': ' . sprintf(__('kept %d existing values', 'wp-seo-next'), $skippedExisting);
            }
            if ($missingSource > 0) {
                $warnings[] = $label . ': ' . sprintf(__('source had no value for %d items', 'wp-seo-next'), $missingSource);
            }
        }

        $termsUpdated = isset($terms['updated']) ? (int) $terms['updated'] : 0;
        $termsSkipped = isset($terms['skipped_existing']) ? (int) $terms['skipped_existing'] : 0;
        $termsMissing = isset($terms['missing_source']) ? (int) $terms['missing_source'] : 0;
        if ($termsUpdated > 0 || $termsSkipped > 0 || $termsMissing > 0) {
            $sourceFound[] = __('Taxonomy SEO fields', 'wp-seo-next');
        }
        if ($termsUpdated > 0) {
            $applied[] = sprintf(__('Taxonomy SEO meta updated: %d terms', 'wp-seo-next'), $termsUpdated);
        }
        if ($termsSkipped > 0) {
            $skipped[] = sprintf(__('Taxonomy SEO meta kept existing values on %d terms', 'wp-seo-next'), $termsSkipped);
        }
        if ($termsMissing > 0) {
            $warnings[] = sprintf(__('Taxonomy source fields were missing on %d terms', 'wp-seo-next'), $termsMissing);
        }

        $manual = array_merge($manual, $this->manualActionsForMigration($caps, $job));
        $sourceFound = array_values(array_unique(array_filter(array_map('strval', $sourceFound))));

        return [
            'kind' => 'migration',
            'source' => isset($job['source']) ? sanitize_key((string) $job['source']) : '',
            'source_label' => $this->sourceLabel(isset($job['source']) ? (string) $job['source'] : ''),
            'context' => 'site',
            'title' => sprintf(
                /* translators: %s is source plugin name */
                __('%s content migration', 'wp-seo-next'),
                $this->sourceLabel(isset($job['source']) ? (string) $job['source'] : '')
            ),
            'created_at' => gmdate('c'),
            'summary' => [
                'found' => count($sourceFound),
                'applied' => count($applied),
                'skipped' => count($skipped),
                'not_mapped' => 0,
                'warnings' => count($warnings),
                'manual' => count($manual),
                'posts_updated' => isset($counts['posts_updated']) ? (int) $counts['posts_updated'] : 0,
                'terms_updated' => $termsUpdated,
            ],
            'items' => [
                'source_found' => $sourceFound,
                'applied' => $applied,
                'skipped' => $skipped,
                'not_mapped' => [],
                'warnings' => $warnings,
                'manual_actions' => array_values(array_unique(array_filter(array_map('strval', $manual)))),
            ],
            'meta' => [
                'job_id' => isset($job['id']) ? (string) $job['id'] : '',
                'post_types' => isset($job['post_types']) && is_array($job['post_types']) ? array_values(array_map('strval', $job['post_types'])) : [],
                'overwrite' => !empty($job['overwrite']),
            ],
        ];
    }

    /**
     * @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));
    }

    /**
     * @param array<int,string> $items
     * @return array<int,string>
     */
    private function friendlySettingsPaths(array $items): array
    {
        $out = [];
        foreach ($items as $item) {
            $key = trim($item);
            if ($key === '') {
                continue;
            }
            $out[] = $this->settingsPathLabel($key);
        }
        return array_values(array_unique($out));
    }

    private function sourceLabel(string $source): string
    {
        $map = [
            'yoast' => __('Yoast SEO', 'wp-seo-next'),
            'rankmath' => __('Rank Math', 'wp-seo-next'),
            'seopress' => __('SEOPress', 'wp-seo-next'),
            'aioseo' => __('AIOSEO / All in One SEO', 'wp-seo-next'),
            'wp_seo_next' => __('WP SEO Next', 'wp-seo-next'),
        ];

        $source = sanitize_key($source);
        return $map[$source] ?? __('Import', 'wp-seo-next');
    }

    private function settingsPathLabel(string $path): string
    {
        $map = [
            'separator' => __('Title separator', 'wp-seo-next'),
            'templates.title' => __('Default title template', 'wp-seo-next'),
            'templates.description' => __('Default meta description template', 'wp-seo-next'),
            'templates.author_title' => __('Author title template', 'wp-seo-next'),
            'templates.author_description' => __('Author meta description template', 'wp-seo-next'),
            'social.facebook.default_title' => __('Default Facebook title', 'wp-seo-next'),
            'social.facebook.default_description' => __('Default Facebook description', 'wp-seo-next'),
            'social.twitter.default_title' => __('Default Twitter/X title', 'wp-seo-next'),
            'social.twitter.default_description' => __('Default Twitter/X description', 'wp-seo-next'),
            'schema.site_name' => __('Schema site name', 'wp-seo-next'),
            'schema.org_name' => __('Organization name', 'wp-seo-next'),
            'schema.org_logo_url' => __('Organization logo URL', 'wp-seo-next'),
            'schema.org_same_as' => __('Organization social profiles', 'wp-seo-next'),
            'schema.org_alternate_name' => __('Organization alternate name', 'wp-seo-next'),
            'schema.org_description' => __('Organization description', 'wp-seo-next'),
            'schema.org_email' => __('Organization email', 'wp-seo-next'),
            'schema.org_telephone' => __('Organization telephone', 'wp-seo-next'),
            'schema.org_legal_name' => __('Organization legal name', 'wp-seo-next'),
            'schema.org_founding_date' => __('Organization founding date', 'wp-seo-next'),
            'schema.org_number_employees' => __('Organization employee count', 'wp-seo-next'),
            'schema.org_vat_id' => __('Organization VAT ID', 'wp-seo-next'),
            'schema.org_tax_id' => __('Organization tax ID', 'wp-seo-next'),
            'schema.org_iso6523_code' => __('Organization ISO 6523 code', 'wp-seo-next'),
            'schema.org_duns' => __('Organization DUNS', 'wp-seo-next'),
            'schema.org_lei_code' => __('Organization LEI code', 'wp-seo-next'),
            'schema.org_naics' => __('Organization NAICS', 'wp-seo-next'),
            'schema.policy_urls.publishing_principles' => __('Publishing principles URL', 'wp-seo-next'),
            'schema.policy_urls.ownership_funding_info' => __('Ownership / funding info URL', 'wp-seo-next'),
            'schema.policy_urls.actionable_feedback_policy' => __('Actionable feedback policy URL', 'wp-seo-next'),
            'schema.policy_urls.corrections_policy' => __('Corrections policy URL', 'wp-seo-next'),
            'schema.policy_urls.ethics_policy' => __('Ethics policy URL', 'wp-seo-next'),
            'schema.policy_urls.diversity_policy' => __('Diversity policy URL', 'wp-seo-next'),
            'schema.policy_urls.diversity_staffing_report' => __('Diversity staffing report URL', 'wp-seo-next'),
            'schema.organization.name' => __('Organization name', 'wp-seo-next'),
            'schema.organization.logo' => __('Organization logo', 'wp-seo-next'),
            'robots.author_archive' => __('Author archive robots', 'wp-seo-next'),
            'robots.date_archive' => __('Date archive robots', 'wp-seo-next'),
            'robots.post_type_archive' => __('Post type archive robots', 'wp-seo-next'),
            'indexnow.enabled' => __('IndexNow enabled', 'wp-seo-next'),
            'indexnow.key' => __('IndexNow key', 'wp-seo-next'),
            'breadcrumbs.enabled' => __('Breadcrumbs enabled', 'wp-seo-next'),
            'breadcrumbs.separator' => __('Breadcrumb separator', 'wp-seo-next'),
            'breadcrumbs.home_label' => __('Breadcrumb home label', 'wp-seo-next'),
            'search_urls.enabled' => __('Search URL canonicalization enabled', 'wp-seo-next'),
            'search_urls.mode' => __('Search URL cleanup mode', 'wp-seo-next'),
            'search_urls.sanitize_query' => __('Search query sanitizer enabled', 'wp-seo-next'),
            'search_urls.query_limit' => __('Search query length limit', 'wp-seo-next'),
            'search_urls.strip_emoji' => __('Remove emoji from search queries', 'wp-seo-next'),
            'search_urls.block_common_patterns' => __('Block common search spam patterns', 'wp-seo-next'),
            'search_urls.invalid_action' => __('Invalid search query action', 'wp-seo-next'),
            'search_urls.strip_category_base' => __('Strip category base', 'wp-seo-next'),
        ];

        if (isset($map[$path])) {
            return $map[$path];
        }

        if (strpos($path, 'schema.default_types.') === 0) {
            $postType = sanitize_key(substr($path, strlen('schema.default_types.')));
            $label = $postType !== '' ? $postType : __('content', 'wp-seo-next');
            if ($postType !== '' && function_exists('get_post_type_object')) {
                $object = get_post_type_object($postType);
                if (is_object($object)) {
                    if (isset($object->labels) && is_object($object->labels) && isset($object->labels->singular_name) && is_string($object->labels->singular_name) && $object->labels->singular_name !== '') {
                        $label = $object->labels->singular_name;
                    } elseif (isset($object->label) && is_string($object->label) && $object->label !== '') {
                        $label = $object->label;
                    }
                }
            }

            return sprintf(__('Default schema type for %s', 'wp-seo-next'), $label);
        }

        return str_replace(['.', '_'], [' → ', ' '], $path);
    }

    private function fieldLabel(string $slug): string
    {
        $map = [
            'title' => __('SEO title', 'wp-seo-next'),
            'description' => __('Meta description', 'wp-seo-next'),
            'focus_keyword' => __('Focus keyword', 'wp-seo-next'),
            'canonical' => __('Canonical URL', 'wp-seo-next'),
            'robots_index' => __('Robots index', 'wp-seo-next'),
            'robots_follow' => __('Robots follow', 'wp-seo-next'),
            'og_title' => __('OG title', 'wp-seo-next'),
            'og_description' => __('OG description', 'wp-seo-next'),
            'og_image' => __('OG image', 'wp-seo-next'),
            'twitter_title' => __('Twitter/X title', 'wp-seo-next'),
            'twitter_description' => __('Twitter/X description', 'wp-seo-next'),
            'twitter_image' => __('Twitter/X image', 'wp-seo-next'),
            'schema_type' => __('Schema type', 'wp-seo-next'),
        ];

        return $map[$slug] ?? str_replace('_', ' ', $slug);
    }

    /**
     * @param array<int,string> $applied
     * @param array<int,string> $skipped
     * @param array<int,string> $warnings
     * @return array<int,string>
     */
    private function manualActionsForSettingsImport(string $source, array $applied, array $skipped, array $warnings, array $notMapped = []): array
    {
        $manual = [];
        $combined = array_map('strtolower', array_merge($applied, $skipped, $warnings, $notMapped));

        foreach ($combined as $line) {
            if (strpos($line, 'author_archive') !== false || strpos($line, 'date_archive') !== false || strpos($line, 'post_type_archive') !== false) {
                $manual[] = __('Review Archive Controls after import, especially if the old plugin disabled author/date/post type archives.', 'wp-seo-next');
            }
            if (strpos($line, 'stripcategorybase') !== false || strpos($line, 'category') !== false && strpos($line, 'base') !== false) {
                $manual[] = __('Review category/permalink behavior manually if the source plugin handled “strip category base” or custom archive rewrites.', 'wp-seo-next');
            }
            if (strpos($line, 'schema') !== false || strpos($line, 'json-ld') !== false) {
                $manual[] = __('Review Schema settings and custom JSON-LD templates. Source plugins often store schema defaults in different formats.', 'wp-seo-next');
            }
            if (strpos($line, 'policy') !== false || strpos($line, 'publishing principles') !== false || strpos($line, 'ownership funding') !== false || strpos($line, 'corrections') !== false) {
                $manual[] = __('Review editorial policy / trust pages after import so the referenced URLs still point to the correct public pages.', 'wp-seo-next');
            }
            if (strpos($line, 'local') !== false || strpos($line, 'organization') !== false || strpos($line, 'logo') !== false) {
                $manual[] = __('Review Local / Organization details manually, including logo, business name, and address fields.', 'wp-seo-next');
            }
            if (strpos($line, 'indexnow.key') !== false && strpos($line, 'skipped') !== false) {
                $manual[] = __('Generate or enter an IndexNow key manually if the source export did not contain one.', 'wp-seo-next');
            }
            if (strpos($line, 'breadcrumb') !== false) {
                $manual[] = __('Review breadcrumb separators, home label, and archive behavior after the import.', 'wp-seo-next');
            }
        }

        if (!empty($notMapped)) {
            $manual[] = __('Review the “Not mapped yet” items in the import report and finish any unresolved source-specific settings manually.', 'wp-seo-next');
        }

        if (in_array($source, ['yoast', 'rankmath', 'seopress', 'aioseo'], true) && empty($applied) && empty($skipped) && empty($warnings) && empty($notMapped)) {
            $manual[] = __('The source import report did not expose detailed field data. Review the imported settings manually.', 'wp-seo-next');
        }

        return array_values(array_unique(array_filter(array_map('strval', $manual))));
    }

    /**
     * @param array<string,mixed> $caps
     * @param array<string,mixed> $job
     * @return array<int,string>
     */
    private function manualActionsForMigration(array $caps, array $job): array
    {
        $manual = [];
        $notSupported = isset($caps['not_supported']) && is_array($caps['not_supported']) ? array_map('strval', $caps['not_supported']) : [];
        foreach ($notSupported as $slug) {
            switch ($slug) {
                case 'redirects':
                    $manual[] = __('Review redirects manually or run Redirect Migration, because redirect rules are not part of the core meta migration.', 'wp-seo-next');
                    break;
                case 'breadcrumbs_settings':
                    $manual[] = __('Review breadcrumb settings manually. Breadcrumb config is not migrated as post/term meta.', 'wp-seo-next');
                    break;
                case 'schema_type':
                    $manual[] = __('Review schema defaults or custom JSON-LD manually for content that depended on source-plugin schema settings.', 'wp-seo-next');
                    break;
                case 'focus_keywords':
                    $manual[] = __('Focus keywords were not available from this source. Add them manually where needed.', 'wp-seo-next');
                    break;
                case 'readability_scores':
                    $manual[] = __('Readability scores and editorial guidance are not migrated. Re-check key content manually if you relied on them.', 'wp-seo-next');
                    break;
                default:
                    $manual[] = sprintf(__('Review unsupported migration area manually: %s.', 'wp-seo-next'), $slug);
                    break;
            }
        }

        if (!empty($job['include_social'])) {
            $manual[] = __('Spot-check social meta on a few important posts/pages after migration to confirm images and defaults came across as expected.', 'wp-seo-next');
        }
        if (!empty($job['include_schema'])) {
            $manual[] = __('Spot-check schema-related pages after migration. Source schema settings can map differently across plugins.', 'wp-seo-next');
        }
        if (!empty($job['include_terms'])) {
            $manual[] = __('Review important category/tag archive pages after term-meta migration.', 'wp-seo-next');
        }

        return array_values(array_unique(array_filter(array_map('strval', $manual))));
    }
}
