<?php
/**
 * FirewallRulesTable — standalone class file.
 *
 * @package UltraguardPro\modules\firewall\db
 */

namespace UltraguardPro\modules\firewall\db;

use UltraguardPro\db\BaseTable;
use UltraguardPro\core\DataCodec;
use UltraguardPro\core\DatabaseCrud;

if ( ! defined( 'ABSPATH' ) ) { exit; }

// ─────────────────────────────────────────────────────────────────────────────
class FirewallRulesTable extends BaseTable {

    protected $table_name  = 'ultraguard_firewall_rules';
    protected $cache_group = 'ultraguard_firewall_rules';

    public function get_defaults(): array {
        return [
            'id'            => 0,
            'name'          => '',
            'description'   => null,
            'rule_type'     => 'block',
            'match_field'   => 'ip',
            'match_operator' => 'equals',
            'match_value'   => '',
            'action'        => 'block',
            'priority'      => 100,
            'is_active'     => 1,
            'hits_count'    => 0,
            'last_hit_at'   => null,
            'created_by'    => null,
            'created_at'    => '',
            'updated_at'    => '',
        ];
    }

    protected function validate_data( $data ) {
        if ( empty( $data['name'] ) || empty( $data['match_value'] ) ) {
            return false;
        }
        $valid_actions = [ 'block', 'allow', 'log', 'challenge', 'redirect' ];
        if ( isset( $data['action'] ) && ! in_array( $data['action'], $valid_actions, true ) ) {
            return false;
        }
        return $data;
    }

    protected function sanitize_data( $data ): array {
        $out = [];
        foreach ( $data as $k => $v ) {
            $out[ $k ] = match ( $k ) {
                // match_value stores regex/string patterns — must NOT go through
                // sanitize_textarea_field() which calls wp_strip_all_tags() and
                // destroys patterns like /<script[\s>]/i  →  /]/i. Use the
                // regex-safe sanitizer that only strips null bytes + validates UTF-8.
                'match_value'  => FirewallRulesTable::sanitize_regex_pattern( (string) $v ),
                'description'  => sanitize_textarea_field( (string) $v ),
                'priority', 'hits_count'     => absint( $v ),
                'is_active'                  => (int) (bool) $v,
                'created_by'                 => $v === null ? null : absint( $v ),
                default                      => is_string( $v ) ? sanitize_text_field( $v ) : $v,
            };
        }
        return $out;
    }

    /**
     * Delegates to BaseTable::sanitize_regex_pattern().
     * Kept for backward-compatibility with any external callers.
     *
     * @deprecated Use BaseTable::sanitize_regex_pattern() directly.
     */
    public static function sanitize_regex_pattern( string $v ): string {
        return parent::sanitize_regex_pattern( $v );
    }

    public function get_active_rules( string $action = '' ): array {
        $conds = [ 'is_active' => 1 ];
        if ( $action ) { $conds['action'] = $action; }
        return $this->get_by( $conds, 1000, 'priority', 'ASC' );
    }

    public function increment_hits( int $id ): bool {
        $this->update( $id, [ 'last_hit_at' => current_time( 'mysql', true ) ] );
        return $this->increment_field( 'hits_count', [ 'id' => $id ] );
    }

    // ── DataCodec integration ───────────────────────────────────────────────────

    /**
     * 'match_value' stores regex / string patterns (e.g. /<script[\s>]/i) that
     * would be corrupted by sanitize_text_field() / wp_strip_all_tags() inside
     * DatabaseCrud.  DataCodec Base64-wraps the value at write time and decodes
     * it transparently at read time.
     *
     * @return string[]
     */
    protected function get_codec_fields(): array {
        return [ 'match_value' ];
    }

    protected function get_searchable_fields(): array {
        return [ 'name', 'description', 'match_value', 'rule_type' ];
    }
}

// ─────────────────────────────────────────────────────────────────────────────
