<?php

declare(strict_types=1);

namespace App\Services\TenantReviews;

use App\Models\TenantReview;
use App\Support\TenantReviewCompletenessState;
use App\Support\TenantReviewStatus;
use Illuminate\Support\Collection;

final class TenantReviewReadinessGate
{
    /**
     * @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'] ?? TenantReviewCompletenessState::Missing->value);
            $title = (string) ($section['title'] ?? 'Review section');

            if (! $required) {
                continue;
            }

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

            if ($state === TenantReviewCompletenessState::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): TenantReviewCompletenessState
    {
        $states = collect($sections)
            ->map(static fn (array $section): string => (string) ($section['completeness_state'] ?? TenantReviewCompletenessState::Missing->value))
            ->values();

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

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

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

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

        return TenantReviewCompletenessState::Complete;
    }

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

    /**
     * @return list<string>
     */
    public function blockersForReview(TenantReview $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(TenantReview $review): bool
    {
        if (! $review->isMutable()) {
            return false;
        }

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

    public function canExport(TenantReview $review): bool
    {
        if (! in_array($review->statusEnum(), [
            TenantReviewStatus::Ready,
            TenantReviewStatus::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'] ?? TenantReviewCompletenessState::Missing->value))
            ->map(static fn (Collection $group): int => $group->count());

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