<?php

namespace App\Domain\Audit\Models;

use App\Domain\Audit\Enums\ActivityCategory;
use App\Domain\Identity\Models\User;
use App\Domain\Organization\Models\Branch;
use App\Domain\Organization\Models\Company;
use App\Support\Concerns\HasUuid;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use RuntimeException;

/**
 * An entry in the activity log. Append-only: the record of what happened is
 * not something the application gets to revise.
 */
class Activity extends Model
{
    use HasUuid;

    public const UPDATED_AT = null;

    protected $guarded = ['id', 'uuid'];

    protected function casts(): array
    {
        return [
            'event_category' => ActivityCategory::class,
            'old_values' => 'array',
            'new_values' => 'array',
            'metadata' => 'array',
            'is_system' => 'boolean',
            'created_at' => 'datetime',
        ];
    }

    protected static function booted(): void
    {
        // Append-only (§65). Editing or deleting history is refused in code, not
        // merely discouraged by convention.
        static::updating(fn () => throw new RuntimeException('Activity records cannot be modified.'));
        static::deleting(fn () => throw new RuntimeException('Activity records cannot be deleted.'));
    }

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

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

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

    public function subject(): MorphTo
    {
        return $this->morphTo();
    }

    public function parent(): MorphTo
    {
        return $this->morphTo();
    }

    /** Everything on one record's timeline, whether logged against it or a child. */
    public function scopeForRecord(Builder $query, Model $record): Builder
    {
        $type = $record->getMorphClass();
        $id = $record->getKey();

        return $query->where(fn (Builder $inner) => $inner
            ->where(fn (Builder $q) => $q->where('subject_type', $type)->where('subject_id', $id))
            ->orWhere(fn (Builder $q) => $q->where('parent_type', $type)->where('parent_id', $id)));
    }

    public function scopeVisibleToOutsiders(Builder $query): Builder
    {
        return $query->where('visibility', 'shared');
    }

    public function actorName(): string
    {
        if ($this->is_system) {
            return 'System';
        }

        return $this->actor_name_snapshot ?? 'Unknown user';
    }
}
