<?php

declare(strict_types=1);

/*
|--------------------------------------------------------------------------
| RUTE28 -> DOMAIN BARU WEB MIGRATOR
|--------------------------------------------------------------------------
| Taruh file ini di DOCUMENT ROOT DOMAIN BARU.
|
| Contoh:
| /home/USERNAME/rutekami28.xyz/domain-switch-migrator.php
|--------------------------------------------------------------------------
*/

const DOMAIN_SWITCH_ACCESS_KEY = 'LJ4MW6SBrV9PkcgLJrrVd53d';
const DOMAIN_SWITCH_SESSION = 'RUTE28DOMAINMIGRATOR';

error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('log_errors', '1');
@set_time_limit(0);
@ini_set('memory_limit', '512M');

session_name(DOMAIN_SWITCH_SESSION);
session_start();

header('Content-Type: text/html; charset=UTF-8');
header('X-Robots-Tag: noindex, nofollow, noarchive');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('X-Content-Type-Options: nosniff');
header('Referrer-Policy: same-origin');

function dsEscape($value): string
{
    return htmlspecialchars(
        (string) $value,
        ENT_QUOTES | ENT_SUBSTITUTE,
        'UTF-8'
    );
}

function dsNormalizePath(string $path): string
{
    $path = str_replace('\\', '/', trim($path));
    $real = realpath($path);

    if ($real !== false) {
        $path = str_replace('\\', '/', $real);
    }

    return rtrim($path, '/');
}

function dsCsrf(): string
{
    if (
        empty($_SESSION['ds_csrf']) ||
        !is_string($_SESSION['ds_csrf'])
    ) {
        $_SESSION['ds_csrf'] =
            bin2hex(random_bytes(32));
    }

    return $_SESSION['ds_csrf'];
}

function dsVerifyCsrf(): void
{
    $posted = (string) (
        $_POST['csrf_token'] ?? ''
    );

    $stored = (string) (
        $_SESSION['ds_csrf'] ?? ''
    );

    if (
        $posted === '' ||
        $stored === '' ||
        !hash_equals($stored, $posted)
    ) {
        throw new RuntimeException(
            'Token keamanan tidak valid. Muat ulang halaman.'
        );
    }
}

function dsFlash(
    string $type,
    string $message
): void {
    $_SESSION['ds_flash'] = [
        'type' => $type,
        'message' => $message,
    ];
}

function dsPullFlash(): ?array
{
    $flash = $_SESSION['ds_flash'] ?? null;
    unset($_SESSION['ds_flash']);

    return is_array($flash)
        ? $flash
        : null;
}

function dsRedirect(): never
{
    header(
        'Location: ' . basename(__FILE__),
        true,
        303
    );

    exit;
}

function dsIsTextFile(string $path): bool
{
    $handle = @fopen($path, 'rb');

    if (!$handle) {
        return false;
    }

    $sample = fread($handle, 8192);
    fclose($handle);

    return
        is_string($sample) &&
        strpos($sample, "\0") === false;
}

function dsShouldSkip(string $relative): bool
{
    $relative =
        '/' .
        trim(
            str_replace('\\', '/', $relative),
            '/'
        ) .
        '/';

    $skip = [
        '/.git/',
        '/.svn/',
        '/.idea/',
        '/.vscode/',
        '/vendor/',
        '/node_modules/',
        '/storage/logs/',
        '/logs/',
        '/cache/',
        '/tmp/',
        '/temp/',
        '/sessions/',
        '/uploads/',
        '/backup/',
        '/backups/',
        '/.domain-switch-backup-',
    ];

    foreach ($skip as $needle) {
        if (str_contains($relative, $needle)) {
            return true;
        }
    }

    return false;
}

function dsRelativePath(
    string $root,
    string $path
): string {
    $root =
        rtrim(dsNormalizePath($root), '/') .
        '/';

    $path = dsNormalizePath($path);

    return str_starts_with($path, $root)
        ? substr($path, strlen($root))
        : basename($path);
}

