<?php
namespace Crawl\Services;

use Crawl\Repositories\CrawlPageRepository;

/**
 * Compares two crawl runs and surfaces new issues, fixed issues, and page-level changes.
 */
class CrawlComparisonService
{
    private CrawlPageRepository $pages;
    private IssueService $issues;

    public function __construct()
    {
        $this->pages  = new CrawlPageRepository();
        $this->issues = new IssueService();
    }

    public function compare(int $runIdA, int $runIdB): array
    {
        $pagesA = $this->pages->allByRun($runIdA);
        $pagesB = $this->pages->allByRun($runIdB);

        $urlsA = array_flip(array_column($pagesA, 'url'));
        $urlsB = array_flip(array_column($pagesB, 'url'));

        $newPages     = array_keys(array_diff_key($urlsB, $urlsA));
        $removedPages = array_keys(array_diff_key($urlsA, $urlsB));

        // Status changes
        $indexA = array_column($pagesA, null, 'url');
        $indexB = array_column($pagesB, null, 'url');

        $statusChanged = [];
        foreach (array_intersect_key($indexA, $indexB) as $url => $rowA) {
            $scA = (int)($rowA['status_code']    ?? 0);
            $scB = (int)($indexB[$url]['status_code'] ?? 0);
            if ($scA !== $scB) {
                $statusChanged[] = ['url' => $url, 'from' => $scA, 'to' => $scB];
            }
        }

        // Issue diff
        $issuesA = $this->buildIssueSet($this->issues->summarize($pagesA));
        $issuesB = $this->buildIssueSet($this->issues->summarize($pagesB));

        $newIssues   = [];
        $fixedIssues = [];

        foreach ($issuesB as $label => $row) {
            if (!isset($issuesA[$label])) {
                $newIssues[] = array_merge($row, ['change' => '+' . $row['count']]);
            } elseif ($row['count'] > $issuesA[$label]['count']) {
                $delta       = $row['count'] - $issuesA[$label]['count'];
                $newIssues[] = array_merge($row, ['change' => '+' . $delta]);
            }
        }
        foreach ($issuesA as $label => $row) {
            if (!isset($issuesB[$label])) {
                $fixedIssues[] = array_merge($row, ['change' => '-' . $row['count']]);
            } elseif ($row['count'] > ($issuesB[$label]['count'] ?? 0)) {
                $delta         = $row['count'] - ($issuesB[$label]['count'] ?? 0);
                $fixedIssues[] = array_merge($row, ['change' => '-' . $delta]);
            }
        }

        $healthA = $this->issues->healthScore($this->issues->summarize($pagesA), count($pagesA));
        $healthB = $this->issues->healthScore($this->issues->summarize($pagesB), count($pagesB));

        return [
            'run_a'          => $runIdA,
            'run_b'          => $runIdB,
            'pages_a'        => count($pagesA),
            'pages_b'        => count($pagesB),
            'health_a'       => $healthA,
            'health_b'       => $healthB,
            'new_pages'      => array_slice($newPages,     0, 50),
            'removed_pages'  => array_slice($removedPages, 0, 50),
            'status_changed' => array_slice($statusChanged, 0, 50),
            'new_issues'     => $newIssues,
            'fixed_issues'   => $fixedIssues,
        ];
    }

    private function buildIssueSet(array $issues): array
    {
        $set = [];
        foreach ($issues as $issue) {
            $set[$issue['label']] = $issue;
        }
        return $set;
    }
}
