<?php

declare(strict_types=1);

namespace App\Services\EnvironmentReviews;

use App\Models\EnvironmentReview;
use App\Support\EnvironmentReviewCompletenessState;
use App\Support\EnvironmentReviewStatus;
use Illuminate\Support\Collection;

final class EnvironmentReviewReadinessGate
{
    /**
     * @param  iterable<array<string, mixed>>  $sections
     * @return list<string>
     */
    public function blockersForSections(iterable $sections): array
    {
        $blockers = [];

        foreach ($sections as $section) {
            $required = (bool) ($section['required'] ?? false);
            $state = (string) ($section['completeness_state'] ?? EnvironmentReviewCompletenessState::Missing->value);
            $title = (string) ($section['title'] ?? 'Review section');

            if (! $required) {
                continue;
            }

            if ($state === EnvironmentReviewCompletenessState::Missing->value) {
                $blockers[] = sprintf('%s is missing.', $title);
            }

            if ($state === EnvironmentReviewCompletenessState::Stale->value) {
                $blockers[] = sprintf('%s is stale and must be refreshed before publication.', $title);
            }
        }

        return array_values(array_unique($blockers));
    }

    /**
     * @param  iterable<array<string, mixed>>  $sections
     */
    public function completenessForSections(iterable $sections): EnvironmentReviewCompletenessState
    {
        $states = collect($sections)
            ->map(static fn (array $section): string => (string) ($section['completeness_state'] ?? EnvironmentReviewCompletenessState::Missing->value))
            ->values();

        if ($states->isEmpty()) {
            return EnvironmentReviewCompletenessState::Missing;
        }

        if ($states->contains(EnvironmentReviewCompletenessState::Missing->value)) {
            return EnvironmentReviewCompletenessState::Missing;
        }

        if ($states->contains(EnvironmentReviewCompletenessState::Stale->value)) {
            return EnvironmentReviewCompletenessState::Stale;
        }

        if ($states->contains(EnvironmentReviewCompletenessState::Partial->value)) {
            return EnvironmentReviewCompletenessState::Partial;
        }

        return EnvironmentReviewCompletenessState::Complete;
    }

    /**
     * @param  iterable<array<string, mixed>>  $sections
     */
    public function statusForSections(iterable $sections): EnvironmentReviewStatus
    {
        return $this->blockersForSections($sections) === []
            ? EnvironmentReviewStatus::Ready
            : EnvironmentReviewStatus::Draft;
    }

    /**
     * @return list<string>
     */
    public function blockersForReview(EnvironmentReview $review): array
    {
        $sections = $review->relationLoaded('sections')
            ? $review->sections
            : $review->sections()->get();

        return $this->blockersForSections($sections->map(static function ($section): array {
            return [
                'title' => (string) $section->title,
                'required' => (bool) $section->required,
                'completeness_state' => (string) $section->completeness_state,
            ];
        })->all());
    }

    public function canPublish(EnvironmentReview $review): bool
    {
        if (! $review->isMutable()) {
            return false;
        }

        return $this->blockersForReview($review) === [];
    }

    public function canExport(EnvironmentReview $review): bool
    {
        if (! in_array($review->statusEnum(), [
            EnvironmentReviewStatus::Ready,
            EnvironmentReviewStatus::Published,
        ], true)) {
            return false;
        }

        return $this->blockersForReview($review) === [];
    }

    /**
     * @param  iterable<array<string, mixed>>  $sections
     * @return array<string, int>
     */
    public function sectionStateCounts(iterable $sections): array
    {
        $counts = collect($sections)
            ->groupBy(static fn (array $section): string => (string) ($section['completeness_state'] ?? EnvironmentReviewCompletenessState::Missing->value))
            ->map(static fn (Collection $group): int => $group->count());

        return [
            'complete' => (int) ($counts[EnvironmentReviewCompletenessState::Complete->value] ?? 0),
            'partial' => (int) ($counts[EnvironmentReviewCompletenessState::Partial->value] ?? 0),
            'missing' => (int) ($counts[EnvironmentReviewCompletenessState::Missing->value] ?? 0),
            'stale' => (int) ($counts[EnvironmentReviewCompletenessState::Stale->value] ?? 0),
        ];
    }
}
