<?php

namespace WPSN\Modules\Diagnostics;

defined('ABSPATH') || exit;

/**
 * Builds a compact Platform Ops summary for Dashboard / Tasks Hub / Platform Ops.
 */
final class PlatformOpsCompactSummaryBuilder
{
    private PlatformOpsReportRepository $reports;
    private PlatformOpsDiffBuilder $diffs;

    public function __construct(
        ?PlatformOpsReportRepository $reports = null,
        ?PlatformOpsDiffBuilder $diffs = null
    ) {
        $this->reports = $reports ?? new PlatformOpsReportRepository();
        $this->diffs = $diffs ?? new PlatformOpsDiffBuilder();
    }

    /**
     * @return array<string,mixed>
     */
    public function build(): array
    {
        return $this->buildFromRuns(
            $this->reports->getLast(),
            $this->reports->getHistory(10),
            $this->reports->getPinned()
        );
    }

    /**
     * @param array<string,mixed> $last
     * @param array<int,array<string,mixed>> $history
     * @param array<string,mixed> $pinned
     * @return array<string,mixed>
     */
    public function buildFromRuns(array $last, array $history, array $pinned = []): array
    {
        $candidate = $this->pickCandidate($last, $history);
        if (empty($candidate)) {
            return [
                'available' => false,
                'has_comparison' => false,
                'candidate_is_pinned' => false,
                'compare_status' => 'info',
                'value' => __('Not run', 'wp-seo-next'),
                'hint' => __('Run Platform Ops to capture a backend health snapshot.', 'wp-seo-next'),
                'meta' => '',
                'summary' => __('No saved platform probe run yet.', 'wp-seo-next'),
                'baseline_context' => '',
                'baseline_label' => '',
                'badges' => [
                    [
                        'tone' => 'info',
                        'label' => __('Platform not run', 'wp-seo-next'),
                    ],
                ],
                'candidate' => [],
                'baseline' => [],
                'diff' => [],
            ];
        }

        $candidateId = isset($candidate['id']) ? trim((string) $candidate['id']) : '';
        $candidateSeverity = $this->normalizeSeverity(isset($candidate['severity']) ? (string) $candidate['severity'] : 'info');
        $candidateIsPinned = !empty($pinned) && $candidateId !== '' && (string) ($pinned['id'] ?? '') === $candidateId;

        [$baseline, $baselineContext, $baselineLabel, $baselineShort] = $this->pickBaseline($candidateId, $history, $pinned);

        $hasComparison = !empty($baseline) && $candidateId !== '' && (string) ($baseline['id'] ?? '') !== $candidateId;
        $diff = $hasComparison ? $this->diffs->compare($baseline, $candidate) : [];
        $compareStatus = $hasComparison ? $this->deriveCompareStatus($diff) : 'info';
        $badges = $this->buildBadges($candidateSeverity, $candidateIsPinned, $baselineShort, $hasComparison, $compareStatus, $diff);

        return [
            'available' => true,
            'has_comparison' => $hasComparison,
            'candidate_is_pinned' => $candidateIsPinned,
            'compare_status' => $compareStatus,
            'value' => $this->buildValue($candidateSeverity, $hasComparison, $compareStatus, $diff),
            'hint' => $this->buildHint($candidate, $hasComparison, $baselineShort, $candidateIsPinned),
            'meta' => $this->buildMeta($candidate, $baseline, $hasComparison, $baselineLabel, $candidateIsPinned),
            'summary' => $this->buildSummary($candidateSeverity, $hasComparison, $diff),
            'baseline_context' => $baselineContext,
            'baseline_label' => $baselineLabel,
            'badges' => $badges,
            'candidate' => $candidate,
            'baseline' => $baseline,
            'diff' => $diff,
        ];
    }

    /**
     * @param array<string,mixed> $last
     * @param array<int,array<string,mixed>> $history
     * @return array<string,mixed>
     */
    private function pickCandidate(array $last, array $history): array
    {
        if (!empty($last) && !empty($last['id'])) {
            return $last;
        }

        foreach ($history as $row) {
            if (is_array($row) && !empty($row['id'])) {
                return $row;
            }
        }

        return [];
    }

