<?php
declare(strict_types=1);

/**
 * sync_prediksi_pro.php
 *
 * PRO version:
 * - auto detect folder ID
 * - trigger/fetch source per ID
 * - regex-safe replacement
 * - force update optional
 * - inject sync marker optional
 * - smart summary: updated / checked_no_change / failed
 * - optional LiteSpeed purge
 * - log file
 *
 * Browser:
 *   /sync_prediksi_pro.php
 *   /sync_prediksi_pro.php?purge=1
 *   /sync_prediksi_pro.php?force=1&marker=1&purge=1
 *
 * CLI:
 *   php sync_prediksi_pro.php
 *   php sync_prediksi_pro.php --force=1 --marker=1 --purge=1
 */

error_reporting(E_ALL);
ini_set('display_errors', '1');
set_time_limit(900);

$sourceBase = 'https://blog8.inatips.com/client/blog/prediksi/';
$prediksiDir = __DIR__ . '/client/blog/prediksi';
$logFile = __DIR__ . '/sync_prediksi_pro.log';

$plainReplacements = [
    'Inatogel | Blog Pasaran Terlengkap' => 'Totowin88 | Blog Pasaran Terlengkap dan Terpercaya',
    '/storage/gambarGlobal/blog/p0Dvjjy2UkslgvNdY2rG4ogBuUFFslblEfYkFyCA.png' => 'https://imagizer.imageshack.com/img923/7031/Kc4ulM.png',
    'Inatogel prediksi terlengkap' => 'Totowin88 prediksi terlengkap dan terpercaya',
    'https://sorty.bio/rtpina/' => 'https://yuklah.me/rtp-totowin88',
    'https://sorty.bio/buktiina/' => 'https://yuklah.me/BuktiJp-Totowin88',
    'https://sorty.bio/Inapromo' => 'https://yuklah.me/LuckySpin-Totowin88',
    'https://sorty.bio/apkina' => 'https://yuklah.me/APKtotowin88',
    'https://t.me/inatogel88_official' => 'https://yuklah.me/totowin88',
    'https://secure.livechatinc.com/customer/action/open_chat?license_id=13079895' => 'https://yuklah.me/Totowin88-LC',
    'https://api.whatsapp.com/send?phone=6281389114046' => 'https://yuklah.me/Totowin88-WA',
    'https://paito3.acakina.com/assets/frontend/img/logo.png' => 'https://imagizer.imageshack.com/img921/2258/mXKOxK.png',
    'Selamat datang di Inatogel Situs Games Online Terpercaya | Data Result Angka Terlengkap | Prediksi Angka Jitu Online | Paito Warna Terlengkap' => 'Selamat datang di Totowin88 Situs Togel dengan Pasaran Terlengkap dan Terpercaya | Result Angka Togel Terlengkap | Prediksi Togel Angka Jitu Online Terpercaya | Paito Warna Terlengkap 2026',
    '/storage/gambarGlobal/blog/7wEI1zIVYeWdm1NoIVjlOepcXBEDX2h533KFkd9E.png' => 'https://buatbanner.com/assets/totowin88/img/banner/promo/totowin88_BonusDeposit10v2.webp',
    'https://cdn.harimauterbang.xyz/inatogel/Image/4tiprize1234.jpg' => 'https://buatbanner.com/assets/totowin88/img/banner/promo/totowin88_antirungkatv2.webp',
    '/storage/gambarGlobal/blog/lrt1E8spjaAA9tGiWuWp5PzVPORyKDJklHN9ReNe.jpg' => 'https://buatbanner.com/assets/totowin88/img/banner/promo/Totowin88_Lucky_Spin_Slider.webp',
    '2022</span> Inatogel Agen Games Online Terpercaya.</div>' => '2026</span> Totowin88 Agen Togel Online Terpercaya.</div>',
    'https://blog8.inatips.com/' => '/',
];

$regexReplacements = [
    [
        'pattern' => '~https://sorty\.bio/ina1(?=[^A-Za-z0-9_]|$)~',
        'replacement' => 'https://yuklah.me/totowin88',
        'label' => 'sorty.bio/ina1 exact boundary'
    ],
    [
        'pattern' => '~https://sorty\.bio/ina(?=[^A-Za-z0-9_]|$)~',
        'replacement' => 'https://yuklah.me/totowin88',
        'label' => 'sorty.bio/ina exact boundary'
    ],
];

function isCli(): bool { return PHP_SAPI === 'cli'; }

function getOptValue(string $name, string $default = ''): string
{
    if (isCli()) {
        global $argv;
        foreach ($argv as $arg) {
            if (strpos($arg, "--{$name}=") === 0) {
                return substr($arg, strlen($name) + 3);
            }
        }
        return $default;
    }
    return isset($_GET[$name]) ? (string)$_GET[$name] : $default;
}

function appendLog(string $path, string $line): void
{
    @file_put_contents($path, '[' . date('Y-m-d H:i:s') . '] ' . $line . PHP_EOL, FILE_APPEND);
}

