<?php
/**
 * Database Security Layer
 *
 * Provides secure database access methods and SQL injection protection
 *
 * @package Care_API
 * @subpackage Utils
 * @version    1.0.0
 * @author     Descomplicar® <dev@descomplicar.pt>
 * @link       https://descomplicar.pt
 * @since      1.0.0
 */

namespace Care_API\Utils;

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

/**
 * Class Database_Security_Layer
 *
 * Secure database operations wrapper for WordPress $wpdb
 * Enforces prepared statements and provides security validation
 *
 * @since 1.0.0
 */
class Database_Security_Layer {

    /**
     * WordPress database instance
     *
     * @var wpdb
     */
    private static $wpdb;

    /**
     * Security audit log
     *
     * @var array
     */
    private static $security_audit = array();

    /**
     * Initialize the security layer
     *
     * @since 1.0.0
     */
    public static function init() {
        global $wpdb;
        self::$wpdb = $wpdb;

        // Hook into WordPress for security monitoring
        add_action( 'shutdown', array( self::class, 'log_security_audit' ) );
    }

    /**
     * Secure SELECT query with mandatory prepared statements
     *
     * @param string $query  SQL query with placeholders
     * @param array  $params Parameters for the query
     * @param string $output Optional. OBJECT, ARRAY_A, or ARRAY_N
     * @return mixed Query results
     * @throws InvalidArgumentException If query contains unsafe patterns
     * @since 1.0.0
     */
    public static function secure_get_results( $query, $params = array(), $output = OBJECT ) {
        self::validate_query_security( $query, $params );

        $prepared_query = self::$wpdb->prepare( $query, $params );
        $result = self::$wpdb->get_results( $prepared_query, $output );

        self::audit_query( 'SELECT', $query, $params );

        return $result;
    }

    /**
     * Secure single row query
     *
     * @param string $query  SQL query with placeholders
     * @param array  $params Parameters for the query
     * @param string $output Optional. OBJECT, ARRAY_A, or ARRAY_N
     * @return mixed Single row result
     * @throws InvalidArgumentException If query contains unsafe patterns
     * @since 1.0.0
     */
    public static function secure_get_row( $query, $params = array(), $output = OBJECT ) {
        self::validate_query_security( $query, $params );

        $prepared_query = self::$wpdb->prepare( $query, $params );
        $result = self::$wpdb->get_row( $prepared_query, $output );

        self::audit_query( 'SELECT_ROW', $query, $params );

        return $result;
    }

    /**
     * Secure single value query
     *
     * @param string $query  SQL query with placeholders
     * @param array  $params Parameters for the query
     * @return mixed Single value result
     * @throws InvalidArgumentException If query contains unsafe patterns
     * @since 1.0.0
     */
    public static function secure_get_var( $query, $params = array() ) {
        self::validate_query_security( $query, $params );

        $prepared_query = self::$wpdb->prepare( $query, $params );
        $result = self::$wpdb->get_var( $prepared_query );

        self::audit_query( 'SELECT_VAR', $query, $params );

        return $result;
    }

    /**
     * Secure INSERT operation
     *
     * @param string $table Table name (without prefix)
     * @param array  $data  Data to insert
     * @param array  $format Optional. Data format
     * @return int|false Insert ID on success, false on failure
     * @throws InvalidArgumentException If table name or data is invalid
     * @since 1.0.0
     */
    public static function secure_insert( $table, $data, $format = null ) {
        $table_name = self::validate_table_name( $table );
        self::validate_insert_data( $data );

        $result = self::$wpdb->insert( $table_name, $data, $format );

        if ( $result !== false ) {
            self::audit_query( 'INSERT', "INSERT INTO {$table_name}", $data );
            return self::$wpdb->insert_id;
        }

        return false;
    }

