<?php
/**
 * Request Model - Represents single HTTP request for live traffic
 *
 * @package VMPFence
 * @subpackage Tools
 * @since 2.1.0
 */

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Class VMPFence_Request_Model
 *
 * Represents a single HTTP request to be logged in vmpfence_hits table
 *
 * @since 2.1.0
 */
class VMPFence_Request_Model {

    /**
     * Record ID (primary key)
     * @var int|null
     */
    private $id = null;

    /**
     * Attack log time (microtime)
     * @var float
     */
    private $attackLogTime;

    /**
     * Creation time (microtime)
     * @var float
     */
    private $ctime;

    /**
     * IP address (binary format)
     * @var string
     */
    private $IP;

    /**
     * JavaScript run flag (1=human, 0=bot)
     * @var int
     */
    private $jsRun = 0;

    /**
     * HTTP status code
     * @var int
     */
    private $statusCode = 200;

    /**
     * Is verified Google crawler
     * @var int
     */
    private $isGoogle = 0;

    /**
     * User ID (0 for guests)
     * @var int
     */
    private $userID = 0;

    /**
     * New visit flag
     * @var int
     */
    private $newVisit = 0;

    /**
     * Request URL
     * @var string
     */
    private $URL = '';

    /**
     * HTTP Referer
     * @var string
     */
    private $referer = '';

    /**
     * User Agent
     * @var string
     */
    private $UA = '';

    /**
     * Action type (e.g., 'blocked:waf', 'rate_limit', etc.)
     * @var string
     */
    private $action = '';

    /**
     * Action description
     * @var string
     */
    private $actionDescription = '';

    /**
     * Action data (JSON)
     * @var string
     */
    private $actionData = '';

    /**
     * Constructor
     *
     * @since 2.1.0
     */
    public function __construct() {
        // Initialize with current time
        $this->ctime = microtime(true);
        $this->attackLogTime = $this->ctime;
    }

    /**
     * Save record to database
     *
     * @since 2.1.0
     * @return int|false Record ID on success, false on failure
     */
    public function save() {
        global $wpdb;
        $table_name = $wpdb->prefix . 'vmpfence_hits';

        // Validate required fields
        if (empty($this->IP)) {
            return false;
        }

        // Prepare data
        $data = array(
            'attackLogTime' => $this->attackLogTime,
            'ctime' => $this->ctime,
            'IP' => $this->IP,
            'jsRun' => $this->jsRun,
            'statusCode' => $this->statusCode,
            'isGoogle' => $this->isGoogle,
            'userID' => $this->userID,
            'newVisit' => $this->newVisit,
            'URL' => $this->URL,
            'referer' => $this->referer,
            'UA' => $this->UA,
            'action' => $this->action,
            'actionDescription' => $this->actionDescription,
            'actionData' => $this->actionData,
        );

        // Insert record
        $result = $wpdb->insert($table_name, $data, array(
            '%f', // attackLogTime
            '%f', // ctime
            '%s', // IP (binary)
            '%d', // jsRun
            '%d', // statusCode
            '%d', // isGoogle
            '%d', // userID
            '%d', // newVisit
            '%s', // URL
            '%s', // referer
            '%s', // UA
            '%s', // action
            '%s', // actionDescription
            '%s', // actionData
        ));

        if ($result) {
            $this->id = $wpdb->insert_id;
            return $this->id;
        }

        return false;
    }

    /**
     * Get primary key (record ID)
     *
     * @since 2.1.0
     * @return int|null
     */
    public function getPrimaryKey() {
        return $this->id;
    }

    /**
     * Convert IP address to binary format
     *
     * @since 2.1.0
     * @param string $ip IP address in string format
     * @return string Binary IP address
     */
    public static function ipToBinary($ip) {
        return @inet_pton($ip);
    }

    /**
     * Convert binary IP to string format
     *
     * @since 2.1.0
     * @param string $binary Binary IP address
     * @return string IP address in string format
     */
    public static function binaryToIP($binary) {
        return @inet_ntop($binary);
    }

    // Getters and Setters

    /**
     * Set attack log time
     *
     * @since 2.1.0
     * @param float $time Microtime
     */
    public function setAttackLogTime($time) {
        $this->attackLogTime = $time;
    }

    /**
     * Get attack log time
     *
     * @since 2.1.0
     * @return float
     */
    public function getAttackLogTime() {
        return $this->attackLogTime;
    }

    /**
     * Set creation time
     *
     * @since 2.1.0
     * @param float $time Microtime
     */
    public function setCtime($time) {
        $this->ctime = $time;
    }

    /**
     * Get creation time
     *
     * @since 2.1.0
     * @return float
     */
    public function getCtime() {
        return $this->ctime;
    }