function dsParseTargetRoots(string $value): array
{
    $lines = preg_split(
        '/\R+/',
        trim($value)
    );

    $roots = [];

    foreach ((array) $lines as $line) {
        $line = dsNormalizePath($line);

        if (
            $line !== '' &&
            is_dir($line)
        ) {
            $roots[$line] = $line;
        }
    }

    return array_values($roots);
}

function dsBuildMap(array $form): array
{
    $oldDomain = strtolower(
        trim(
            (string) $form['old_domain']
        )
    );

    $newDomain = strtolower(
        trim(
            (string) $form['new_domain']
        )
    );

    $oldMainPath = dsNormalizePath(
        (string) $form['old_main_path']
    );

    $newMainPath = dsNormalizePath(
        (string) $form['new_main_path']
    );

    if (
        $oldDomain === '' ||
        $newDomain === '' ||
        $oldMainPath === '' ||
        $newMainPath === ''
    ) {
        throw new RuntimeException(
            'Domain dan path utama wajib diisi.'
        );
    }

    $map = [
        'https://www.' . $oldDomain =>
            'https://www.' . $newDomain,

        'http://www.' . $oldDomain =>
            'http://www.' . $newDomain,

        'https://' . $oldDomain =>
            'https://' . $newDomain,

        'http://' . $oldDomain =>
            'http://' . $newDomain,

        $oldMainPath =>
            $newMainPath,

        $oldDomain =>
            $newDomain,
    ];

    $map = array_filter(
        $map,
        static fn(
            string $to,
            string $from
        ): bool =>
            $from !== '' &&
            $to !== '' &&
            $from !== $to,
        ARRAY_FILTER_USE_BOTH
    );

    uksort(
        $map,
        static fn(
            string $first,
            string $second
        ): int =>
            strlen($second) <=>
            strlen($first)
    );

    return $map;
}

function dsScan(
    array $targetRoots,
    array $map
): array {
    $allowedExtensions = [
        'php',
        'env',
        'ini',
        'htaccess',
        'json',
        'js',
        'css',
        'txt',
        'xml',
        'yaml',
        'yml',
    ];

    $scriptPath =
        dsNormalizePath(__FILE__);

    $changes = [];
    $scanned = 0;
    $occurrences = 0;

    foreach ($targetRoots as $targetRoot) {
        $targetRoot =
            dsNormalizePath($targetRoot);

        $iterator =
            new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator(
                    $targetRoot,
                    FilesystemIterator::SKIP_DOTS
                )
            );

        foreach ($iterator as $fileInfo) {
            if (
                !$fileInfo->isFile() ||
                $fileInfo->isLink()
            ) {
                continue;
            }

            $scanned++;

            if ($scanned > 30000) {
                throw new RuntimeException(
                    'Scan dihentikan karena melebihi 30.000 file.'
                );
            }

            $path = dsNormalizePath(
                $fileInfo->getPathname()
            );

            if ($path === $scriptPath) {
                continue;
            }

            $relative =
                basename($targetRoot) .
                '/' .
                dsRelativePath(
                    $targetRoot,
                    $path
                );

            if (dsShouldSkip($relative)) {
                continue;
            }

            if (
                $fileInfo->getSize() >
                3 * 1024 * 1024
            ) {
                continue;
            }

            $extension = strtolower(
                pathinfo(
                    $path,
                    PATHINFO_EXTENSION
                )
            );

            $basename = strtolower(
                basename($path)
            );

            if (
                !in_array(
                    $extension,
                    $allowedExtensions,
                    true
                ) &&
                !in_array(
                    $basename,
                    [
                        '.env',
                        '.htaccess',
                        'robots.txt',
                    ],
                    true
                )
            ) {
                continue;
            }

            if (!dsIsTextFile($path)) {
                continue;
            }

            $original =
                @file_get_contents($path);

            if (!is_string($original)) {
                continue;
            }

            $updated = $original;
            $matched = [];
            $fileOccurrences = 0;

            foreach ($map as $from => $to) {
                if (
                    $from === '' ||
                    !str_contains(
                        $updated,
                        $from
                    )
                ) {
                    continue;
                }

                $count = substr_count(
                    $updated,
                    $from
                );

                $updated = str_replace(
                    $from,
                    $to,
                    $updated
                );

                $matched[] = [
                    'from' => $from,
                    'to' => $to,
                    'count' => $count,
                ];

                $fileOccurrences += $count;
            }

            if ($updated === $original) {
                continue;
            }

            $changes[] = [
                'target_root' => $targetRoot,
                'path' => $path,
                'relative' => $relative,
                'original' => $original,
                'updated' => $updated,
                'matched' => $matched,
                'occurrences' =>
                    $fileOccurrences,
                'permissions' =>
                    fileperms($path) & 0777,
            ];

            $occurrences +=
                $fileOccurrences;
        }
    }

    return [
        'changes' => $changes,
        'files_scanned' => $scanned,
        'occurrences' => $occurrences,
    ];
}