    /**
     * @param array<int,array<string,mixed>> $history
     * @param array<string,mixed> $pinned
     * @return array{0:array<string,mixed>,1:string,2:string,3:string}
     */
    private function pickBaseline(string $candidateId, array $history, array $pinned): array
    {
        if (!empty($pinned) && !empty($pinned['id']) && (string) $pinned['id'] !== $candidateId) {
            return [
                $pinned,
                'pinned',
                __('Pinned healthy baseline', 'wp-seo-next'),
                __('Vs pinned', 'wp-seo-next'),
            ];
        }

        foreach ($history as $row) {
            if (!is_array($row) || empty($row['id'])) {
                continue;
            }
            if ((string) $row['id'] === $candidateId) {
                continue;
            }

            return [
                $row,
                'previous',
                __('Previous run', 'wp-seo-next'),
                __('Vs previous', 'wp-seo-next'),
            ];
        }

        return [[], '', '', ''];
    }

    private function buildValue(string $candidateSeverity, bool $hasComparison, string $compareStatus, array $diff): string
    {
        if (!$hasComparison) {
            return strtoupper($candidateSeverity);
        }

        $summary = isset($diff['summary']) && is_array($diff['summary']) ? $diff['summary'] : [];
        $regressions = (int) ($summary['regressions'] ?? 0);
        if ($regressions > 0) {
            return $this->countLabel($regressions, __('regression', 'wp-seo-next'), __('regressions', 'wp-seo-next'));
        }

        if ($compareStatus === 'improved') {
            return __('Improved', 'wp-seo-next');
        }
        if ($compareStatus === 'changed') {
            return __('Changed', 'wp-seo-next');
        }
        if ($compareStatus === 'unchanged') {
            return __('Stable', 'wp-seo-next');
        }

        return strtoupper($candidateSeverity);
    }

