<?php

namespace App\Models;

use App\Support\Concerns\DerivesWorkspaceIdFromTenant;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Finding extends Model
{
    /** @use HasFactory<\Database\Factories\FindingFactory> */
    use DerivesWorkspaceIdFromTenant;

    use HasFactory;

    public const string FINDING_TYPE_DRIFT = 'drift';

    public const string FINDING_TYPE_PERMISSION_POSTURE = 'permission_posture';

    public const string FINDING_TYPE_ENTRA_ADMIN_ROLES = 'entra_admin_roles';

    public const string SEVERITY_LOW = 'low';

    public const string SEVERITY_MEDIUM = 'medium';

    public const string SEVERITY_HIGH = 'high';

    public const string SEVERITY_CRITICAL = 'critical';

    public const string STATUS_NEW = 'new';

    public const string STATUS_ACKNOWLEDGED = 'acknowledged';

    public const string STATUS_TRIAGED = 'triaged';

    public const string STATUS_IN_PROGRESS = 'in_progress';

    public const string STATUS_REOPENED = 'reopened';

    public const string STATUS_RESOLVED = 'resolved';

    public const string STATUS_CLOSED = 'closed';

    public const string STATUS_RISK_ACCEPTED = 'risk_accepted';

    protected $guarded = [];

    protected $casts = [
        'acknowledged_at' => 'datetime',
        'closed_at' => 'datetime',
        'due_at' => 'datetime',
        'evidence_jsonb' => 'array',
        'first_seen_at' => 'datetime',
        'in_progress_at' => 'datetime',
        'last_seen_at' => 'datetime',
        'reopened_at' => 'datetime',
        'resolved_at' => 'datetime',
        'sla_days' => 'integer',
        'times_seen' => 'integer',
        'triaged_at' => 'datetime',
    ];

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

    public function baselineRun(): BelongsTo
    {
        return $this->belongsTo(OperationRun::class, 'baseline_operation_run_id');
    }

    public function currentRun(): BelongsTo
    {
        return $this->belongsTo(OperationRun::class, 'current_operation_run_id');
    }

    public function acknowledgedByUser(): BelongsTo
    {
        return $this->belongsTo(User::class, 'acknowledged_by_user_id');
    }

    public function ownerUser(): BelongsTo
    {
        return $this->belongsTo(User::class, 'owner_user_id');
    }

    public function assigneeUser(): BelongsTo
    {
        return $this->belongsTo(User::class, 'assignee_user_id');
    }

    public function closedByUser(): BelongsTo
    {
        return $this->belongsTo(User::class, 'closed_by_user_id');
    }

    /**
     * @return array<int, string>
     */
    public static function openStatuses(): array
    {
        return [
            self::STATUS_NEW,
            self::STATUS_TRIAGED,
            self::STATUS_IN_PROGRESS,
            self::STATUS_REOPENED,
        ];
    }

    /**
     * @return array<int, string>
     */
    public static function terminalStatuses(): array
    {
        return [
            self::STATUS_RESOLVED,
            self::STATUS_CLOSED,
            self::STATUS_RISK_ACCEPTED,
        ];
    }

    /**
     * @return array<int, string>
     */
    public static function openStatusesForQuery(): array
    {
        return [
            ...self::openStatuses(),
            self::STATUS_ACKNOWLEDGED,
        ];
    }

    public static function canonicalizeStatus(?string $status): ?string
    {
        if ($status === self::STATUS_ACKNOWLEDGED) {
            return self::STATUS_TRIAGED;
        }

        return $status;
    }

    public static function isOpenStatus(?string $status): bool
    {
        return is_string($status) && in_array($status, self::openStatusesForQuery(), true);
    }

    public static function isTerminalStatus(?string $status): bool
    {
        $canonical = self::canonicalizeStatus($status);

        return is_string($canonical) && in_array($canonical, self::terminalStatuses(), true);
    }

    public function hasOpenStatus(): bool
    {
        return self::isOpenStatus($this->status);
    }

    public function acknowledge(User $user): void
    {
        if ($this->status === self::STATUS_ACKNOWLEDGED) {
            return;
        }

        $this->forceFill([
            'status' => self::STATUS_ACKNOWLEDGED,
            'acknowledged_at' => now(),
            'acknowledged_by_user_id' => $user->getKey(),
        ]);

        $this->save();
    }

    /**
     * Auto-resolve the finding.
     */
    public function resolve(string $reason): void
    {
        $this->status = self::STATUS_RESOLVED;
        $this->resolved_at = now();
        $this->resolved_reason = $reason;
        $this->save();
    }

    /**
     * Re-open a resolved finding.
     */
    public function reopen(array $evidence): void
    {
        $this->status = self::STATUS_NEW;
        $this->resolved_at = null;
        $this->resolved_reason = null;
        $this->evidence_jsonb = $evidence;
        $this->save();
    }
}
