<?php
namespace PSSG_Sync_Sheet\App\Service;

/**
 * Request Log Service Class
 * 
 * This class handles all request logging functionality
 * including database table creation, logging requests,
 * and managing log entries.
 * 
 * @author CodeAstrology Team
 * @package PSSG_Sync_Sheet
 * @since 1.1.4
 */
class Request_Log
{
    use Standalone;

    /**
     * Table name without prefix
     */
    const TABLE_NAME = 'pssg_request_logs';

    /**
     * Option key for database version
     */
    const DB_VERSION_KEY = 'pssg_request_log_db_version';

    /**
     * Current database version
     */
    const DB_VERSION = '1.1.0';

    /**
     * Access Key option name
     */
    const ACCESS_KEY_OPTION = 'pssg_api_access_key';

    /**
     * Direction constants
     */
    const DIRECTION_INCOMING  = 'incoming';
    const DIRECTION_OUTGOING  = 'outgoing';
    const DIRECTION_SCHEDULED = 'scheduled';

    /**
     * Get full table name with prefix
     *
     * @return string
     */
    public static function get_table_name()
    {
        global $wpdb;
        return $wpdb->prefix . self::TABLE_NAME;
    }

    /**
     * Create or update database table
     *
     * @return void
     */
    public static function create_table()
    {
        global $wpdb;
        
        $table_name = self::get_table_name();
        $charset_collate = $wpdb->get_charset_collate();

        $sql = "CREATE TABLE $table_name (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            request_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
            direction varchar(20) NOT NULL DEFAULT 'incoming',
            endpoint varchar(255) NOT NULL,
            method varchar(10) NOT NULL DEFAULT 'POST',
            request_data longtext,
            response_data longtext,
            status varchar(50) NOT NULL DEFAULT 'pending',
            status_code int(11) DEFAULT NULL,
            ip_address varchar(100) DEFAULT NULL,
            user_agent text,
            access_key_valid tinyint(1) DEFAULT 0,
            error_message text,
            product_id bigint(20) unsigned DEFAULT NULL,
            created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY  (id),
            KEY request_time (request_time),
            KEY status (status),
            KEY endpoint (endpoint),
            KEY product_id (product_id),
            KEY direction (direction)
        ) $charset_collate;";

        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta($sql);

