<?php

namespace Kylin\Activity\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Kylin\Activity\Contracts\Activity as ActivityContract;

/**
 * 活动日志模型
 *
 * 存储所有系统操作审计日志，包括模型 CRUD 和用户行为记录。
 *
 * 合规要求：
 *  - 依据《网络安全法》第二十一条，日志保留 >= 180 天
 *  - 依据《个人信息保护法》第二十八条，禁止记录敏感个人信息明文
 *  - 依据等保 2.0 (GB/T 22239)，审计记录不可删除/修改
 *
 * @property int $id
 * @property string|null $log_name           日志分类名称
 * @property string $description             日志描述
 * @property string|null $subject_type       操作主体类型
 * @property int|null $subject_id            操作主体 ID
 * @property string|null $causer_type        操作者类型
 * @property int|null $causer_id             操作者 ID
 * @property string|null $event              触发事件（created/updated/deleted）
 * @property string|null $batch_uuid         批次 UUID
 * @property \Illuminate\Support\Collection|null $properties 属性快照
 * @property int|null $tenant_id             租户 ID（多租户隔离）
 * @property \Carbon\Carbon|null $created_at
 * @property \Carbon\Carbon|null $updated_at
 */
class Activity extends Model implements ActivityContract
{
    /**
     * 禁止批量赋值的字段
     *
     * 依据等保 2.0 (GB/T 22239) 安全审计要求，
     * 审计记录不可被修改，保护关键字段。
     */
    protected $guarded = ['id', 'created_at', 'updated_at'];

    /**
     * 允许批量赋值的字段
     */
    protected $fillable = [
        'log_name',
        'description',
        'subject_type',
        'subject_id',
        'causer_type',
        'causer_id',
        'event',
        'batch_uuid',
        'properties',
        'tenant_id',
    ];

    protected $casts = [
        'properties' => 'collection',
    ];

    /**
     * 模型启动引导
     *
     * 注册防篡改事件：禁止更新和删除审计日志。
     */
    protected static function booted(): void
    {
        if (config('kylin-activity.immutable_logs', true)) {
            // 禁止更新
            static::updating(function (Activity $activity) {
                return false;
            });

            // 禁止删除（通过 CleanActivitylogCommand 除外，使用 forceDelete）
            static::deleting(function (Activity $activity) {
                return false;
            });
        }
    }

    public function __construct(array $attributes = [])
    {
        if (! isset($this->connection)) {
            $this->setConnection(config('kylin-activity.database_connection'));
        }

        if (! isset($this->table)) {
            $this->setTable(config('kylin-activity.table_name'));
        }

        parent::__construct($attributes);
    }

    /**
     * 获取操作主体
     */
    public function subject(): MorphTo
    {
        if (config('kylin-activity.subject_returns_soft_deleted_models')) {
            return $this->morphTo()->withoutGlobalScope(SoftDeletingScope::class);
        }

        return $this->morphTo();
    }

    /**
     * 获取操作者
     */
    public function causer(): MorphTo
    {
        return $this->morphTo();
    }

    /**
     * 获取额外属性
     */
    public function getExtraProperty(string $propertyName, mixed $defaultValue = null): mixed
    {
        return Arr::get($this->properties->toArray(), $propertyName, $defaultValue);
    }

    /**
     * 获取属性变更集合
     */
    public function changes(): Collection
    {
        if (! $this->properties instanceof Collection) {
            return new Collection();
        }

        return $this->properties->only(['attributes', 'old']);
    }

    /**
     * 变更属性访问器
     */
    public function getChangesAttribute(): Collection
    {
        return $this->changes();
    }

    /**
     * 按日志名称筛选
     */
    public function scopeInLog(Builder $query, ...$logNames): Builder
    {
        if (is_array($logNames[0])) {
            $logNames = $logNames[0];
        }

        return $query->whereIn('log_name', $logNames);
    }

    /**
     * 按操作者筛选
     */
    public function scopeCausedBy(Builder $query, Model $causer): Builder
    {
        return $query
            ->where('causer_type', $causer->getMorphClass())
            ->where('causer_id', $causer->getKey());
    }

    /**
     * 按操作主体筛选
     */
    public function scopeForSubject(Builder $query, Model $subject): Builder
    {
        return $query
            ->where('subject_type', $subject->getMorphClass())
            ->where('subject_id', $subject->getKey());
    }

    /**
     * 按事件类型筛选
     */
    public function scopeForEvent(Builder $query, string $event): Builder
    {
        return $query->where('event', $event);
    }

    /**
     * 筛选有批次标记的日志
     */
    public function scopeHasBatch(Builder $query): Builder
    {
        return $query->whereNotNull('batch_uuid');
    }

    /**
     * 按批次 UUID 筛选
     */
    public function scopeForBatch(Builder $query, string $batchUuid): Builder
    {
        return $query->where('batch_uuid', $batchUuid);
    }

    /**
     * 按租户筛选
     *
     * 多租户场景下，自动限定当前租户的日志记录。
     */
    public function scopeForTenant(Builder $query, ?int $tenantId = null): Builder
    {
        if (! config('kylin-activity.multi_tenant.enabled', false)) {
            return $query;
        }

        $field = config('kylin-activity.multi_tenant.tenant_field', 'tenant_id');
        $tenantId = $tenantId ?? $this->resolveTenantId();

        return $query->where($field, $tenantId);
    }

    /**
     * 解析当前租户 ID
     */
    protected function resolveTenantId(): ?int
    {
        $resolver = config('kylin-activity.multi_tenant.tenant_resolver');

        if ($resolver && is_callable($resolver)) {
            return call_user_func($resolver);
        }

        return null;
    }
}
