<?php

namespace WPSN\Modules\Privacy;

defined('ABSPATH') || exit;

final class ExternalRequestLogRepository
{
    private function table(): string
    {
        global $wpdb;
        return $wpdb->prefix . 'wpsn_external_log';
    }

    /**
     * @param array{service:string,context?:string,method?:string,url?:string,response_code?:int,error?:string,request_bytes?:int,created_at?:string} $row
     */
    public function insert(array $row): void
    {
        global $wpdb;

        $service = isset($row['service']) ? sanitize_key((string) $row['service']) : '';
        if ($service === '') {
            return;
        }

        $context = isset($row['context']) ? sanitize_key((string) $row['context']) : '';
        if (strlen($context) > 64) {
            $context = substr($context, 0, 64);
        }

        $method = isset($row['method']) ? strtoupper(sanitize_key((string) $row['method'])) : '';
        if ($method === '' || strlen($method) > 10) {
            $method = '';
        }

        $url = isset($row['url']) ? (string) $row['url'] : '';
        $url = $url !== '' ? esc_url_raw($url) : '';

        $code = isset($row['response_code']) ? (int) $row['response_code'] : 0;
        $err = isset($row['error']) ? (string) $row['error'] : '';
        if (strlen($err) > 2000) {
            $err = substr($err, 0, 2000);
        }

        $bytes = isset($row['request_bytes']) ? (int) $row['request_bytes'] : 0;
        if ($bytes < 0) {
            $bytes = 0;
        }

        $created = isset($row['created_at']) ? (string) $row['created_at'] : '';
        if ($created === '') {
            $created = gmdate('Y-m-d H:i:s');
        }

        $wpdb->insert(
            $this->table(),
            [
                'service' => $service,
                'context' => $context,
                'method' => $method,
                'url' => $url,
                'response_code' => $code,
                'error' => $err,
                'request_bytes' => $bytes,
                'created_at' => $created,
            ],
            ['%s','%s','%s','%s','%d','%s','%d','%s']
        );
    }

    /**
     * @param array{service?:string,code?:int} $filters
     * @return array<int,array<string,mixed>>
     */
    public function list(int $limit = 200, int $offset = 0, array $filters = []): array
    {
        global $wpdb;

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

        $where = '1=1';
        $params = [];

        if (!empty($filters['service'])) {
            $where .= ' AND service = %s';
            $params[] = sanitize_key((string) $filters['service']);
        }
        if (isset($filters['code']) && (int) $filters['code'] > 0) {
            $where .= ' AND response_code = %d';
            $params[] = (int) $filters['code'];
        }

        $sql = "SELECT * FROM {$this->table()} WHERE {$where} ORDER BY id DESC LIMIT %d OFFSET %d";
        $params[] = $limit;
        $params[] = $offset;

        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
        $prepared = $wpdb->prepare($sql, $params);
        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
        $rows = $wpdb->get_results($prepared, ARRAY_A);

        return is_array($rows) ? $rows : [];
    }

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

        $where = '1=1';
        $params = [];

        if (!empty($filters['service'])) {
            $where .= ' AND service = %s';
            $params[] = sanitize_key((string) $filters['service']);
        }
        if (isset($filters['code']) && (int) $filters['code'] > 0) {
            $where .= ' AND response_code = %d';
            $params[] = (int) $filters['code'];
        }

        $sql = "SELECT COUNT(*) FROM {$this->table()} WHERE {$where}";

        if (!empty($params)) {
            // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
            $prepared = $wpdb->prepare($sql, $params);
            // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
            $n = $wpdb->get_var($prepared);
        } else {
            // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
            $n = $wpdb->get_var($sql);
        }

        return (int) $n;
    }

    public function deleteAll(): void
    {
        global $wpdb;
        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
        $wpdb->query('TRUNCATE TABLE ' . $this->table());
    }

    public function purgeOlderThan(int $days): int
    {
        global $wpdb;

        $days = max(1, min(3650, $days));
        $cutoff = gmdate('Y-m-d H:i:s', time() - ($days * DAY_IN_SECONDS));

        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
        $sql = $wpdb->prepare(
            'DELETE FROM ' . $this->table() . ' WHERE created_at < %s',
            $cutoff
        );

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

        return (int) $wpdb->rows_affected;
    }
}
