<?php
defined('BASEPATH') or exit('No direct script access allowed');

/**
 * Anti-Spam Helper for Actions
 * 
 * Helper library to prevent spam by tracking attempts for different actions
 * and blocking after too many failed attempts.
 */
class Spam_blocker
{
    protected $CI;
    protected $max_attempts = 5;
    protected $block_time = 3600;
    protected $table_name = 'action_attempts';

    /**
     * Constructor
     */
    public function __construct()
    {
        // Get CI instance
        $this->CI = &get_instance();

        // Load required libraries
        $this->CI->load->database();
        $this->CI->load->library('session');

        // Ensure the table exists
        $this->_create_table_if_not_exists();
    }

    public function check_action_allowed($action_type, $is_success = FALSE, $user_id = NULL)
    {
        // Clean old records first


        // Get user information
        $ip_address = $this->CI->input->ip_address();
        $user_agent = $this->CI->input->user_agent();
        $session_id = $this->CI->session->session_id;
        $mac_address = $this->_get_mac_address();

        // Check if user already exceeded limits (only for failed attempts)
        if ($is_success) {
            $this->_clean_old_attempts();
            // Count attempts
            $ip_attempts = $this->_count_attempts_by_ip($ip_address, $action_type);
            $session_attempts = $this->_count_attempts_by_session($session_id, $action_type);

            $mac_attempts = 0;
            if ($mac_address) {
                $mac_attempts = $this->_count_attempts_by_mac($mac_address, $action_type);
            }

            $user_attempts = 0;
            if ($user_id) {
                $user_attempts = $this->_count_attempts_by_user_id($user_id, $action_type);
            }

            // Check if any limit has been reached
            if ($ip_attempts >= $this->max_attempts) {
                return __("too_many_attempts_please_try_later");
            }

            if ($session_attempts >= $this->max_attempts) {
                return __("too_many_attempts_please_try_later") . __('from_browser');
            }

            if ($mac_address && $mac_attempts >= $this->max_attempts) {
                return __("too_many_attempts_please_try_later") . __('device');
            }

            if ($user_id && $user_attempts >= $this->max_attempts) {
                return __("too_many_attempts_please_try_later") . __('user');
            }

            // Additional check for bot patterns in user-agent
            if ($this->_check_bot_patterns($user_agent)) {
                return "Automated {$action_type} attempts detected. Please contact support if you believe this is an error.";
            }

            $this->_record_attempt($action_type, $is_success, $user_id);
        }

        // All checks passed or this is a successful attempt, record the attempt


        return TRUE;
    }

    public function reset_attempts($action_type, $user_id = NULL)
    {
        $this->CI->db->where('action_type', $action_type);

        if ($user_id) {
            $this->CI->db->where('user_id', $user_id);
        } else {
            $ip_address = $this->CI->input->ip_address();
            $session_id = $this->CI->session->session_id;

            $this->CI->db->group_start();
            $this->CI->db->where('ip_address', $ip_address);
            $this->CI->db->or_where('session_id', $session_id);
            $this->CI->db->group_end();
        }

        $this->CI->db->delete($this->table_name);
    }


    private function _record_attempt($action_type, $success = FALSE, $user_id = NULL)
    {
        $data = array(
            'ip_address' => $this->CI->input->ip_address(),
            'user_agent' => $this->CI->input->user_agent(),
            'session_id' => $this->CI->session->session_id,
            'mac_address' => $this->_get_mac_address(),
            'user_id' => $user_id,
            'action_type' => $action_type,
            'attempt_time' => date('Y-m-d H:i:s'),
            'success' => 1
        );

        $this->CI->db->insert($this->table_name, $data);
    }

    private function _get_mac_address()
    {
        // Try to get MAC address if possible - this is not reliable in all environments
        // and typically requires special server configurations to work properly
        if (function_exists('exec')) {
            // For Windows
            if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
                @exec('getmac', $mac_addr);
                if (isset($mac_addr[0])) {
                    $mac = preg_replace('/[^0-9a-f]/i', '', $mac_addr[0]);
                    if ($mac) return $mac;
                }
            }
            // For Linux/Unix/Mac
            else {
                @exec('ifconfig -a | grep HWaddr', $mac_addr);
                if (isset($mac_addr[0])) {
                    $mac = preg_replace('/.*HWaddr\s+([0-9a-f:]+).*/i', '$1', $mac_addr[0]);
                    if ($mac) return str_replace(':', '', $mac);
                }
            }
        }

