<?php

class BotGobot {

    private string $apiKey;
    private string $redirection;
    private string $ipBotFile;

    private static ?array $config = null;

    private static function loadConfig(): array {

        if (self::$config !== null) {
            return self::$config;
        }

        self::$config = [];

        // Load from config.json first
        $basePath = defined('BASE_PATH')
            ? BASE_PATH
            : dirname(dirname(__DIR__));

        $jsonPath = $basePath . '/system/Gobot/config.json';

        if (file_exists($jsonPath)) {

            $json = file_get_contents($jsonPath);
            $decoded = json_decode($json, true);

            if (is_array($decoded)) {
                self::$config = $decoded;
            }
        }

        // Fallback to PHP constants
        if (defined('BOT_PROTECTION_ENABLED') && !isset(self::$config['bot_protection'])) {
            self::$config['bot_protection'] = BOT_PROTECTION_ENABLED;
        }

        if (defined('BOT_GOBOT_API_KEY') && empty(self::$config['gobot_key'])) {
            self::$config['gobot_key'] = BOT_GOBOT_API_KEY;
        }

        if (defined('BOT_REDIRECTION') && empty(self::$config['redirection'])) {
            self::$config['redirection'] = BOT_REDIRECTION;
        }

        return self::$config;
    }

    private static function getConfig(string $key, $default = null) {
        return self::loadConfig()[$key] ?? $default;
    }

    public static function isProtectionEnabled(): bool {

        return (bool) self::getConfig(
            'bot_protection',
            defined('BOT_PROTECTION_ENABLED')
                ? BOT_PROTECTION_ENABLED
                : false
        );
    }

    public function __construct() {

        $this->apiKey = (string) self::getConfig(
            'gobot_key',
            defined('BOT_GOBOT_API_KEY')
                ? BOT_GOBOT_API_KEY
                : ''
        );

        $this->redirection = (string) self::getConfig(
            'redirection',
            defined('BOT_REDIRECTION')
                ? BOT_REDIRECTION
                : ''
        );

        // lokasi file ipbot.txt
        $this->ipBotFile = dirname(__DIR__, 2) . '/ipbot.txt';

        // buat file jika belum ada
        if (!file_exists($this->ipBotFile)) {
            file_put_contents($this->ipBotFile, '');
        }
    }

    private function getClientIP(): string {

        return function_exists('get_client_ip')
            ? get_client_ip()
            : (
                function_exists('getClientIP')
                    ? getClientIP()
                    : ($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0')
            );
    }

    /*
    |--------------------------------------------------------------------------
    | CEK IP DI ipbot.txt
    |--------------------------------------------------------------------------
    */

    private function isBlockedIP(string $ip): bool {

        if (!file_exists($this->ipBotFile)) {
            return false;
        }

        $ips = file(
            $this->ipBotFile,
            FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES
        );

        return in_array(trim($ip), $ips, true);
    }

    /*
    |--------------------------------------------------------------------------
    | SIMPAN IP KE ipbot.txt
    |--------------------------------------------------------------------------
    */

    private function saveBlockedIP(string $ip): void {

        // jangan duplicate
        if ($this->isBlockedIP($ip)) {
            return;
        }

        file_put_contents(
            $this->ipBotFile,
            $ip . PHP_EOL,
            FILE_APPEND | LOCK_EX
        );
    }

    public function showNotFoundPage(): void {

        header("HTTP/1.0 404 Not Found");

        $downPath = defined('SYSTEM_PATH')
            ? SYSTEM_PATH . '/Gobot/down.php'
            : __DIR__ . '/down.php';

        if (file_exists($downPath)) {

            include $downPath;

        } else {

            echo '
            <!DOCTYPE html>
            <html>
            <head>
                <title>521 Web server is down</title>
            </head>
            <body>
                <h1>521 Web server is down</h1>
            </body>
            </html>';
        }

        exit();
    }

    private function httpPost(string $url, array $postFields): array {

        $ch = curl_init($url);

        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => 15,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => http_build_query($postFields),
        ]);

        $res  = curl_exec($ch);
        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

        curl_close($ch);

        return [
            'response'  => $res,
            'http_code' => $code
        ];
    }

    public function gobotBlocker(string $ip = null): array {

        if (empty($this->apiKey)) {
            return [
                'success' => false,
                'message' => 'Missing API key'
            ];
        }

        $ip = $ip ?? $this->getClientIP();

        /*
        |--------------------------------------------------------------------------
        | CEK DULU DI ipbot.txt
        |--------------------------------------------------------------------------
        */

        if ($this->isBlockedIP($ip)) {

            return [
                'success' => false,
                'type'    => 'bot',
                'reason'  => 'IP already blocked'
            ];
        }

        $ua = $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown';

        $url =
            ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on')
                ? 'https'
                : 'http')
            . '://'
            . ($_SERVER['HTTP_HOST'] ?? '')
            . ($_SERVER['REQUEST_URI'] ?? '/');

        $reff = $_SERVER['HTTP_REFERER'] ?? '';

        $result = $this->httpPost(
            'https://gobot.su/api/v1/blocker',
            [
                'ip'     => $ip,
                'apikey' => $this->apiKey,
                'ua'     => $ua,
                'url'    => $url,
                'reff'   => $reff,
            ]
        );

        $res  = $result['response'];
        $code = $result['http_code'];

        if (!$res) {
            return [
                'success' => false,
                'message' => 'Server not responding'
            ];
        }

        $data = json_decode($res, true);

        if (!is_array($data)) {
            return [
                'success' => false,
                'message' => 'Invalid API response'
            ];
        }

        if ($code >= 400) {
            return [
                'success' => false,
                'message' => "HTTP Error $code"
            ];
        }

        $type   = strtolower($data['type'] ?? 'unknown');
        $reason = strtolower($data['reason'] ?? '');

        if (strpos($reason, 'no active package') !== false) {
            return [
                'success' => false,
                'message' => 'No Package'
            ];
        }

        if (strpos($reason, 'expired') !== false) {
            return [
                'success' => false,
                'message' => 'Package Expired'
            ];
        }

        if (strpos($reason, 'quota') !== false) {
            return [
                'success' => false,
                'message' => 'Quota Limit'
            ];
        }

        /*
        |--------------------------------------------------------------------------
        | JIKA BOT → SIMPAN KE ipbot.txt
        |--------------------------------------------------------------------------
        */

        if ($type === 'bot') {

            $this->saveBlockedIP($ip);

            return [
                'success' => false,
                'type'    => 'bot',
                'reason'  => $reason ?: 'Detected as bot'
            ];
        }

        if ($type === 'human') {

            return [
                'success' => true,
                'type'    => 'human',
                'reason'  => $reason
            ];
        }

        return [
            'success' => false,
            'message' => 'Unknown response'
        ];
    }

    public function handleBotDetected(): void {

        if (!empty($this->redirection)) {

            header('Location: ' . $this->redirection);
            exit();
        }

        $this->showNotFoundPage();
    }
}