<?php
declare(strict_types=1);

/**
 * sync_multi_site_ai.php
 *
 * Multi-site AI sync:
 * - totowin88.info-angkajitu.com
 * - betslots88.info-angkajitu.com
 * - kingliga.info-angkajitu.com
 *
 * Syncs:
 * - /client/blog/prediksi/{id}/
 * - /client/blog/result/{id}/
 */

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

$sites = [
    [
        'name' => 'totowin88',
        'brand' => 'Totowin88',
        'base_dir' => '/home/berihdjs/totowin88.info-angkajitu.com',
        'base_url' => 'https://totowin88.info-angkajitu.com',
        'links' => [
            'promo' => 'https://yuklah.me/LuckySpin-Totowin88',
            'rtp' => 'https://yuklah.me/rtp-totowin88',
            'bukti' => 'https://yuklah.me/BuktiJp-Totowin88',
            'apk' => 'https://yuklah.me/APKtotowin88',
            'telegram' => 'https://yuklah.me/Totowin88-Tele',
            'livechat' => 'https://yuklah.me/Totowin88-LC',
            'whatsapp' => 'https://yuklah.me/Totowin88-WA',
            'ina' => 'https://yuklah.me/totowin88',
            ],
            'assets' => [
            'icon' => 'https://imagizer.imageshack.com/img923/7031/Kc4ulM.png',
            'logo' => 'https://imagizer.imageshack.com/img921/2258/mXKOxK.png',
            'banner1' => 'https://buatbanner.com/assets/totowin88/img/banner/promo/totowin88_BonusDeposit10v2.webp',
            'banner2' => 'https://buatbanner.com/assets/totowin88/img/banner/promo/totowin88_antirungkatv2.webp',
            'slider' => 'https://buatbanner.com/assets/totowin88/img/banner/promo/Totowin88_Lucky_Spin_Slider.webp',
        ]
    ],
    [
        'name' => 'betslots88',
        'brand' => 'Betslots88',
        'base_dir' => '/home/berihdjs/betslots88.info-angkajitu.com',
        'base_url' => 'https://betslots88.info-angkajitu.com',
        'links' => [
            'promo' => 'https://yuklah.me/LuckySpin-Betslots88',
            'rtp' => 'https://yuklah.me/rtp-betslots88',
            'bukti' => 'https://yuklah.me/BuktiJp-Betslots88',
            'apk' => 'https://yuklah.me/APKBetslots88',
            'telegram' => 'https://yuklah.me/Tele-Betslots88',
            'livechat' => 'https://yuklah.me/Betslots88-LC',
            'whatsapp' => 'https://yuklah.me/Bts88-Whatsapp',
            'ina' => 'https://yuklah.me/betslots88',
            ],
            'assets' => [
            'icon' => 'https://media.mediatelekomunikasisejahtera.com/template/alpha/desktop/assets/betslots88/img/logo/favicon.webp?v=1.0',
            'logo' => 'https://media.mediatelekomunikasisejahtera.com/template/alpha/desktop/assets/betslots88/img/logo/logo.webp',
            'banner1' => 'https://buatbanner.com/assets/betslots88/img/menu/slider/betslots88_cashback%20vip_Slider2026.webp',
            'banner2' => 'https://buatbanner.com/assets/betslots88/img/menu/slider/betslots88_Anti%20Rungkad_slider.webp',
            'slider' => 'https://buatbanner.com/assets/betslots88/img/menu/slider/betslots88_THR%20Antam%20Series_slider.webp',
        ]
    ],
    [
        'name' => 'kingliga',
        'brand' => 'Kingliga',
        'base_dir' => '/home/berihdjs/kingliga.info-angkajitu.com',
        'base_url' => 'https://kingliga.info-angkajitu.com',
        'links' => [
            'promo' => 'https://yuklah.me/LuckySpin-Kingliga',
            'rtp' => 'https://yuklah.me/rtp-Kingliga',
            'bukti' => 'https://yuklah.me/BuktiJp-Kingliga',
            'apk' => 'https://yuklah.me/APKKingliga',
            'telegram' => 'https://yuklah.me/Tele-Kingliga',
            'livechat' => 'https://yuklah.me/Kingliga-LC',
            'whatsapp' => 'https://yuklah.me/Kingliga-WA',
            'ina' => 'https://yuklah.me/kingliga',
            ],
            'assets' => [
            'icon' => 'https://kingliga-image.idnsp.net/uploads/frontend-site/siteImage/favicon.ico',
            'logo' => 'https://kingliga-image.idnsp.net/uploads/frontend-site/siteImage/logo_full_light.webp',
            'banner1' => 'https://kingliga-image.idnsp.net/uploads/content-management/2025081720530524.webp',
            'banner2' => 'https://kingliga-image.idnsp.net/uploads/content-management/202508200603443442.webp',
            'slider' => 'https://kingliga-image.idnsp.net/uploads/content-management/202508172059191344.webp',
        ]
    ],
];

