<?php

declare(strict_types=1);

namespace App\Support\Diff;

use InvalidArgumentException;

final readonly class DiffSummary
{
    public int $changedCount;

    public int $addedCount;

    public int $removedCount;

    public int $unchangedCount;

    public bool $hasRows;

    public ?string $message;

    public function __construct(
        int $changedCount = 0,
        int $addedCount = 0,
        int $removedCount = 0,
        int $unchangedCount = 0,
        ?string $message = null,
    ) {
        foreach ([
            'changedCount' => $changedCount,
            'addedCount' => $addedCount,
            'removedCount' => $removedCount,
            'unchangedCount' => $unchangedCount,
        ] as $field => $value) {
            if ($value < 0) {
                throw new InvalidArgumentException(sprintf('DiffSummary %s must be zero or greater.', $field));
            }
        }

        $this->changedCount = $changedCount;
        $this->addedCount = $addedCount;
        $this->removedCount = $removedCount;
        $this->unchangedCount = $unchangedCount;
        $this->message = is_string($message) && trim($message) !== ''
            ? trim($message)
            : null;
        $this->hasRows = ($changedCount + $addedCount + $removedCount + $unchangedCount) > 0;
    }

    /**
     * @param  array<int, DiffRow>  $rows
     */
    public static function fromRows(array $rows, ?string $message = null): self
    {
        $counts = [
            DiffRowStatus::Changed->value => 0,
            DiffRowStatus::Added->value => 0,
            DiffRowStatus::Removed->value => 0,
            DiffRowStatus::Unchanged->value => 0,
        ];

        foreach ($rows as $row) {
            if (! $row instanceof DiffRow) {
                continue;
            }

            $counts[$row->status->value]++;
        }

        if ($message === null) {
            $message = match (true) {
                $rows === [] => 'No diff data available.',
                ($counts[DiffRowStatus::Changed->value]
                    + $counts[DiffRowStatus::Added->value]
                    + $counts[DiffRowStatus::Removed->value]) === 0 => 'No changes detected.',
                default => null,
            };
        }

        return new self(
            changedCount: $counts[DiffRowStatus::Changed->value],
            addedCount: $counts[DiffRowStatus::Added->value],
            removedCount: $counts[DiffRowStatus::Removed->value],
            unchangedCount: $counts[DiffRowStatus::Unchanged->value],
            message: $message,
        );
    }

    public static function empty(?string $message = 'No diff data available.'): self
    {
        return new self(message: $message);
    }

    public function totalCount(): int
    {
        return $this->changedCount + $this->addedCount + $this->removedCount + $this->unchangedCount;
    }
}
