<?php

namespace WPSN\Modules\AIReadiness;

defined('ABSPATH') || exit;

final class PolicyLogRepository
{
    private string $table;

    public function __construct()
    {
        global $wpdb;
        $this->table = $wpdb->prefix . 'wpsn_policy_log';
    }

    /**
     * @param array<string,mixed>|null $old
     * @param array<string,mixed>|null $new
     */
    public function log(string $context, string $action, string $message, ?array $old, ?array $new, int $userId = 0): void
    {
        global $wpdb;

        $context = sanitize_key($context);
        $action = sanitize_key($action);

        if ($context === '') {
            $context = 'general';
        }
        if ($action === '') {
            $action = 'updated';
        }

        $now = gmdate('Y-m-d H:i:s');

        $data = [
            'context' => $context,
            'action' => $action,
            'message' => sanitize_text_field($message),
            'user_id' => $userId > 0 ? $userId : null,
            'old_value' => $old ? wp_json_encode($old) : null,
            'new_value' => $new ? wp_json_encode($new) : null,
            'created_at' => $now,
        ];

        // wpdb will treat nulls safely here; we let it infer formats.
        $wpdb->insert($this->table, $data);
    }

    /**
     * @param array{context?:string,action?:string} $filters
     */
    public function count(array $filters = []): int
    {
        global $wpdb;

        [$where, $params] = $this->buildWhere($filters);
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        $sql = "SELECT COUNT(*) FROM {$this->table} WHERE {$where}";

        // Avoid wpdb::prepare() warnings when there are no placeholders.
        if (!empty($params)) {
            // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
            $sql = $wpdb->prepare($sql, $params);
        }

        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
        $count = $wpdb->get_var($sql);

        return (int) $count;
    }

    /**
     * @param array{context?:string,action?:string} $filters
     * @return array<int,array<string,mixed>>
     */
    public function query(array $filters, int $limit, int $offset): array
    {
        global $wpdb;

        $limit = max(1, min(200, (int) $limit));
        $offset = max(0, (int) $offset);

        [$where, $params] = $this->buildWhere($filters);
        $params[] = $limit;
        $params[] = $offset;

        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        $sql = "SELECT id, context, action, message, user_id, old_value, new_value, created_at
                FROM {$this->table}
                WHERE {$where}
                ORDER BY created_at DESC, id DESC
                LIMIT %d OFFSET %d";

        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
        $rows = $wpdb->get_results($wpdb->prepare($sql, $params), ARRAY_A);
        if (!is_array($rows)) {
            return [];
        }

        return $rows;
    }

    /**
     * @return array<int,string>
     */
    public function distinctActions(): array
    {
        global $wpdb;
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        $rows = $wpdb->get_col("SELECT DISTINCT action FROM {$this->table} ORDER BY action ASC");
        if (!is_array($rows)) {
            return [];
        }
        $out = [];
        foreach ($rows as $a) {
            $a = sanitize_key((string) $a);
            if ($a !== '') {
                $out[] = $a;
            }
        }
        return $out;
    }

    /**
     * @param array{context?:string,action?:string} $filters
     * @return array{0:string,1:array<int,mixed>}
     */
    private function buildWhere(array $filters): array
    {
        $where = '1=1';
        $params = [];

        $context = isset($filters['context']) ? sanitize_key((string) $filters['context']) : '';
        if ($context !== '' && $context !== 'all') {
            $where .= ' AND context = %s';
            $params[] = $context;
        }

        $action = isset($filters['action']) ? sanitize_key((string) $filters['action']) : '';
        if ($action !== '' && $action !== 'all') {
            $where .= ' AND action = %s';
            $params[] = $action;
        }

        return [$where, $params];
    }
}
