<?php
declare(strict_types=1);

namespace App\Models;

use App\Core\Auth;
use PDO;
use Throwable;

class AdminLog extends BaseModel
{
    protected string $table = 'admin_logs';

    public function write(string $action, ?string $targetType = null, ?int $targetId = null, array $details = []): void
    {
        try {
            $statement = $this->db()->prepare(
                'INSERT INTO admin_logs
                    (admin_id, action, target_type, target_id, entity_type, entity_id, ip_address, user_agent, details, created_at)
                 VALUES
                    (:admin_id, :action, :target_type, :target_id, :entity_type, :entity_id, :ip_address, :user_agent, :details, NOW())'
            );
            $statement->execute([
                'admin_id' => Auth::id(),
                'action' => substr($action, 0, 120),
                'target_type' => $targetType,
                'target_id' => $targetId,
                'entity_type' => $targetType,
                'entity_id' => $targetId,
                'ip_address' => substr((string) ($_SERVER['REMOTE_ADDR'] ?? ''), 0, 64),
                'user_agent' => substr((string) ($_SERVER['HTTP_USER_AGENT'] ?? ''), 0, 255),
                'details' => json_encode($this->redact($details), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
            ]);
        } catch (Throwable) {
            // Admin logging must never break the admin action itself.
        }
    }

    public function listForAdmin(array $filters = [], int $limit = 120): array
    {
        $limit = min(max($limit, 1), 300);
        $where = [];
        $params = [];

        if (!empty($filters['q'])) {
            $where[] = '(al.action LIKE :q_action OR al.target_type LIKE :q_target OR al.details LIKE :q_details OR u.username LIKE :q_user OR u.email LIKE :q_email)';
            $like = '%' . trim((string) $filters['q']) . '%';
            $params = [
                'q_action' => $like,
                'q_target' => $like,
                'q_details' => $like,
                'q_user' => $like,
                'q_email' => $like,
            ];
        }

        if (!empty($filters['action'])) {
            $where[] = 'al.action = :action';
            $params['action'] = (string) $filters['action'];
        }

        if (!empty($filters['target_type'])) {
            $where[] = 'al.target_type = :target_type';
            $params['target_type'] = (string) $filters['target_type'];
        }

        $sql = 'SELECT al.*, u.username AS admin_username, u.email AS admin_email
                FROM admin_logs al
                LEFT JOIN users u ON u.id = al.admin_id';
        if ($where !== []) {
            $sql .= ' WHERE ' . implode(' AND ', $where);
        }
        $sql .= ' ORDER BY al.created_at DESC, al.id DESC LIMIT :limit';

        $statement = $this->db()->prepare($sql);
        foreach ($params as $key => $value) {
            $statement->bindValue($key, $value);
        }
        $statement->bindValue('limit', $limit, PDO::PARAM_INT);
        $statement->execute();

        return $statement->fetchAll();
    }

    private function redact(array $details): array
    {
        foreach ($details as $key => $value) {
            $lower = strtolower((string) $key);
            if (preg_match('/password|secret|token|card|cvv|iban|api_key|merchant_key/', $lower) === 1) {
                $details[$key] = '[redacted]';
                continue;
            }

            if (is_array($value)) {
                $details[$key] = $this->redact($value);
            }
        }

        return $details;
    }
}