    /**
     * Secure UPDATE operation
     *
     * @param string $table  Table name (without prefix)
     * @param array  $data   Data to update
     * @param array  $where  WHERE conditions
     * @param array  $format Optional. Data format
     * @param array  $where_format Optional. WHERE format
     * @return int|false Number of rows updated, false on failure
     * @throws InvalidArgumentException If parameters are invalid
     * @since 1.0.0
     */
    public static function secure_update( $table, $data, $where, $format = null, $where_format = null ) {
        $table_name = self::validate_table_name( $table );
        self::validate_update_data( $data, $where );

        $result = self::$wpdb->update( $table_name, $data, $where, $format, $where_format );

        if ( $result !== false ) {
            self::audit_query( 'UPDATE', "UPDATE {$table_name}", array_merge( $data, $where ) );
        }

        return $result;
    }

    /**
     * Secure DELETE operation
     *
     * @param string $table  Table name (without prefix)
     * @param array  $where  WHERE conditions
     * @param array  $where_format Optional. WHERE format
     * @return int|false Number of rows deleted, false on failure
     * @throws InvalidArgumentException If parameters are invalid
     * @since 1.0.0
     */
    public static function secure_delete( $table, $where, $where_format = null ) {
        $table_name = self::validate_table_name( $table );
        self::validate_where_conditions( $where );

        $result = self::$wpdb->delete( $table_name, $where, $where_format );

        if ( $result !== false ) {
            self::audit_query( 'DELETE', "DELETE FROM {$table_name}", $where );
        }

        return $result;
    }

    /**
     * Validate query for security vulnerabilities
     *
     * @param string $query  SQL query
     * @param array  $params Parameters
     * @throws InvalidArgumentException If query is unsafe
     * @since 1.0.0
     */
    private static function validate_query_security( $query, $params ) {
        // Check for raw queries without placeholders when params exist
        if ( ! empty( $params ) && strpos( $query, '%' ) === false ) {
            throw new \InvalidArgumentException( 'Query contains parameters but no placeholders - potential SQL injection' );
        }

        // Detect dangerous SQL patterns
        $dangerous_patterns = array(
            '/;\s*(DROP|DELETE|UPDATE|INSERT|CREATE|ALTER|TRUNCATE)/i',
            '/UNION\s+SELECT/i',
            '/\'\s*OR\s+\'/i',
            '/--\s/',
            '/\/\*.*\*\//s',
            '/\bEXEC\s*\(/i',
            '/\bxp_\w+/i'
        );

        foreach ( $dangerous_patterns as $pattern ) {
            if ( preg_match( $pattern, $query ) ) {
                self::log_security_violation( 'Dangerous SQL pattern detected', $query );
                throw new \InvalidArgumentException( 'Query contains potentially dangerous SQL patterns' );
            }
        }

        // Validate parameter count matches placeholders
        $placeholder_count = substr_count( $query, '%s' ) + substr_count( $query, '%d' ) + substr_count( $query, '%f' );
        $param_count = count( $params );

        if ( $placeholder_count !== $param_count ) {
            throw new \InvalidArgumentException( "Parameter count mismatch: {$param_count} params for {$placeholder_count} placeholders" );
        }
    }

    /**
     * Validate table name
     *
     * @param string $table Table name
     * @return string Full table name with prefix
     * @throws InvalidArgumentException If table name is invalid
     * @since 1.0.0
     */
    private static function validate_table_name( $table ) {
        // Allow only alphanumeric characters and underscores
        if ( ! preg_match( '/^[a-zA-Z0-9_]+$/', $table ) ) {
            throw new \InvalidArgumentException( 'Invalid table name format' );
        }

        // Check if it's a known KiviCare table
        $allowed_tables = array(
            'kc_clinics', 'kc_patients', 'kc_doctors', 'kc_appointments',
            'kc_patient_encounters', 'kc_prescription', 'kc_bills',
            'kc_services', 'kc_doctor_clinic_mappings', 'kc_patient_clinic_mappings',
            'kc_api_sessions'
        );

        if ( ! in_array( $table, $allowed_tables, true ) ) {
            throw new \InvalidArgumentException( "Table '{$table}' is not in the allowed tables list" );
        }

        return self::$wpdb->prefix . $table;
    }