function dsPreviewHash(
    array $targetRoots,
    array $map,
    array $changes
): string {
    $parts = [
        json_encode(
            array_values($targetRoots),
            JSON_UNESCAPED_SLASHES
        ),
        json_encode(
            $map,
            JSON_UNESCAPED_SLASHES
        ),
    ];

    foreach ($changes as $change) {
        $parts[] =
            $change['path'] .
            ':' .
            hash(
                'sha256',
                $change['original']
            ) .
            ':' .
            hash(
                'sha256',
                $change['updated']
            );
    }

    return hash(
        'sha256',
        implode("\n", $parts)
    );
}

function dsApply(
    string $accountHome,
    array $targetRoots,
    array $map,
    array $scan,
    string $expectedHash
): array {
    $actualHash = dsPreviewHash(
        $targetRoots,
        $map,
        $scan['changes']
    );

    if (
        $expectedHash === '' ||
        !hash_equals(
            $expectedHash,
            $actualHash
        )
    ) {
        throw new RuntimeException(
            'Isi file berubah sejak preview. Jalankan Preview ulang.'
        );
    }

    $backupRoot =
        rtrim($accountHome, '/') .
        '/.domain-switch-backup-' .
        date('Ymd-His');

    if (
        !mkdir(
            $backupRoot . '/files',
            0750,
            true
        )
    ) {
        throw new RuntimeException(
            'Folder backup tidak dapat dibuat: ' .
            $backupRoot
        );
    }

    $manifest = [];

    foreach ($scan['changes'] as $index => $change) {
        $backupFile =
            $backupRoot .
            '/files/' .
            str_pad(
                (string) ($index + 1),
                5,
                '0',
                STR_PAD_LEFT
            ) .
            '-' .
            basename($change['path']);

        if (
            !copy(
                $change['path'],
                $backupFile
            )
        ) {
            throw new RuntimeException(
                'Gagal backup: ' .
                $change['path']
            );
        }

        $manifest[] = [
            'original' =>
                $change['path'],
            'backup' => $backupFile,
            'permissions' =>
                $change['permissions'],
        ];
    }

    file_put_contents(
        $backupRoot . '/manifest.json',
        json_encode(
            $manifest,
            JSON_PRETTY_PRINT |
            JSON_UNESCAPED_SLASHES
        ),
        LOCK_EX
    );

    file_put_contents(
        $backupRoot .
        '/replacement-map.json',
        json_encode(
            $map,
            JSON_PRETTY_PRINT |
            JSON_UNESCAPED_SLASHES
        ),
        LOCK_EX
    );

    $updated = 0;

    foreach ($scan['changes'] as $change) {
        $temporary =
            $change['path'] .
            '.domain-switch-tmp-' .
            bin2hex(random_bytes(4));

        if (
            file_put_contents(
                $temporary,
                $change['updated'],
                LOCK_EX
            ) === false
        ) {
            throw new RuntimeException(
                'Gagal menulis temporary: ' .
                $change['path']
            );
        }

        @chmod(
            $temporary,
            $change['permissions']
        );

        if (
            !rename(
                $temporary,
                $change['path']
            )
        ) {
            @unlink($temporary);

            throw new RuntimeException(
                'Gagal mengganti file: ' .
                $change['path']
            );
        }

        $updated++;
    }

    return [
        'backup_root' => $backupRoot,
        'updated_files' => $updated,
        'occurrences' =>
            $scan['occurrences'],
    ];
}