        update_option(self::DB_VERSION_KEY, self::DB_VERSION);
    }

    /**
     * Check if table needs to be created/updated
     *
     * @return void
     */
    public static function maybe_create_table()
    {
        $installed_version = get_option(self::DB_VERSION_KEY);
        
        if ($installed_version !== self::DB_VERSION) {
            self::create_table();
        }
    }

    /**
     * Log a request
     *
     * @param array $data Log data
     * @return int|false The row ID on success, false on failure
     */
    public static function log_request($data)
    {
        global $wpdb;
        
        self::maybe_create_table();

        $defaults = [
            'request_time' => current_time('mysql'),
            'direction' => self::DIRECTION_INCOMING,
            'endpoint' => '',
            'method' => 'POST',
            'request_data' => '',
            'response_data' => '',
            'status' => 'pending',
            'status_code' => null,
            'ip_address' => self::get_client_ip(),
            'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? substr(sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])), 0, 255) : '',
            'access_key_valid' => 0,
            'error_message' => '',
            'product_id' => null,
            'created_at' => current_time('mysql'),
        ];

        $data = wp_parse_args($data, $defaults);

        // Serialize arrays/objects
        if (is_array($data['request_data']) || is_object($data['request_data'])) {
            $data['request_data'] = wp_json_encode($data['request_data']);
        }
        if (is_array($data['response_data']) || is_object($data['response_data'])) {
            $data['response_data'] = wp_json_encode($data['response_data']);
        }

        $result = $wpdb->insert(
            self::get_table_name(),
            $data,
            [
                '%s', // request_time
                '%s', // direction
                '%s', // endpoint
                '%s', // method
                '%s', // request_data
                '%s', // response_data
                '%s', // status
                '%d', // status_code
                '%s', // ip_address
                '%s', // user_agent
                '%d', // access_key_valid
                '%s', // error_message
                '%d', // product_id
                '%s', // created_at
            ]
        );

        if ($result === false) {
            return false;
        }

        return $wpdb->insert_id;
    }

    /**
     * Update a log entry
     *
     * @param int $id Log entry ID
     * @param array $data Data to update
     * @return bool
     */
    public static function update_log($id, $data)
    {
        global $wpdb;

        // Serialize arrays/objects
        if (isset($data['request_data']) && (is_array($data['request_data']) || is_object($data['request_data']))) {
            $data['request_data'] = wp_json_encode($data['request_data']);
        }
        if (isset($data['response_data']) && (is_array($data['response_data']) || is_object($data['response_data']))) {
            $data['response_data'] = wp_json_encode($data['response_data']);
        }

        $result = $wpdb->update(
            self::get_table_name(),
            $data,
            ['id' => $id],
            null,
            ['%d']
        );

        return $result !== false;
    }

    /**
     * Get log entries
     *
     * @param array $args Query arguments
     * @return array
     */
    public static function get_logs($args = [])
    {
        global $wpdb;

        self::maybe_create_table();

        $defaults = [
            'per_page' => 20,
            'page' => 1,
            'orderby' => 'id',
            'order' => 'DESC',
            'status' => '',
            'search' => '',
        ];

        $args = wp_parse_args($args, $defaults);
        $table_name = self::get_table_name();
        
        $where = '1=1';
        $where_values = [];

        if (!empty($args['status'])) {
            $where .= ' AND status = %s';
            $where_values[] = $args['status'];
        }

        if (!empty($args['search'])) {
            $where .= ' AND (endpoint LIKE %s OR request_data LIKE %s OR response_data LIKE %s OR error_message LIKE %s)';
            $search_term = '%' . $wpdb->esc_like($args['search']) . '%';
            $where_values[] = $search_term;
            $where_values[] = $search_term;
            $where_values[] = $search_term;
            $where_values[] = $search_term;
        }

        $offset = ($args['page'] - 1) * $args['per_page'];
        
        $allowed_orderby = ['id', 'request_time', 'status', 'endpoint', 'created_at'];
        $orderby = in_array($args['orderby'], $allowed_orderby, true) ? $args['orderby'] : 'id';
        $order = strtoupper($args['order']) === 'ASC' ? 'ASC' : 'DESC';

        $query = "SELECT * FROM $table_name WHERE $where ORDER BY $orderby $order LIMIT %d OFFSET %d";
        $query_values = array_merge($where_values, [$args['per_page'], $offset]);

        if (!empty($where_values)) {
            $prepared_query = $wpdb->prepare($query, $query_values);
        } else {
            $prepared_query = $wpdb->prepare(
                "SELECT * FROM $table_name WHERE 1=1 ORDER BY $orderby $order LIMIT %d OFFSET %d",
                $args['per_page'],
                $offset
            );
        }

        return $wpdb->get_results($prepared_query, ARRAY_A);
    }

    /**
     * Get total count of logs
     *
     * @param array $args Query arguments
     * @return int
     */
    public static function get_logs_count($args = [])
    {
        global $wpdb;

        self::maybe_create_table();

        $table_name = self::get_table_name();
        
        $where = '1=1';
        $where_values = [];

        if (!empty($args['status'])) {
            $where .= ' AND status = %s';
            $where_values[] = $args['status'];
        }

        if (!empty($args['search'])) {
            $where .= ' AND (endpoint LIKE %s OR request_data LIKE %s OR response_data LIKE %s OR error_message LIKE %s)';
            $search_term = '%' . $wpdb->esc_like($args['search']) . '%';
            $where_values[] = $search_term;
            $where_values[] = $search_term;
            $where_values[] = $search_term;
            $where_values[] = $search_term;
        }

        if (!empty($where_values)) {
            return (int) $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM $table_name WHERE $where",
                $where_values
            ));
        }

        return (int) $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE 1=1");
    }

    /**
     * Delete a log entry
     *
     * @param int $id Log entry ID
     * @return bool
     */
    public static function delete_log($id)
    {
        global $wpdb;

        $result = $wpdb->delete(
            self::get_table_name(),
            ['id' => $id],
            ['%d']
        );

        return $result !== false;
    }

    /**
     * Delete multiple log entries
     *
     * @param array $ids Array of log entry IDs
     * @return int Number of deleted rows
     */
    public static function delete_logs($ids)
    {
        global $wpdb;

        if (empty($ids) || !is_array($ids)) {
            return 0;
        }

        // Sanitize and filter IDs
        $ids = array_map('absint', $ids);
        $ids = array_filter($ids); // Remove any zero values
        
        if (empty($ids)) {
            return 0;
        }

        $ids_placeholder = implode(',', array_fill(0, count($ids), '%d'));
        $table_name = self::get_table_name();

        $result = $wpdb->query(
            $wpdb->prepare(
                "DELETE FROM $table_name WHERE id IN ($ids_placeholder)",
                $ids
            )
        );

        return $result !== false ? $result : 0;
    }

    /**
     * Delete all log entries
     *
     * @return int Number of deleted rows
     */
    public static function delete_all_logs()
    {
        global $wpdb;

        $table_name = self::get_table_name();
        $result = $wpdb->query("TRUNCATE TABLE $table_name");

        return $result !== false;
    }

    /**
     * Get client IP address
     *
     * @return string
     */
    private static function get_client_ip()
    {
        $ip_keys = [
            'HTTP_CF_CONNECTING_IP', // Cloudflare
            'HTTP_X_FORWARDED_FOR',
            'HTTP_X_FORWARDED',
            'HTTP_X_CLUSTER_CLIENT_IP',
            'HTTP_FORWARDED_FOR',
            'HTTP_FORWARDED',
            'REMOTE_ADDR',
        ];

        foreach ($ip_keys as $key) {
            if (!empty($_SERVER[$key])) {
                $ip = sanitize_text_field(wp_unslash($_SERVER[$key]));
                // Handle comma-separated IPs (from proxies)
                if (strpos($ip, ',') !== false) {
                    $ip = trim(explode(',', $ip)[0]);
                }
                if (filter_var($ip, FILTER_VALIDATE_IP)) {
                    return $ip;
                }
            }
        }

        return 'unknown';
    }

    /**
     * Generate a new access key
     *
     * @return string The generated access key
     */
    public static function generate_access_key()
    {
        // Use cryptographically secure random bytes
        if (function_exists('random_bytes')) {
            $key = 'pssg_' . bin2hex(random_bytes(16));
        } else {
            // Fallback for older PHP versions
            $key = 'pssg_' . wp_generate_password(32, false, false);
        }
        update_option(self::ACCESS_KEY_OPTION, $key);
        return $key;
    }

    /**
     * Get the current access key
     *
     * @return string|false
     */
    public static function get_access_key()
    {
        return get_option(self::ACCESS_KEY_OPTION, false);
    }

    /**
     * Validate an access key
     *
     * @param string $key The key to validate
     * @return bool
     */
    public static function validate_access_key($key)
    {
        $stored_key = self::get_access_key();
        
        if (empty($stored_key) || empty($key)) {
            return false;
        }

        return hash_equals($stored_key, $key);
    }

    /**
     * Check if access key exists
     *
     * @return bool
     */
    public static function has_access_key()
    {
        $key = self::get_access_key();
        return !empty($key);
    }

    /**
     * Delete the access key
     *
     * @return bool
     */
    public static function delete_access_key()
    {
        return delete_option(self::ACCESS_KEY_OPTION);
    }
}