$sourceBases = [
    'prediksi' => 'https://blog8.inatips.com/client/blog/prediksi/',
    'result'   => 'https://blog8.inatips.com/client/blog/result/',
];

$lockFile = __DIR__ . '/sync_multi_site_ai.lock';
$logFile  = __DIR__ . '/sync_multi_site_ai.log';

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 acquireLock(string $lockFile): array {
    $fp = fopen($lockFile, 'c+');
    if (!$fp) throw new RuntimeException('Tidak bisa membuat lock file.');
    if (!flock($fp, LOCK_EX | LOCK_NB)) {
        $content = stream_get_contents($fp, -1, 0);
        return ['ok' => false, 'handle' => $fp, 'content' => $content ?: 'busy'];
    }
    ftruncate($fp, 0);
    fwrite($fp, json_encode(['pid' => getmypid(), 'started_at' => date('Y-m-d H:i:s')]));
    fflush($fp);
    return ['ok' => true, 'handle' => $fp];
}

function releaseLock($fp, string $lockFile): void {
    if (is_resource($fp)) {
        flock($fp, LOCK_UN);
        fclose($fp);
    }
    @unlink($lockFile);
}

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,
            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);

        if ($response === false || $httpCode >= 400) {
            usleep(200000);
            continue;
        }
        if (stripos((string)$response, 'Index of /') !== false) {
            usleep(200000);
            continue;
        }
        if (stripos((string)$response, '<html') === false) {
            usleep(200000);
            continue;
        }
        return (string)$response;
    }
    throw new RuntimeException("Gagal fetch setelah retry: {$url}" . (isset($error) ? " | {$error}" : ''));
}

function fetchUrlLoose(string $url): array {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_TIMEOUT => 20,
        CURLOPT_SSL_VERIFYPEER => false,
        CURLOPT_SSL_VERIFYHOST => 0,
        CURLOPT_USERAGENT => 'Mozilla/5.0 SyncMultiSite/1.0',
    ]);
    $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,
        'url' => $url,
    ];
}