    /**
     * Set IP address (converts to binary)
     *
     * @since 2.1.0
     * @param string $ip IP address in string format
     */
    public function setIP($ip) {
        $this->IP = self::ipToBinary($ip);
    }

    /**
     * Get IP address (returns binary)
     *
     * @since 2.1.0
     * @return string Binary IP
     */
    public function getIP() {
        return $this->IP;
    }

    /**
     * Get IP address as string
     *
     * @since 2.1.0
     * @return string IP address
     */
    public function getIPString() {
        return self::binaryToIP($this->IP);
    }

    /**
     * Set jsRun flag
     *
     * @since 2.1.0
     * @param int $jsRun 1=human, 0=bot
     */
    public function setJsRun($jsRun) {
        $this->jsRun = (int) $jsRun;
    }

    /**
     * Get jsRun flag
     *
     * @since 2.1.0
     * @return int
     */
    public function getJsRun() {
        return $this->jsRun;
    }

    /**
     * Set status code
     *
     * @since 2.1.0
     * @param int $code HTTP status code
     */
    public function setStatusCode($code) {
        $this->statusCode = (int) $code;
    }

    /**
     * Get status code
     *
     * @since 2.1.0
     * @return int
     */
    public function getStatusCode() {
        return $this->statusCode;
    }

    /**
     * Set isGoogle flag
     *
     * @since 2.1.0
     * @param int $isGoogle 1=verified Google, 0=not Google
     */
    public function setIsGoogle($isGoogle) {
        $this->isGoogle = (int) $isGoogle;
    }

    /**
     * Get isGoogle flag
     *
     * @since 2.1.0
     * @return int
     */
    public function getIsGoogle() {
        return $this->isGoogle;
    }

    /**
     * Set user ID
     *
     * @since 2.1.0
     * @param int $userID WordPress user ID
     */
    public function setUserID($userID) {
        $this->userID = (int) $userID;
    }

    /**
     * Get user ID
     *
     * @since 2.1.0
     * @return int
     */
    public function getUserID() {
        return $this->userID;
    }

    /**
     * Set new visit flag
     *
     * @since 2.1.0
     * @param int $newVisit 1=new visit, 0=returning
     */
    public function setNewVisit($newVisit) {
        $this->newVisit = (int) $newVisit;
    }

    /**
     * Get new visit flag
     *
     * @since 2.1.0
     * @return int
     */
    public function getNewVisit() {
        return $this->newVisit;
    }

    /**
     * Set URL
     *
     * @since 2.1.0
     * @param string $url Request URL
     */
    public function setURL($url) {
        $this->URL = $url;
    }

    /**
     * Get URL
     *
     * @since 2.1.0
     * @return string
     */
    public function getURL() {
        return $this->URL;
    }

    /**
     * Set referer
     *
     * @since 2.1.0
     * @param string $referer HTTP referer
     */
    public function setReferer($referer) {
        $this->referer = $referer;
    }

    /**
     * Get referer
     *
     * @since 2.1.0
     * @return string
     */
    public function getReferer() {
        return $this->referer;
    }

    /**
     * Set user agent
     *
     * @since 2.1.0
     * @param string $ua User agent string
     */
    public function setUA($ua) {
        $this->UA = $ua;
    }

    /**
     * Get user agent
     *
     * @since 2.1.0
     * @return string
     */
    public function getUA() {
        return $this->UA;
    }

    /**
     * Set action
     *
     * @since 2.1.0
     * @param string $action Action type
     * @param string $description Action description (optional)
     * @param mixed $data Action data (will be JSON encoded)
     */
    public function setAction($action, $description = '', $data = null) {
        $this->action = $action;
        $this->actionDescription = $description;
        
        if ($data !== null) {
            $this->actionData = is_string($data) ? $data : json_encode($data);
        }
    }

    /**
     * Get action
     *
     * @since 2.1.0
     * @return string
     */
    public function getAction() {
        return $this->action;
    }

    /**
     * Get action description
     *
     * @since 2.1.0
     * @return string
     */
    public function getActionDescription() {
        return $this->actionDescription;
    }

    /**
     * Get action data
     *
     * @since 2.1.0
     * @param bool $decode Whether to decode JSON
     * @return string|array|null
     */
    public function getActionData($decode = false) {
        if ($decode && !empty($this->actionData)) {
            return json_decode($this->actionData, true);
        }
        return $this->actionData;
    }

    /**
     * Update request data (for updating fields before save)
     *
     * @since 2.1.0
     * @param array $data Associative array of field => value
     */
    public function updateRequestData($data) {
        foreach ($data as $field => $value) {
            $method = 'set' . ucfirst($field);
            if (method_exists($this, $method)) {
                $this->$method($value);
            }
        }
    }
}