    /**
     * @param array<string,mixed> $candidate
     */
    private function buildHint(array $candidate, bool $hasComparison, string $baselineShort, bool $candidateIsPinned): string
    {
        $candidateTs = isset($candidate['created_at_utc']) ? trim((string) $candidate['created_at_utc']) : '';
        $parts = [];
        if ($candidateTs !== '') {
            $parts[] = sprintf(__('Last run: %s', 'wp-seo-next'), $candidateTs);
        }

        if ($hasComparison && $baselineShort !== '') {
            $parts[] = $baselineShort;
        } elseif ($candidateIsPinned) {
            $parts[] = __('Pinned baseline', 'wp-seo-next');
        } else {
            $parts[] = __('Enable platform flip notifications or pin a healthy baseline to track regressions.', 'wp-seo-next');
        }

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

    /**
     * @param array<string,mixed> $candidate
     * @param array<string,mixed> $baseline
     */
    private function buildMeta(array $candidate, array $baseline, bool $hasComparison, string $baselineLabel, bool $candidateIsPinned): string
    {
        $candidateTs = isset($candidate['created_at_utc']) ? trim((string) $candidate['created_at_utc']) : '';
        if (!$hasComparison) {
            if ($candidateTs === '') {
                return $candidateIsPinned
                    ? __('Latest saved platform run is pinned as the healthy baseline.', 'wp-seo-next')
                    : __('Save another platform run or pin a healthy baseline to unlock compact backend diffs.', 'wp-seo-next');
            }

            if ($candidateIsPinned) {
                return sprintf(__('Latest saved platform run %s is pinned as the healthy baseline.', 'wp-seo-next'), $candidateTs);
            }

            return sprintf(__('Latest saved platform run %s has no comparison baseline yet.', 'wp-seo-next'), $candidateTs);
        }

        $baselineTs = isset($baseline['created_at_utc']) ? trim((string) $baseline['created_at_utc']) : '';
        if ($baselineTs !== '' && $candidateTs !== '') {
            return sprintf(__('%1$s: %2$s → Candidate: %3$s', 'wp-seo-next'), $baselineLabel, $baselineTs, $candidateTs);
        }

        return $baselineLabel;
    }

    private function buildSummary(string $candidateSeverity, bool $hasComparison, array $diff): string
    {
        if (!$hasComparison) {
            return sprintf(__('Latest platform snapshot severity: %s.', 'wp-seo-next'), strtoupper($candidateSeverity));
        }

        $summary = isset($diff['summary']) && is_array($diff['summary']) ? $diff['summary'] : [];
        $countDeltas = isset($summary['count_deltas']) && is_array($summary['count_deltas']) ? $summary['count_deltas'] : [];

        return sprintf(
            __('Regressions: %1$d · Improvements: %2$d · Changed: %3$d · Critical Δ %4$s', 'wp-seo-next'),
            (int) ($summary['regressions'] ?? 0),
            (int) ($summary['improvements'] ?? 0),
            (int) ($summary['changed'] ?? 0),
            $this->signed((int) ($countDeltas['critical'] ?? 0))
        );
    }

    /**
     * @param array<string,mixed> $diff
     * @return array<int,array{tone:string,label:string}>
     */
    private function buildBadges(
        string $candidateSeverity,
        bool $candidateIsPinned,
        string $baselineShort,
        bool $hasComparison,
        string $compareStatus,
        array $diff
    ): array {
        $badges = [
            [
                'tone' => $this->toneForSeverity($candidateSeverity),
                'label' => sprintf(__('Platform %s', 'wp-seo-next'), strtoupper($candidateSeverity)),
            ],
        ];

        if ($hasComparison && $baselineShort !== '') {
            $badges[] = [
                'tone' => 'info',
                'label' => $baselineShort,
            ];
        } elseif ($candidateIsPinned) {
            $badges[] = [
                'tone' => 'info',
                'label' => __('Pinned baseline', 'wp-seo-next'),
            ];
        }

        if (!$hasComparison) {
            return $badges;
        }

        $summary = isset($diff['summary']) && is_array($diff['summary']) ? $diff['summary'] : [];
        $regressions = (int) ($summary['regressions'] ?? 0);
        $improvements = (int) ($summary['improvements'] ?? 0);
        $changed = (int) ($summary['changed'] ?? 0);
        $countDeltas = isset($summary['count_deltas']) && is_array($summary['count_deltas']) ? $summary['count_deltas'] : [];
        $criticalDelta = (int) ($countDeltas['critical'] ?? 0);

        if ($compareStatus === 'regressed') {
            $badges[] = ['tone' => 'bad', 'label' => __('Regressed', 'wp-seo-next')];
        } elseif ($compareStatus === 'improved') {
            $badges[] = ['tone' => 'ok', 'label' => __('Improved', 'wp-seo-next')];
        } elseif ($compareStatus === 'changed') {
            $badges[] = ['tone' => 'warn', 'label' => __('Changed', 'wp-seo-next')];
        } else {
            $badges[] = ['tone' => 'info', 'label' => __('Stable', 'wp-seo-next')];
        }

        if ($regressions > 0) {
            $badges[] = [
                'tone' => 'bad',
                'label' => $this->countLabel($regressions, __('regression', 'wp-seo-next'), __('regressions', 'wp-seo-next')),
            ];
        }
        if ($improvements > 0) {
            $badges[] = [
                'tone' => 'ok',
                'label' => $this->countLabel($improvements, __('improvement', 'wp-seo-next'), __('improvements', 'wp-seo-next')),
            ];
        }
        if ($changed > 0 && $regressions === 0 && $improvements === 0) {
            $badges[] = [
                'tone' => 'warn',
                'label' => $this->countLabel($changed, __('changed check', 'wp-seo-next'), __('changed checks', 'wp-seo-next')),
            ];
        }
        if ($criticalDelta !== 0) {
            $badges[] = [
                'tone' => $criticalDelta > 0 ? 'bad' : 'ok',
                'label' => sprintf(__('Critical %s', 'wp-seo-next'), $this->signed($criticalDelta)),
            ];
        }

        return $badges;
    }

    /**
     * @param array<string,mixed> $diff
     */
    private function deriveCompareStatus(array $diff): string
    {
        $summary = isset($diff['summary']) && is_array($diff['summary']) ? $diff['summary'] : [];
        $status = sanitize_key((string) ($summary['severity_status'] ?? ''));
        if (in_array($status, ['regressed', 'improved', 'changed', 'unchanged'], true)) {
            return $status;
        }

        if ((int) ($summary['regressions'] ?? 0) > 0) {
            return 'regressed';
        }
        if ((int) ($summary['improvements'] ?? 0) > 0) {
            return 'improved';
        }
        if ((int) ($summary['changed'] ?? 0) > 0) {
            return 'changed';
        }

        return 'unchanged';
    }

    private function toneForSeverity(string $severity): string
    {
        if ($severity === 'critical') {
            return 'bad';
        }
        if ($severity === 'warn') {
            return 'warn';
        }
        if ($severity === 'ok') {
            return 'ok';
        }

        return 'info';
    }

    private function normalizeSeverity(string $severity): string
    {
        $severity = sanitize_key($severity);
        if (!in_array($severity, ['ok', 'warn', 'critical'], true)) {
            return 'info';
        }

        return $severity;
    }

    private function countLabel(int $count, string $one, string $many): string
    {
        return sprintf('%d %s', $count, $count === 1 ? $one : $many);
    }

    private function signed(int $value): string
    {
        if ($value > 0) {
            return '+' . $value;
        }
        if ($value < 0) {
            return (string) $value;
        }

        return '0';
    }
}