function purgeSiteCache(string $baseUrl): array {
    $urls = [
        rtrim($baseUrl, '/') . '/?LSCWP_CTRL=flush_all',
        rtrim($baseUrl, '/') . '/?litespeed_purge=all',
    ];
    $results = [];
    foreach ($urls as $url) $results[] = fetchUrlLoose($url);
    return $results;
}

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 detectNumericFolders(string $baseDir): array {
    if (!is_dir($baseDir)) return [];
    $ids = [];
    $items = scandir($baseDir);
    if ($items === false) return [];
    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 buildPlainReplacements(array $site): array {
    $brand = $site['brand'];
    $links = $site['links'] ?? [];
    $assets = array_merge([
        'icon' => '',
        'logo' => '',
        'banner1' => '',
        'banner2' => '',
        'slider' => ''
    ], $site['assets'] ?? []);

    return [
        // branding text
        'Inatogel | Blog Pasaran Terlengkap' => $brand . ' | Blog Pasaran Terlengkap dan Terpercaya',
        'Inatogel prediksi terlengkap' => $brand . ' prediksi terlengkap dan terpercaya',
        'Inatogel result terlengkap' => $brand . ' result terlengkap dan terpercaya',

        // links
        'https://sorty.bio/rtpina/' => $links['rtp'] ?? '',
        'https://sorty.bio/buktiina/' => $links['bukti'] ?? '',
        'https://sorty.bio/Inapromo' => $links['promo'] ?? '',
        'https://sorty.bio/apkina' => $links['apk'] ?? '',
        'https://t.me/inatogel88_official' => $links['telegram'] ?? '',
        'https://secure.livechatinc.com/customer/action/open_chat?license_id=13079895' => $links['livechat'] ?? '',
        'https://api.whatsapp.com/send?phone=6281389114046' => $links['whatsapp'] ?? '',

        // title/description/footer
        'Selamat datang di Inatogel Situs Games Online Terpercaya | Data Result Angka Terlengkap | Prediksi Angka Jitu Online | Paito Warna Terlengkap'
            => 'Selamat datang di ' . $brand . ' Situs Togel dengan Pasaran Terlengkap dan Terpercaya | Result Angka Togel Terlengkap | Prediksi Togel Angka Jitu Online Terpercaya | Paito Warna Terlengkap 2026',
        '2022</span> Inatogel Agen Games Online Terpercaya.</div>'
            => '2026</span> ' . $brand . ' Agen Togel Online Terpercaya.</div>',

        // source absolute urls -> main domain assets
        'https://blog8.inatips.com/assets/'  => 'https://info-angkajitu.com/assets/',
        'https://blog8.inatips.com/build/'   => 'https://info-angkajitu.com/build/',
        'https://blog8.inatips.com/storage/' => 'https://info-angkajitu.com/storage/',
        'https://blog8.inatips.com/client/'  => 'https://info-angkajitu.com/client/',

        // relative html attrs only (AMAN)
        ' src="/assets/'  => ' src="https://info-angkajitu.com/assets/',
        ' href="/assets/' => ' href="https://info-angkajitu.com/assets/',
        ' src="/build/'   => ' src="https://info-angkajitu.com/build/',
        ' href="/build/'  => ' href="https://info-angkajitu.com/build/',
        ' src="/storage/' => ' src="https://info-angkajitu.com/storage/',
        ' href="/storage/' => ' href="https://info-angkajitu.com/storage/',

        // assets per brand
        'https://paito3.acakina.com/assets/frontend/img/logo.png' => $assets['logo'],
        '/storage/gambarGlobal/blog/p0Dvjjy2UkslgvNdY2rG4ogBuUFFslblEfYkFyCA.png' => $assets['icon'],
        '/storage/gambarGlobal/blog/7wEI1zIVYeWdm1NoIVjlOepcXBEDX2h533KFkd9E.png' => $assets['banner1'],
        'https://cdn.harimauterbang.xyz/inatogel/Image/4tiprize1234.jpg' => $assets['banner2'],
        '/storage/gambarGlobal/blog/lrt1E8spjaAA9tGiWuWp5PzVPORyKDJklHN9ReNe.jpg' => $assets['slider'],

        // keep internal page links on current subdomain
        'https://blog8.inatips.com/' => '/',
    ];
}

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

    foreach ($plainReplacements as $from => $to) {
        if ($to === null || $to === '') continue;
        $html = str_replace($from, (string)$to, $html);
    }

    return $html;
}

function applyRegexReplacements(string $html, string $targetLink): string {
    $patterns = [
        '~https://sorty\.bio/ina1(?=[^A-Za-z0-9_]|$)~' => $targetLink,
        '~https://sorty\.bio/ina(?=[^A-Za-z0-9_]|$)~' => $targetLink,
        '~https://yuklah\.me/totowin88[0-9]+~' => $targetLink,
    ];
    foreach ($patterns as $pattern => $replacement) $html = preg_replace($pattern, $replacement, $html);
    return $html;
}

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