function dsFindBackups(
    string $accountHome
): array {
    $directories = glob(
        rtrim($accountHome, '/') .
        '/.domain-switch-backup-*',
        GLOB_ONLYDIR
    );

    if (!is_array($directories)) {
        return [];
    }

    rsort($directories);

    return array_values(
        array_filter(
            $directories,
            static fn(string $directory): bool =>
                is_file(
                    $directory .
                    '/manifest.json'
                )
        )
    );
}

function dsRestore(
    string $accountHome,
    string $backupRoot
): int {
    $accountHome =
        dsNormalizePath($accountHome);

    $backupRoot =
        dsNormalizePath($backupRoot);

    if (
        !str_starts_with(
            $backupRoot . '/',
            $accountHome . '/'
        ) ||
        !str_starts_with(
            basename($backupRoot),
            '.domain-switch-backup-'
        )
    ) {
        throw new RuntimeException(
            'Lokasi backup tidak valid.'
        );
    }

    $manifestPath =
        $backupRoot .
        '/manifest.json';

    if (!is_file($manifestPath)) {
        throw new RuntimeException(
            'Manifest backup tidak ditemukan.'
        );
    }

    $manifest = json_decode(
        (string) file_get_contents(
            $manifestPath
        ),
        true
    );

    if (!is_array($manifest)) {
        throw new RuntimeException(
            'Manifest backup tidak valid.'
        );
    }

    $restored = 0;

    foreach ($manifest as $row) {
        $original = dsNormalizePath(
            (string) (
                $row['original'] ?? ''
            )
        );

        $backup = dsNormalizePath(
            (string) (
                $row['backup'] ?? ''
            )
        );

        if (
            !str_starts_with(
                $original . '/',
                $accountHome . '/'
            ) ||
            !str_starts_with(
                $backup . '/',
                $backupRoot . '/'
            ) ||
            !is_file($backup)
        ) {
            continue;
        }

        if (!copy($backup, $original)) {
            throw new RuntimeException(
                'Gagal restore: ' .
                $original
            );
        }

        if (
            isset($row['permissions'])
        ) {
            @chmod(
                $original,
                (int) $row[
                    'permissions'
                ]
            );
        }

        $restored++;
    }

    return $restored;
}

/*
|--------------------------------------------------------------------------
| Login
|--------------------------------------------------------------------------
*/

$clientIp = (string) (
    $_SERVER['REMOTE_ADDR'] ?? ''
);

if (
    isset($_POST['action']) &&
    $_POST['action'] === 'login'
) {
    $key = trim(
        (string) (
            $_POST['access_key'] ?? ''
        )
    );

    if (
        !hash_equals(
            DOMAIN_SWITCH_ACCESS_KEY,
            $key
        )
    ) {
        sleep(2);

        dsFlash(
            'error',
            'Access key salah.'
        );

        dsRedirect();
    }

    $_SESSION['ds_logged_in'] = true;
    $_SESSION['ds_ip'] = $clientIp;

    dsFlash(
        'success',
        'Login berhasil.'
    );

    dsRedirect();
}

$loggedIn =
    !empty(
        $_SESSION['ds_logged_in']
    ) &&
    isset($_SESSION['ds_ip']) &&
    hash_equals(
        (string) $_SESSION['ds_ip'],
        $clientIp
    );

