<?php

declare(strict_types=1);

namespace App\Support\Inventory;

use App\Models\OperationRun;
use InvalidArgumentException;

final readonly class TenantCoverageTruth
{
    /**
     * @param  list<TenantCoverageTypeTruth>  $rows
     */
    public function __construct(
        public int $tenantId,
        public ?OperationRun $basisRun,
        public bool $hasCurrentCoverageResult,
        public int $supportedTypeCount,
        public int $succeededTypeCount,
        public int $failedTypeCount,
        public int $skippedTypeCount,
        public int $unknownTypeCount,
        public int $followUpTypeCount,
        public int $observedItemTotal,
        public array $rows,
    ) {
        if ($this->tenantId <= 0) {
            throw new InvalidArgumentException('ManagedEnvironment coverage truth requires a positive tenant id.');
        }

        if ($this->supportedTypeCount < 0 || $this->observedItemTotal < 0) {
            throw new InvalidArgumentException('ManagedEnvironment coverage truth counts must be zero or greater.');
        }
    }

    public function basisRunId(): ?int
    {
        return $this->basisRun instanceof OperationRun
            ? (int) $this->basisRun->getKey()
            : null;
    }

    public function basisRunOutcome(): ?string
    {
        return $this->basisRun instanceof OperationRun
            ? (string) $this->basisRun->outcome
            : null;
    }

    public function basisCompletedAtLabel(): ?string
    {
        if (! $this->basisRun instanceof OperationRun) {
            return null;
        }

        $timestamp = $this->basisRun->completed_at ?? $this->basisRun->started_at ?? $this->basisRun->created_at;

        return $timestamp?->diffForHumans(['short' => true]);
    }

    public function topPriorityFollowUpRow(): ?TenantCoverageTypeTruth
    {
        foreach ($this->rows as $row) {
            if ($row->followUpRequired) {
                return $row;
            }
        }

        return null;
    }

    public function observedTypeCount(): int
    {
        return count(array_filter(
            $this->rows,
            static fn (TenantCoverageTypeTruth $row): bool => $row->observedItemCount > 0,
        ));
    }

    /**
     * @return list<TenantCoverageTypeTruth>
     */
    public function followUpRows(): array
    {
        return array_values(array_filter(
            $this->rows,
            static fn (TenantCoverageTypeTruth $row): bool => $row->followUpRequired,
        ));
    }

    /**
     * @return array{
     *     tenantId: int,
     *     basisRun: array{id: int, outcome: string, completedAt: string|null}|null,
     *     hasCurrentCoverageResult: bool,
     *     summary: array{
     *         supportedTypes: int,
     *         succeededTypes: int,
     *         failedTypes: int,
     *         skippedTypes: int,
     *         unknownTypes: int,
     *         followUpTypes: int,
     *         observedItems: int
     *     },
     *     rows: list<array<string, mixed>>
     * }
     */
    public function toArray(): array
    {
        return [
            'tenantId' => $this->tenantId,
            'basisRun' => $this->basisRun instanceof OperationRun
                ? [
                    'id' => (int) $this->basisRun->getKey(),
                    'outcome' => (string) $this->basisRun->outcome,
                    'completedAt' => $this->basisRun->completed_at?->toIso8601String(),
                ]
                : null,
            'hasCurrentCoverageResult' => $this->hasCurrentCoverageResult,
            'summary' => [
                'supportedTypes' => $this->supportedTypeCount,
                'succeededTypes' => $this->succeededTypeCount,
                'failedTypes' => $this->failedTypeCount,
                'skippedTypes' => $this->skippedTypeCount,
                'unknownTypes' => $this->unknownTypeCount,
                'followUpTypes' => $this->followUpTypeCount,
                'observedItems' => $this->observedItemTotal,
            ],
            'rows' => array_map(
                static fn (TenantCoverageTypeTruth $row): array => $row->toArray(),
                $this->rows,
            ),
        ];
    }
}
