<?php

namespace App\Domains\Logging\Models\Concerns;

use App\Domains\Logging\Enums\ValueChangeActorType;
use App\Domains\Logging\Models\ValueChange;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Support\Facades\Auth;
use LogicException;

trait LogsValueChanges
{
    abstract protected function valueChangeTenantCode(): string;

    public static function bootLogsValueChanges(): void
    {
        static::updated(function (Model $model): void {
            $changedAttributes = array_values(array_intersect(
                $model->getLoggedAttributes(),
                array_keys($model->getChanges()),
            ));

            if ($changedAttributes === []) {
                return;
            }

            $userId = Auth::id();
            $actorType = $userId === null
                ? ValueChangeActorType::System
                : ValueChangeActorType::User;

            foreach ($changedAttributes as $attribute) {
                $model->valueChanges()->create([
                    'tenant_code' => $model->valueChangeTenantCode(),
                    'attribute' => $attribute,
                    'old_value' => $model->getRawOriginal($attribute),
                    'new_value' => $model->getAttributes()[$attribute] ?? null,
                    'changed_at' => now(),
                    'actor_type' => $actorType,
                    'user_id' => $userId,
                ]);
            }
        });
    }

    /** @return array<int, string> */
    public function getLoggedAttributes(): array
    {
        if (! property_exists($this, 'loggedAttributes')) {
            throw new LogicException(sprintf(
                'The [%s] model must define a $loggedAttributes property.',
                static::class,
            ));
        }

        return array_values(array_unique($this->loggedAttributes));
    }

    /** @return MorphMany<ValueChange, $this> */
    public function valueChanges(): MorphMany
    {
        return $this->morphMany(ValueChange::class, 'trackable');
    }
}