function detectNumericFolders(string $baseDir): array
{
    if (!is_dir($baseDir)) {
        throw new RuntimeException("Folder prediksi tidak ditemukan: {$baseDir}");
    }

    $ids = [];
    $items = scandir($baseDir);
    if ($items === false) {
        throw new RuntimeException("Gagal membaca folder: {$baseDir}");
    }

    foreach ($items as $item) {
        if ($item === '.' || $item === '..') continue;
        $full = $baseDir . DIRECTORY_SEPARATOR . $item;
        if (is_dir($full) && ctype_digit($item)) $ids[] = (int)$item;
    }

    sort($ids, SORT_NUMERIC);
    return $ids;
}

function fetchUrl(string $url): string
{
    for ($i = 0; $i < 3; $i++) {

        $ch = curl_init($url);

        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_CONNECTTIMEOUT => 10,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,

            // 🔥 WAJIB biar dianggap browser
            CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120 Safari/537.36',

            CURLOPT_HTTPHEADER => [
                'Accept: text/html,application/xhtml+xml',
                'Cache-Control: no-cache'
            ],
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        // ❌ gagal fetch
        if ($response === false || $httpCode >= 400) {
            usleep(200000);
            continue;
        }

        // 💀 DETECT SALAH HALAMAN
        if (stripos($response, 'Index of /') !== false) {
            usleep(200000);
            continue;
        }

        // 💀 DETECT HTML INVALID (opsional tapi bagus)
        if (stripos($response, '<html') === false) {
            usleep(200000);
            continue;
        }

        return (string)$response;
    }

    throw new RuntimeException("Gagal fetch setelah retry: {$url}");
}

function fetchUrlLoose(string $url): array
{
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_CONNECTTIMEOUT => 15,
        CURLOPT_TIMEOUT => 60,
        CURLOPT_SSL_VERIFYPEER => false,
        CURLOPT_SSL_VERIFYHOST => 0,
        CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120 Safari/537.36',
    ]);

    $body = curl_exec($ch);
    $http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $err = curl_error($ch);
    curl_close($ch);

    return [
        'ok' => $body !== false && $http < 400,
        'http_code' => $http,
        'error' => $err,
        'body' => $body === false ? '' : (string)$body,
        'url' => $url,
    ];
}

function normalizeSavedSourceHtml(string $raw): string
{
    if (stripos($raw, 'class="line-content"') !== false || stripos($raw, "class='line-content'") !== false) {
        if (preg_match_all('/<td class="line-content">(.*?)<\/td>/is', $raw, $matches)) {
            $pieces = [];
            foreach ($matches[1] as $piece) {
                $piece = preg_replace('/<br\s*\/?>/i', "\n", $piece);
                $piece = html_entity_decode(strip_tags($piece), ENT_QUOTES | ENT_HTML5, 'UTF-8');
                $pieces[] = $piece;
            }
            $joined = implode("\n", $pieces);
            if (stripos($joined, '<!DOCTYPE html>') !== false || stripos($joined, '<html') !== false) {
                return $joined;
            }
        }
    }
    return $raw;
}

function ensureDir(string $dir): void
{
    if (!is_dir($dir)) mkdir($dir, 0777, true);
}

function applyPlainReplacements(string $html, array $plainReplacements, array &$details): string
{
    uksort($plainReplacements, function($a, $b) {
        return strlen($b) <=> strlen($a);
    });

    foreach ($plainReplacements as $from => $to) {
        $count = 0;
        $html = str_replace($from, $to, $html, $count);
        if ($count > 0) {
            $details[] = [
                'type' => 'plain',
                'from' => $from,
                'to' => $to,
                'count' => $count
            ];
        }
    }
    return $html;
}

function applyRegexReplacements(string $html, array $regexReplacements, array &$details): string
{
    foreach ($regexReplacements as $rule) {
        $count = 0;
        $html = preg_replace($rule['pattern'], $rule['replacement'], $html, -1, $count);
        if ($count > 0) {
            $details[] = [
                'type' => 'regex',
                'label' => $rule['label'],
                'pattern' => $rule['pattern'],
                'replacement' => $rule['replacement'],
                'count' => $count
            ];
        }
    }
    return $html;
}

function applyFinalCorrections(string $html, array &$details): string
{
    $finalFixes = [
        '~https://yuklah\.me/totowin88[0-9]+~' => 'https://yuklah.me/totowin88',
    ];

    foreach ($finalFixes as $pattern => $replacement) {
        $count = 0;
        $html = preg_replace($pattern, $replacement, $html, -1, $count);
        if ($count > 0) {
            $details[] = [
                'type' => 'final_fix',
                'pattern' => $pattern,
                'replacement' => $replacement,
                'count' => $count
            ];
        }
    }
    return $html;
}

function injectMarker(string $html, int $id): string
{
    $marker = '<!-- synced: ' . date('Y-m-d H:i:s') . ' | id=' . $id . ' -->';
    if (stripos($html, '</body>') !== false) {
        return preg_replace('~</body>~i', $marker . "\n</body>", $html, 1);
    }
    return $html . "\n" . $marker;
}

