<?php

namespace App\Domain\Shared\Traits;

use App\Domain\Audit\Models\AuditLog;
use Illuminate\Support\Facades\Log;

trait Auditable
{
    /**
     * Attributes excluded from audit logging for security reasons.
     */
    protected static array $auditExcluded = [
        'password',
        'remember_token',
        'api_key',
        'secret',
    ];

    public static function bootAuditable(): void
    {
        static::created(function ($model) {
            static::createAuditLog($model, 'created', null, $model->getAuditableAttributes());
        });

        static::updated(function ($model) {
            $dirty = $model->getDirty();
            if (empty($dirty)) {
                return;
            }

            $oldValues = [];
            $newValues = [];

            foreach ($dirty as $key => $value) {
                if (in_array($key, static::$auditExcluded, true)) {
                    continue;
                }
                if ($key === 'updated_at') {
                    continue;
                }

                $oldValues[$key] = $model->getOriginal($key);
                $newValues[$key] = $value;
            }

            if (! empty($newValues)) {
                static::createAuditLog($model, 'updated', $oldValues, $newValues);
            }
        });

        static::deleted(function ($model) {
            static::createAuditLog($model, 'deleted', $model->getAuditableAttributes(), null);
        });

        // Only register restored if the model uses SoftDeletes
        if (in_array(\Illuminate\Database\Eloquent\SoftDeletes::class, class_uses_recursive(static::class))) {
            static::restored(function ($model) {
                static::createAuditLog($model, 'restored', null, $model->getAuditableAttributes());
            });
        }
    }

    /**
     * Create an immutable audit log entry.
     */
    protected static function createAuditLog($model, string $action, ?array $oldValues, ?array $newValues): void
    {
        try {
            $userId = auth()->id();
            $academyId = $model->academy_id
                ?? ($userId ? (auth()->user()->academy_id ?? null) : null);

            // Cannot log without academy context
            if (! $academyId) {
                return;
            }

            AuditLog::create([
                'academy_id' => $academyId,
                'user_id' => $userId,
                'auditable_type' => $model->getMorphClass(),
                'auditable_id' => $model->getKey(),
                'action' => $action,
                'old_values' => $oldValues,
                'new_values' => $newValues,
                'ip_address' => request()?->ip(),
                'user_agent' => request()?->userAgent(),
                'url' => request()?->fullUrl(),
                'is_financial' => $model->isFinancialAudit(),
            ]);
        } catch (\Throwable $e) {
            // Never let audit logging crash the main operation
            Log::error('Audit log failed: ' . $e->getMessage(), [
                'model' => get_class($model),
                'id' => $model->getKey(),
                'action' => $action,
            ]);
        }
    }

    /**
     * Get model attributes filtered to exclude sensitive fields.
     */
    public function getAuditableAttributes(): array
    {
        $attributes = $this->attributesToArray();

        return array_diff_key($attributes, array_flip(static::$auditExcluded));
    }

    /**
     * Whether this model's audit logs are financial (retained forever).
     * Override in financial models (Invoice, Payment, Transaction, Wallet).
     */
    public function isFinancialAudit(): bool
    {
        return false;
    }

    /**
     * Get all audit log entries for this model instance.
     */
    public function auditLogs()
    {
        return AuditLog::where('auditable_type', $this->getMorphClass())
            ->where('auditable_id', $this->getKey())
            ->orderByDesc('created_at');
    }
}
