<?php

declare(strict_types=1);

namespace App\Models;

use App\Support\Concerns\DerivesWorkspaceIdFromTenant;
use App\Support\Governance\Controls\ComplianceEvidenceMappingV1;
use App\Support\EnvironmentReviewCompletenessState;
use App\Support\EnvironmentReviewStatus;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

class EnvironmentReview extends Model
{
    use DerivesWorkspaceIdFromTenant;
    use HasFactory;

    protected $guarded = [];

    /**
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'summary' => 'array',
            'generated_at' => 'datetime',
            'published_at' => 'datetime',
            'archived_at' => 'datetime',
        ];
    }

    /**
     * @return BelongsTo<Workspace, $this>
     */
    public function workspace(): BelongsTo
    {
        return $this->belongsTo(Workspace::class);
    }

    /**
     * @return BelongsTo<ManagedEnvironment, $this>
     */
    public function tenant(): BelongsTo
    {
        return $this->belongsTo(ManagedEnvironment::class, 'managed_environment_id');
    }

    /**
     * @return BelongsTo<EvidenceSnapshot, $this>
     */
    public function evidenceSnapshot(): BelongsTo
    {
        return $this->belongsTo(EvidenceSnapshot::class);
    }

    /**
     * @return BelongsTo<OperationRun, $this>
     */
    public function operationRun(): BelongsTo
    {
        return $this->belongsTo(OperationRun::class);
    }

    /**
     * @return BelongsTo<User, $this>
     */
    public function initiator(): BelongsTo
    {
        return $this->belongsTo(User::class, 'initiated_by_user_id');
    }

    /**
     * @return BelongsTo<User, $this>
     */
    public function publisher(): BelongsTo
    {
        return $this->belongsTo(User::class, 'published_by_user_id');
    }

    /**
     * @return BelongsTo<ReviewPack, $this>
     */
    public function currentExportReviewPack(): BelongsTo
    {
        return $this->belongsTo(ReviewPack::class, 'current_export_review_pack_id');
    }

    /**
     * @return BelongsTo<self, $this>
     */
    public function supersededByReview(): BelongsTo
    {
        return $this->belongsTo(self::class, 'superseded_by_review_id');
    }

    /**
     * @return HasMany<self, $this>
     */
    public function supersededReviews(): HasMany
    {
        return $this->hasMany(self::class, 'superseded_by_review_id');
    }

    /**
     * @return HasMany<EnvironmentReviewSection, $this>
     */
    public function sections(): HasMany
    {
        return $this->hasMany(EnvironmentReviewSection::class)->orderBy('sort_order')->orderBy('id');
    }

    /**
     * @return HasMany<ReviewPack, $this>
     */
    public function reviewPacks(): HasMany
    {
        return $this->hasMany(ReviewPack::class)->latest('generated_at');
    }

    /**
     * @param  Builder<self>  $query
     * @return Builder<self>
     */
    public function scopeForTenant(Builder $query, int $tenantId): Builder
    {
        return $query->where('managed_environment_id', $tenantId);
    }

    /**
     * @param  Builder<self>  $query
     * @return Builder<self>
     */
    public function scopeForWorkspace(Builder $query, int $workspaceId): Builder
    {
        return $query->where('workspace_id', $workspaceId);
    }

    /**
     * @param  Builder<self>  $query
     * @return Builder<self>
     */
    public function scopePublished(Builder $query): Builder
    {
        return $query->where('status', EnvironmentReviewStatus::Published->value);
    }

    /**
     * @param  Builder<self>  $query
     * @return Builder<self>
     */
    public function scopeMutable(Builder $query): Builder
    {
        return $query->whereIn('status', [
            EnvironmentReviewStatus::Draft->value,
            EnvironmentReviewStatus::Ready->value,
            EnvironmentReviewStatus::Failed->value,
        ]);
    }

    public function statusEnum(): EnvironmentReviewStatus
    {
        return EnvironmentReviewStatus::from((string) $this->status);
    }

    public function completenessEnum(): EnvironmentReviewCompletenessState
    {
        return EnvironmentReviewCompletenessState::tryFrom((string) $this->completeness_state)
            ?? EnvironmentReviewCompletenessState::Missing;
    }

    public function isPublished(): bool
    {
        return $this->statusEnum()->isPublished();
    }

    public function isMutable(): bool
    {
        return $this->statusEnum()->isMutable();
    }

    /**
     * @return list<string>
     */
    public function publishBlockers(): array
    {
        $summary = is_array($this->summary) ? $this->summary : [];
        $blockers = $summary['publish_blockers'] ?? [];

        return is_array($blockers) ? array_values(array_map('strval', $blockers)) : [];
    }

    /**
     * @return list<array<string, mixed>>
     */
    public function canonicalControlReferences(): array
    {
        $summary = is_array($this->summary) ? $this->summary : [];
        $references = $summary['canonical_controls'] ?? [];

        return is_array($references)
            ? array_values(array_filter($references, static fn (mixed $reference): bool => is_array($reference)))
            : [];
    }

    /**
     * @return array<string, mixed>
     */
    public function controlInterpretation(): array
    {
        $summary = is_array($this->summary) ? $this->summary : [];
        $interpretation = $summary['control_interpretation'] ?? [];

        return is_array($interpretation) ? $interpretation : [];
    }

    public function controlInterpretationVersion(): ?string
    {
        $version = $this->controlInterpretation()['version_key'] ?? null;

        return is_string($version) && trim($version) !== '' ? $version : null;
    }

    /**
     * @return list<array<string, mixed>>
     */
    public function controlInterpretationControls(): array
    {
        $controls = $this->controlInterpretation()['controls'] ?? [];

        return is_array($controls)
            ? array_values(array_filter($controls, static fn (mixed $control): bool => is_array($control)))
            : [];
    }

    /**
     * @return array<string, int>
     */
    public function controlInterpretationLimitationCounts(): array
    {
        $counts = $this->controlInterpretation()['limitation_counts'] ?? [];

        if (! is_array($counts)) {
            return [];
        }

        return collect($counts)
            ->mapWithKeys(static fn (mixed $count, string|int $key): array => [(string) $key => (int) $count])
            ->all();
    }

    public function controlInterpretationSection(): ?EnvironmentReviewSection
    {
        if ($this->relationLoaded('sections')) {
            $section = $this->sections->firstWhere('section_key', ComplianceEvidenceMappingV1::SECTION_KEY);

            return $section instanceof EnvironmentReviewSection ? $section : null;
        }

        return $this->sections()
            ->where('section_key', ComplianceEvidenceMappingV1::SECTION_KEY)
            ->first();
    }
}