function syncOne(array $site, string $type, int $id, string $sourceBase, bool $force, bool $marker): array {
    $sourceUrl = rtrim($sourceBase, '/') . '/' . $id . '/';
    $targetRoot = $site['base_dir'] . '/client/blog/' . $type;
    $targetDir = rtrim($targetRoot, 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;

    $html = $normalized;
    $html = applyPlainReplacements($html, buildPlainReplacements($site));
    $html = applyRegexReplacements($html, $site['links']['ina']);

    if ($marker) $html = injectMarker($html, $site['name'], $type, $id);
    if ($force) $html .= "\n<!-- force-write: " . date('YmdHis') . ' -->';

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

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

    $report = [
        'success' => true,
        'site' => $site['name'],
        'type' => $type,
        'id' => $id,
        'source_url' => $sourceUrl,
        'target_file' => $targetFile,
        'changed' => $changed,
        '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;
}

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

    $lock = acquireLock($lockFile);
    if (!$lock['ok']) {
        $busy = ['success' => false, 'error' => 'sync multi-site masih berjalan', 'lock_info' => $lock['content']];
        if (isCli()) fwrite(STDERR, json_encode($busy, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . PHP_EOL);
        else {
            header('Content-Type: application/json; charset=utf-8');
            echo json_encode($busy, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        }
        exit;
    }

    appendLog($logFile, 'START MULTI-SITE');

    $summary = [];
    foreach ($sites as $site) {
        $siteSummary = [
            'site' => $site['name'],
            'prediksi' => ['updated' => [], 'no_change' => [], 'failed' => []],
            'result'   => ['updated' => [], 'no_change' => [], 'failed' => []],
            'purge_results' => [],
        ];

        foreach (['prediksi', 'result'] as $type) {
            $targetRoot = $site['base_dir'] . '/client/blog/' . $type;
            $ids = detectNumericFolders($targetRoot);
            appendLog($logFile, 'SITE ' . $site['name'] . ' | TYPE ' . $type . ' | IDS ' . implode(',', $ids));

            foreach ($ids as $id) {
                try {
                    $row = syncOne($site, $type, $id, $sourceBases[$type], $force, $marker);
                    if ($row['changed']) $siteSummary[$type]['updated'][] = $id;
                    else $siteSummary[$type]['no_change'][] = $id;
                    appendLog($logFile, $site['name'] . ' | ' . $type . ' | ID ' . $id . ' | changed=' . ($row['changed'] ? 'yes' : 'no'));
                } catch (Throwable $e) {
                    $siteSummary[$type]['failed'][] = ['id' => $id, 'error' => $e->getMessage()];
                    appendLog($logFile, $site['name'] . ' | ' . $type . ' | ID ' . $id . ' | ERROR ' . $e->getMessage());
                }
                if ($delayMs > 0) usleep($delayMs * 1000);
            }
        }

        if ($purge) {
            $siteSummary['purge_results'] = purgeSiteCache($site['base_url']);
            appendLog($logFile, 'PURGE ' . $site['name']);
        }

        $summary[] = $siteSummary;
    }

    $output = [
        'success' => true,
        'mode' => 'multi_site_ai',
        'sites' => $summary,
        'force' => $force,
        'marker' => $marker,
        'purge' => $purge,
        'delay_ms' => $delayMs,
        'log_file' => $logFile,
        'finished_at' => date('Y-m-d H:i:s'),
    ];

    appendLog($logFile, 'DONE MULTI-SITE');
    if (isCli()) echo json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . PHP_EOL;
    else {
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
    }
} catch (Throwable $e) {
    appendLog($logFile, 'ERROR ' . $e->getMessage());
    $output = [
        'success' => false,
        'error' => $e->getMessage(),
        'log_file' => $logFile,
        'finished_at' => date('Y-m-d H:i:s'),
    ];
    if (isCli()) fwrite(STDERR, json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . PHP_EOL);
    else {
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
    }
} finally {
    if (is_array($lock) && ($lock['ok'] ?? false) && isset($lock['handle'])) releaseLock($lock['handle'], $lockFile);
}
?>