        if (function_exists('shell_exec')) {
            $mac = @shell_exec("cat /sys/class/net/eth0/address");
            if ($mac) return trim(str_replace(':', '', $mac));
        }

        return NULL;
    }

    /**
     * Check user agent for common bot patterns
     * 
     * @param string $user_agent
     * @return bool
     */
    private function _check_bot_patterns($user_agent)
    {
        $bot_patterns = array(
            'bot',
            'crawl',
            'slurp',
            'spider',
            'curl',
            'python-requests',
            'wget',
            'automation',
            'scrape',
            'headless',
            'phantom',
            'selenium'
        );

        $user_agent = strtolower($user_agent);

        foreach ($bot_patterns as $pattern) {
            if (strpos($user_agent, $pattern) !== FALSE) {
                return TRUE;
            }
        }

        return FALSE;
    }

    private function _count_attempts_by_ip($ip_address, $action_type)
    {
        $cutoff_time = date('Y-m-d H:i:s', time() - $this->block_time);

        $this->CI->db->where('ip_address', $ip_address);
        $this->CI->db->where('action_type', $action_type);
        $this->CI->db->where('attempt_time >', $cutoff_time);

        return $this->CI->db->count_all_results($this->table_name);
    }


    private function _count_attempts_by_session($session_id, $action_type)
    {
        $cutoff_time = date('Y-m-d H:i:s', time() - $this->block_time);

        $this->CI->db->where('session_id', $session_id);
        $this->CI->db->where('action_type', $action_type);
        $this->CI->db->where('attempt_time >', $cutoff_time);

        return $this->CI->db->count_all_results($this->table_name);
    }

    /**
     * Count attempts by MAC address within the block time
     * 
     * @param string $mac_address
     * @param string $action_type
     * @return int
     */
    private function _count_attempts_by_mac($mac_address, $action_type)
    {
        $cutoff_time = date('Y-m-d H:i:s', time() - $this->block_time);

        $this->CI->db->where('mac_address', $mac_address);
        $this->CI->db->where('action_type', $action_type);
        $this->CI->db->where('attempt_time >', $cutoff_time);

        return $this->CI->db->count_all_results($this->table_name);
    }


    private function _count_attempts_by_user_id($user_id, $action_type)
    {
        $cutoff_time = date('Y-m-d H:i:s', time() - $this->block_time);

        $this->CI->db->where('user_id', $user_id);
        $this->CI->db->where('action_type', $action_type);
        $this->CI->db->where('attempt_time >', $cutoff_time);

        return $this->CI->db->count_all_results($this->table_name);
    }

    private function _clean_old_attempts()
    {
        $cutoff_time = date('Y-m-d H:i:s', time() - ($this->block_time * 2));

        $this->CI->db->where('attempt_time <', $cutoff_time);
        $this->CI->db->delete($this->table_name);
    }

    /**
     * Create the attempts table if it doesn't exist
     * 
     * @return void
     */
    private function _create_table_if_not_exists()
    {
        if (!$this->CI->db->table_exists($this->table_name)) {
            $this->CI->load->dbforge();

            $fields = array(
                'id' => array(
                    'type' => 'INT',
                    'constraint' => 11,
                    'unsigned' => TRUE,
                    'auto_increment' => TRUE
                ),
                'ip_address' => array(
                    'type' => 'VARCHAR',
                    'constraint' => '45'
                ),
                'mac_address' => array(
                    'type' => 'VARCHAR',
                    'constraint' => '32',
                    'null' => TRUE
                ),
                'user_agent' => array(
                    'type' => 'VARCHAR',
                    'constraint' => '255'
                ),
                'session_id' => array(
                    'type' => 'VARCHAR',
                    'constraint' => '40'
                ),
                'user_id' => array(
                    'type' => 'VARCHAR',
                    'constraint' => '100',
                    'null' => TRUE
                ),
                'action_type' => array(
                    'type' => 'VARCHAR',
                    'constraint' => '50'
                ),
                'attempt_time' => array(
                    'type' => 'DATETIME'
                ),
                'success' => array(
                    'type' => 'TINYINT',
                    'constraint' => '1',
                    'default' => '0'
                )
            );

            $this->CI->dbforge->add_field($fields);
            $this->CI->dbforge->add_key('id', TRUE);
            $this->CI->dbforge->add_key('ip_address');
            $this->CI->dbforge->add_key('session_id');
            $this->CI->dbforge->add_key('action_type');

            $this->CI->dbforge->create_table($this->table_name, TRUE);
        }
    }
}