if (
    isset($_GET['logout']) &&
    $_GET['logout'] === '1'
) {
    $_SESSION = [];
    session_destroy();
    dsRedirect();
}

$flash = dsPullFlash();

if (!$loggedIn) {
    ?>
<!doctype html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Domain Switch Migrator</title>
<style>
*{box-sizing:border-box}
body{
    margin:0;
    min-height:100vh;
    display:grid;
    place-items:center;
    padding:18px;
    color:#fff;
    background:#07111f;
    font:14px Arial,sans-serif
}
.box{
    width:min(420px,100%);
    padding:22px;
    border:1px solid #29537d;
    border-radius:14px;
    background:#0c2038
}
h1{margin:0;color:#ffd24d}
p{color:#a9bdd3;line-height:1.6}
input,button{
    width:100%;
    min-height:43px;
    margin-top:10px;
    border-radius:9px;
    font:inherit
}
input{
    padding:0 12px;
    border:1px solid #35658f;
    color:#fff;
    background:#071525
}
button{
    border:1px solid #ffe382;
    color:#2b1600;
    background:linear-gradient(#ffe987,#f3ab16);
    font-weight:900;
    cursor:pointer
}
.alert{
    margin-top:12px;
    padding:10px;
    border-radius:8px;
    background:#5b1218;
    color:#ffd4d4
}
</style>
</head>
<body>
<form class="box" method="post">
<h1>Domain Switch Migrator</h1>
<p>
Masukkan access key dari README.
Login dikunci ke IP browser ini.
</p>

<?php if (is_array($flash)): ?>
<div class="alert">
<?= dsEscape(
    $flash['message'] ?? ''
); ?>
</div>
<?php endif; ?>

<input
    type="password"
    name="access_key"
    placeholder="Access key"
    required
>
<input
    type="hidden"
    name="action"
    value="login"
>
<button type="submit">MASUK</button>
</form>
</body>
</html>
    <?php
    exit;
}

/*
|--------------------------------------------------------------------------
| Default berdasarkan lokasi file
|--------------------------------------------------------------------------
*/

$newMainPath = dsNormalizePath(__DIR__);
$accountHome = dsNormalizePath(
    dirname(__DIR__)
);

$currentHost = strtolower(
    preg_replace(
        '/:[0-9]+$/',
        '',
        (string) (
            $_SERVER['HTTP_HOST'] ??
            'rutekami28.xyz'
        )
    )
);

$defaults = [
    'account_home' => $accountHome,
    'old_domain' => 'rute-28.xyz',
    'new_domain' => $currentHost,
    'old_main_path' =>
        $accountHome . '/public_html',
    'new_main_path' =>
        $newMainPath,
    'target_roots' =>
        $newMainPath .
        "\n" .
        $accountHome .
        '/backoffice_app' .
        "\n" .
        $accountHome .
        '/rute28-adm.xyz',
];

$form = $defaults;

foreach (
    array_keys($defaults)
    as $field
) {
    if (isset($_POST[$field])) {
        $form[$field] = trim(
            (string) $_POST[$field]
        );
    } elseif (
        isset(
            $_SESSION[
                'ds_form'
            ][$field]
        )
    ) {
        $form[$field] =
            (string) $_SESSION[
                'ds_form'
            ][$field];
    }
}

$preview = null;
$previewHash = '';
$pageError = '';

try {
    if (
        $_SERVER['REQUEST_METHOD'] ===
        'POST'
    ) {
        dsVerifyCsrf();

        $action = (string) (
            $_POST['action'] ?? ''
        );

        if (
            in_array(
                $action,
                ['preview', 'apply'],
                true
            )
        ) {
            $accountHome = dsNormalizePath(
                $form['account_home']
            );

            if (!is_dir($accountHome)) {
                throw new RuntimeException(
                    'Account home tidak ditemukan.'
                );
            }

            $targetRoots = dsParseTargetRoots(
                $form['target_roots']
            );

            if (empty($targetRoots)) {
                throw new RuntimeException(
                    'Tidak ada target folder valid.'
                );
            }

            foreach ($targetRoots as $targetRoot) {
                if (
                    !str_starts_with(
                        $targetRoot . '/',
                        $accountHome . '/'
                    )
                ) {
                    throw new RuntimeException(
                        'Target folder harus berada di dalam account home: ' .
                        $targetRoot
                    );
                }
            }

            $_SESSION['ds_form'] = $form;

            $map = dsBuildMap($form);

            $preview = dsScan(
                $targetRoots,
                $map
            );

            $previewHash =
                dsPreviewHash(
                    $targetRoots,
                    $map,
                    $preview['changes']
                );

            if ($action === 'apply') {
                $confirm = strtoupper(
                    trim(
                        (string) (
                            $_POST[
                                'confirm_apply'
                            ] ?? ''
                        )
                    )
                );

                if (
                    $confirm !==
                    'GANTI DOMAIN'
                ) {
                    throw new RuntimeException(
                        'Ketik GANTI DOMAIN pada kotak konfirmasi.'
                    );
                }

                $result = dsApply(
                    $accountHome,
                    $targetRoots,
                    $map,
                    $preview,
                    (string) (
                        $_POST[
                            'preview_hash'
                        ] ?? ''
                    )
                );

                dsFlash(
                    'success',
                    'Berhasil memperbarui ' .
                    $result[
                        'updated_files'
                    ] .
                    ' file dan ' .
                    $result[
                        'occurrences'
                    ] .
                    ' replacement. Backup: ' .
                    $result[
                        'backup_root'
                    ]
                );

                dsRedirect();
            }
        }

        if ($action === 'restore') {
            $accountHome = dsNormalizePath(
                $form['account_home']
            );

            $confirm = strtoupper(
                trim(
                    (string) (
                        $_POST[
                            'confirm_restore'
                        ] ?? ''
                    )
                )
            );

            if ($confirm !== 'RESTORE') {
                throw new RuntimeException(
                    'Ketik RESTORE untuk rollback.'
                );
            }

            $restored = dsRestore(
                $accountHome,
                (string) (
                    $_POST[
                        'backup_root'
                    ] ?? ''
                )
            );

            dsFlash(
                'success',
                'Restore selesai. ' .
                $restored .
                ' file dikembalikan.'
            );

            dsRedirect();
        }
    }
} catch (Throwable $exception) {
    $pageError =
        $exception->getMessage();
}

$backups = dsFindBackups(
    dsNormalizePath(
        $form['account_home']
    )
);

?>
<!doctype html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Domain Switch Migrator</title>
<style>
*{box-sizing:border-box}
body{
    margin:0;
    color:#e8f1fb;
    background:#07111f;
    font:14px/1.55 Arial,sans-serif
}
.wrap{
    width:min(1180px,100%);
    margin:auto;
    padding:18px
}
h1{margin:0;color:#ffd24d}
h2{margin:0 0 12px}
.lead{color:#9fb4ca}
.panel{
    margin-top:14px;
    padding:15px;
    border:1px solid #28517b;
    border-radius:12px;
    background:#0c2038
}
.alert{
    margin-top:14px;
    padding:12px;
    border-radius:9px
}
.success{
    color:#a6f2ca;
    border:1px solid #1c7651;
    background:#073d2b
}
.error{
    color:#ffd0d0;
    border:1px solid #8f2c35;
    background:#531319
}
.grid{
    display:grid;
    grid-template-columns:
        repeat(2,minmax(0,1fr));
    gap:11px
}
label{
    display:block;
    color:#91d6ff;
    font-size:12px;
    font-weight:700
}
input,textarea{
    width:100%;
    margin-top:5px;
    padding:10px 11px;
    border:1px solid #35658f;
    border-radius:8px;
    color:#fff;
    background:#071525;
    font:inherit
}
input{min-height:41px}
textarea{
    min-height:120px;
    resize:vertical
}
.actions{
    display:flex;
    flex-wrap:wrap;
    gap:8px;
    margin-top:14px
}
button,.button{
    display:inline-flex;
    min-height:40px;
    align-items:center;
    justify-content:center;
    padding:0 14px;
    border:1px solid #ffe382;
    border-radius:8px;
    color:#2b1600;
    background:linear-gradient(#ffe987,#f3ab16);
    font:inherit;
    font-weight:900;
    text-decoration:none;
    cursor:pointer
}
.secondary{
    color:#e3effc;
    border-color:#35658f;
    background:#12385c
}
.danger{
    color:#fff;
    border-color:#e9656d;
    background:#8b1e27
}
.table-wrap{
    overflow:auto;
    border:1px solid #244b70;
    border-radius:9px
}
table{
    width:100%;
    min-width:820px;
    border-collapse:collapse
}
th,td{
    padding:9px;
    border-bottom:1px solid #24415f;
    text-align:left;
    vertical-align:top
}
th{
    color:#91d6ff;
    background:#082653
}
code{
    color:#ffd24d;
    font-family:Consolas,monospace;
    overflow-wrap:anywhere
}
small{color:#829ab3}
.badge{
    display:inline-flex;
    padding:3px 7px;
    border-radius:999px;
    color:#ffd97b;
    background:#5f4308;
    font-size:11px;
    font-weight:700
}
.top{
    display:flex;
    align-items:flex-start;
    justify-content:space-between;
    gap:10px
}
@media(max-width:760px){
    .grid{grid-template-columns:1fr}
    .wrap{padding:10px}
    .top{display:block}
}
</style>
</head>
<body>
<main class="wrap">
<div class="top">
<div>
<h1>Domain Switch Migrator</h1>
<p class="lead">
Khusus perpindahan dari public_html ke folder domain baru,
tanpa mengubah folder backoffice_app dan rute28-adm.xyz.
</p>
</div>
<a class="button secondary" href="?logout=1">
KELUAR
</a>
</div>

<?php if (is_array($flash)): ?>
<div class="alert <?= dsEscape(
    ($flash['type'] ?? '') ===
        'success'
        ? 'success'
        : 'error'
); ?>">
<?= dsEscape(
    $flash['message'] ?? ''
); ?>
</div>
<?php endif; ?>

<?php if ($pageError !== ''): ?>
<div class="alert error">
<?= dsEscape($pageError); ?>
</div>
<?php endif; ?>

<section class="panel">
<h2>Konfigurasi</h2>

<form method="post">
<input
    type="hidden"
    name="csrf_token"
    value="<?= dsEscape(dsCsrf()); ?>"
>

<div class="grid">
<label>
Account home
<input
    name="account_home"
    value="<?= dsEscape(
        $form['account_home']
    ); ?>"
    required
>
</label>

<label>
Domain lama
<input
    name="old_domain"
    value="<?= dsEscape(
        $form['old_domain']
    ); ?>"
    required
>
</label>

<label>
Domain baru
<input
    name="new_domain"
    value="<?= dsEscape(
        $form['new_domain']
    ); ?>"
    required
>
</label>

<label>
Path website lama
<input
    name="old_main_path"
    value="<?= dsEscape(
        $form['old_main_path']
    ); ?>"
    required
>
</label>

<label>
Path website baru
<input
    name="new_main_path"
    value="<?= dsEscape(
        $form['new_main_path']
    ); ?>"
    required
>
</label>
</div>

<label style="margin-top:11px">
Folder yang dipindai — satu folder per baris
<textarea
    name="target_roots"
    required
><?= dsEscape(
    $form['target_roots']
); ?></textarea>
</label>

<div class="actions">
<button
    type="submit"
    name="action"
    value="preview"
>
PREVIEW PERUBAHAN
</button>
</div>
</form>
</section>

<?php if (is_array($preview)): ?>
<section class="panel">
<h2>Hasil Preview</h2>

<p>
<span class="badge">
<?= count(
    $preview['changes']
); ?> file
</span>

<span class="badge">
<?= (int) $preview[
    'occurrences'
]; ?> replacement
</span>

<span class="badge">
<?= (int) $preview[
    'files_scanned'
]; ?> file dipindai
</span>
</p>

<?php if (empty($preview['changes'])): ?>
<p>Tidak ada hardcode lama ditemukan.</p>
<?php else: ?>

<div class="table-wrap">
<table>
<thead>
<tr>
<th>File</th>
<th>Jumlah</th>
<th>Mapping</th>
</tr>
</thead>
<tbody>
<?php foreach (
    $preview['changes']
    as $change
): ?>
<tr>
<td>
<code>
<?= dsEscape(
    $change['relative']
); ?>
</code>
</td>
<td>
<?= (int) $change[
    'occurrences'
]; ?>
</td>
<td>
<?php foreach (
    $change['matched']
    as $matched
): ?>
<div>
<small>
<?= (int) $matched['count']; ?>×
</small>
<code>
<?= dsEscape(
    $matched['from']
); ?>
</code>
→
<code>
<?= dsEscape(
    $matched['to']
); ?>
</code>
</div>
<?php endforeach; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>

<form method="post" style="margin-top:14px">
<input
    type="hidden"
    name="csrf_token"
    value="<?= dsEscape(dsCsrf()); ?>"
>
<?php foreach (
    $form as $field => $value
): ?>
<input
    type="hidden"
    name="<?= dsEscape($field); ?>"
    value="<?= dsEscape($value); ?>"
>
<?php endforeach; ?>

<input
    type="hidden"
    name="preview_hash"
    value="<?= dsEscape(
        $previewHash
    ); ?>"
