<?php

namespace App\Models;

use App\Support\Baselines\PolicyVersionCapturePurpose;
use App\Support\Concerns\DerivesWorkspaceIdFromTenant;
use App\Support\RedactionIntegrity;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;

class PolicyVersion extends Model
{
    use DerivesWorkspaceIdFromTenant;
    use HasFactory;
    use SoftDeletes;

    protected $guarded = [];

    protected $casts = [
        'snapshot' => 'array',
        'metadata' => 'array',
        'assignments' => 'array',
        'scope_tags' => 'array',
        'secret_fingerprints' => 'array',
        'redaction_version' => 'integer',
        'captured_at' => 'datetime',
        'capture_purpose' => PolicyVersionCapturePurpose::class,
    ];

    public function previous(): ?self
    {
        return $this->policy
            ? $this->policy
                ->versions()
                ->where('version_number', '<', $this->version_number)
                ->orderByDesc('version_number')
                ->first()
            : null;
    }

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

    public function policy(): BelongsTo
    {
        return $this->belongsTo(Policy::class);
    }

    public function operationRun(): BelongsTo
    {
        return $this->belongsTo(OperationRun::class);
    }

    public function baselineProfile(): BelongsTo
    {
        return $this->belongsTo(BaselineProfile::class);
    }

    public function snapshotSource(): ?string
    {
        $source = $this->metadata['source']
            ?? $this->metadata['snapshot_source']
            ?? null;

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

    /**
     * @return list<string>
     */
    public function warningMessages(): array
    {
        $warnings = $this->metadata['warnings'] ?? [];

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

        return collect($warnings)
            ->filter(fn (mixed $warning): bool => is_string($warning) && trim($warning) !== '')
            ->map(fn (string $warning): string => trim($warning))
            ->values()
            ->all();
    }

    public function assignmentsFetchFailed(): bool
    {
        return (bool) ($this->metadata['assignments_fetch_failed'] ?? false);
    }

    public function hasOrphanedAssignments(): bool
    {
        return (bool) ($this->metadata['has_orphaned_assignments'] ?? false);
    }

    public function integrityWarning(): ?string
    {
        return RedactionIntegrity::noteForPolicyVersion($this);
    }

    public function hasCapturedPayload(): bool
    {
        return is_array($this->snapshot) && $this->snapshot !== [];
    }

    public function scopePruneEligible($query, int $days = 90)
    {
        return $query
            ->whereNull('deleted_at')
            ->where('captured_at', '<', now()->subDays($days))
            ->whereRaw(
                'policy_versions.version_number < (select max(pv2.version_number) from policy_versions pv2 where pv2.policy_id = policy_versions.policy_id and pv2.deleted_at is null)'
            );
    }
}