    /**
     * Validate insert data
     *
     * @param array $data Insert data
     * @throws InvalidArgumentException If data is invalid
     * @since 1.0.0
     */
    private static function validate_insert_data( $data ) {
        if ( empty( $data ) || ! is_array( $data ) ) {
            throw new \InvalidArgumentException( 'Insert data must be a non-empty array' );
        }

        foreach ( $data as $key => $value ) {
            if ( ! is_string( $key ) || ! preg_match( '/^[a-zA-Z0-9_]+$/', $key ) ) {
                throw new \InvalidArgumentException( "Invalid column name: {$key}" );
            }
        }
    }

    /**
     * Validate update data
     *
     * @param array $data  Update data
     * @param array $where WHERE conditions
     * @throws InvalidArgumentException If data is invalid
     * @since 1.0.0
     */
    private static function validate_update_data( $data, $where ) {
        self::validate_insert_data( $data );
        self::validate_where_conditions( $where );
    }

    /**
     * Validate WHERE conditions
     *
     * @param array $where WHERE conditions
     * @throws InvalidArgumentException If conditions are invalid
     * @since 1.0.0
     */
    private static function validate_where_conditions( $where ) {
        if ( empty( $where ) || ! is_array( $where ) ) {
            throw new \InvalidArgumentException( 'WHERE conditions must be a non-empty array' );
        }

        foreach ( $where as $key => $value ) {
            if ( ! is_string( $key ) || ! preg_match( '/^[a-zA-Z0-9_]+$/', $key ) ) {
                throw new \InvalidArgumentException( "Invalid WHERE column name: {$key}" );
            }
        }
    }

    /**
     * Audit database query
     *
     * @param string $operation Operation type
     * @param string $query     SQL query
     * @param array  $params    Query parameters
     * @since 1.0.0
     */
    private static function audit_query( $operation, $query, $params ) {
        self::$security_audit[] = array(
            'timestamp' => current_time( 'mysql' ),
            'operation' => $operation,
            'query' => $query,
            'param_count' => count( $params ),
            'user_id' => get_current_user_id(),
            'ip' => self::get_client_ip()
        );
    }

    /**
     * Log security violation
     *
     * @param string $message Security violation message
     * @param string $query   Offending query
     * @since 1.0.0
     */
    private static function log_security_violation( $message, $query ) {
        error_log( sprintf(
            'CARE API SECURITY VIOLATION: %s | Query: %s | User: %d | IP: %s',
            $message,
            $query,
            get_current_user_id(),
            self::get_client_ip()
        ) );
    }

    /**
     * Get client IP address
     *
     * @return string Client IP
     * @since 1.0.0
     */
    private static function get_client_ip() {
        $ip_keys = array( 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'HTTP_CLIENT_IP', 'REMOTE_ADDR' );

        foreach ( $ip_keys as $key ) {
            if ( ! empty( $_SERVER[ $key ] ) ) {
                $ip = sanitize_text_field( wp_unslash( $_SERVER[ $key ] ) );
                return filter_var( $ip, FILTER_VALIDATE_IP ) ? $ip : 'unknown';
            }
        }

        return 'unknown';
    }

    /**
     * Log security audit to database
     *
     * @since 1.0.0
     */
    public static function log_security_audit() {
        if ( empty( self::$security_audit ) ) {
            return;
        }

        // Log audit entries (in production, this would go to a secure audit table)
        if ( defined( 'CARE_API_DEBUG' ) && CARE_API_DEBUG ) {
            error_log( 'CARE API Security Audit: ' . wp_json_encode( self::$security_audit ) );
        }

        // Clear audit log
        self::$security_audit = array();
    }

    /**
     * Get security statistics
     *
     * @return array Security statistics
     * @since 1.0.0
     */
    public static function get_security_stats() {
        return array(
            'queries_audited' => count( self::$security_audit ),
            'last_query_time' => ! empty( self::$security_audit ) ? end( self::$security_audit )['timestamp'] : null,
            'security_layer_active' => true,
            'prepared_statements_enforced' => true
        );
    }
}