>

<label>
Ketik <strong>GANTI DOMAIN</strong>
<input
    name="confirm_apply"
    autocomplete="off"
    required
>
</label>

<div class="actions">
<button
    class="danger"
    type="submit"
    name="action"
    value="apply"
>
TERAPKAN DAN BUAT BACKUP
</button>
</div>
</form>
<?php endif; ?>
</section>
<?php endif; ?>

<section class="panel">
<h2>Backup dan Rollback</h2>

<?php if (empty($backups)): ?>
<p>Belum ada backup perpindahan domain.</p>
<?php else: ?>

<?php foreach ($backups as $backup): ?>
<form method="post" style="
    margin-top:10px;
    padding:12px;
    border:1px solid #28517b;
    border-radius:9px;
">
<input
    type="hidden"
    name="csrf_token"
    value="<?= dsEscape(dsCsrf()); ?>"
>
<?php foreach (
    $form as $field => $value
): ?>
<input
    type="hidden"
    name="<?= dsEscape($field); ?>"
    value="<?= dsEscape($value); ?>"
>
<?php endforeach; ?>

<input
    type="hidden"
    name="backup_root"
    value="<?= dsEscape($backup); ?>"
>

<code><?= dsEscape($backup); ?></code>

<label style="margin-top:10px">
Ketik <strong>RESTORE</strong>
<input
    name="confirm_restore"
    autocomplete="off"
    required
>
</label>

<div class="actions">
<button
    class="secondary"
    type="submit"
    name="action"
    value="restore"
>
ROLLBACK BACKUP INI
</button>
</div>
</form>
<?php endforeach; ?>
<?php endif; ?>
</section>

<section class="panel">
<h2>Yang Tidak Diubah</h2>
<p>
Nama folder <code>backoffice_app</code> dan
<code>rute28-adm.xyz</code> tetap. Migrator hanya mengganti
referensi domain utama dan path website utama di dalam file.
</p>
<p>
Setelah selesai, periksa konfigurasi database, callback API,
cookie domain, dan hapus file migrator ini.
</p>
</section>
</main>
</body>
</html>