function purgeLiteSpeed(): array
{
    $urls = [
        'https://info-angkajitu.com/?LSCWP_CTRL=flush_all',
        'https://info-angkajitu.com/?litespeed_purge=all',
    ];

    $results = [];
    foreach ($urls as $url) {
        $results[] = fetchUrlLoose($url);
    }
    return $results;
}

function syncOne(
    int $id,
    string $sourceBase,
    string $prediksiDir,
    array $plainReplacements,
    array $regexReplacements,
    bool $force,
    bool $marker
): array {
    $sourceUrl = rtrim($sourceBase, '/') . '/' . $id;
    $targetDir = rtrim($prediksiDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $id;
    $targetFile = $targetDir . DIRECTORY_SEPARATOR . 'index.html';
    $reportFile = $targetDir . DIRECTORY_SEPARATOR . '_sync_report.json';

    $raw = fetchUrl($sourceUrl);
    $normalized = normalizeSavedSourceHtml($raw);

    $beforeHash = file_exists($targetFile) ? md5_file($targetFile) : null;

    $details = [];
    $result = $normalized;
    $result = applyPlainReplacements($result, $plainReplacements, $details);
    $result = applyRegexReplacements($result, $regexReplacements, $details);
    $result = applyFinalCorrections($result, $details);

    if ($marker) {
        $result = injectMarker($result, $id);
    }

    if ($force) {
        $result .= "\n<!-- force-write: " . date('YmdHis') . ' -->';
    }

    ensureDir($targetDir);
    file_put_contents($targetFile, $result);

    $afterHash = file_exists($targetFile) ? md5_file($targetFile) : null;
    $changed = $beforeHash !== $afterHash;

    $report = [
        'success' => true,
        'id' => $id,
        'source_url' => $sourceUrl,
        'target_file' => $targetFile,
        'changed' => $changed,
        'applied_rules' => count($details),
        'force' => $force,
        'marker' => $marker,
        'checked_at' => date('Y-m-d H:i:s'),
    ];

    file_put_contents($reportFile, json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));

    return $report;
}

try {
    $purge = getOptValue('purge', '0') === '1';
    $force = getOptValue('force', '0') === '1';
    $marker = getOptValue('marker', '0') === '1';
    $delayMs = max(0, (int)getOptValue('delay_ms', '300'));

    $ids = detectNumericFolders($prediksiDir);
    if (empty($ids)) {
        throw new RuntimeException('Tidak ada folder ID yang terdeteksi.');
    }

    appendLog($logFile, 'START pro | ids=' . implode(',', $ids) . ' | purge=' . ($purge ? '1' : '0') . ' | force=' . ($force ? '1' : '0') . ' | marker=' . ($marker ? '1' : '0'));

    $updated = [];
    $checkedNoChange = [];
    $failed = [];
    $results = [];

    foreach ($ids as $id) {
        try {
            $row = syncOne($id, $sourceBase, $prediksiDir, $plainReplacements, $regexReplacements, $force, $marker);
            $results[] = $row;
            if ($row['changed']) {
                $updated[] = $id;
            } else {
                $checkedNoChange[] = $id;
            }
            appendLog($logFile, 'ID ' . $id . ' | changed=' . ($row['changed'] ? 'yes' : 'no'));
        } catch (Throwable $e) {
            $failed[] = [
                'id' => $id,
                'error' => $e->getMessage(),
            ];
            appendLog($logFile, 'ID ' . $id . ' | ERROR ' . $e->getMessage());
        }

        if ($delayMs > 0) {
            usleep($delayMs * 1000);
        }
    }

    $purgeResults = [];
    if ($purge) {
        $purgeResults = purgeLiteSpeed();
        appendLog($logFile, 'PURGE executed');
    }

    $output = [
        'success' => true,
        'detected_ids' => $ids,
        'count_ids' => count($ids),
        'updated_ids' => $updated,
        'checked_no_change_ids' => $checkedNoChange,
        'failed_ids' => array_map(fn($x) => $x['id'], $failed),
        'failed_details' => $failed,
        'count_updated' => count($updated),
        'count_no_change' => count($checkedNoChange),
        'count_failed' => count($failed),
        'force' => $force,
        'marker' => $marker,
        'purge' => $purge,
        'delay_ms' => $delayMs,
        'purge_results' => $purgeResults,
        'log_file' => $logFile,
        'finished_at' => date('Y-m-d H:i:s'),
    ];

    if (isCli()) {
        echo "Updated IDs: " . implode(',', $updated) . PHP_EOL;
        echo "No-change IDs: " . implode(',', $checkedNoChange) . PHP_EOL;
        echo "Failed IDs: " . implode(',', array_map(fn($x) => (string)$x['id'], $failed)) . PHP_EOL;
    } else {
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
    }

    appendLog($logFile, 'DONE pro');

} catch (Throwable $e) {
    appendLog($logFile, 'ERROR ' . $e->getMessage());
    $output = [
        'success' => false,
        'error' => $e->getMessage(),
        'finished_at' => date('Y-m-d H:i:s'),
    ];

    if (isCli()) {
        fwrite(STDERR, '[ERROR] ' . $e->getMessage() . PHP_EOL);
    } else {
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
    }
}
?>