<?php

declare(strict_types=1);

namespace PpPhp\Core\Migrate;

/**
 * The final cut-over state, held on the SOURCE (old) server.
 *
 * Flipping does NOT touch the destination — that has already been migrated and
 * verified. It only marks THIS server as retired: a small flag file makes every
 * front-end request show a "this site has moved" notice pointing at the new URL,
 * so visitors and users are guided to the new home while the old data stays intact
 * and untouched. It is fully reversible (unflip deletes the flag), and the owner
 * can always bypass the notice to reach the console — nothing here can lock the
 * owner out of their own server.
 *
 * Why a file and not a DB row: the flag must be readable on the very first line of
 * a request, before the database or session layers are up, and it must survive even
 * if the database has been pointed at the new server. A file under storage/ is the
 * one thing guaranteed to be local and early.
 */
final class MigrationFlip
{
    /** Query flag / cookie that lets the owner slip past the notice for a session. */
    public const BYPASS = 'pp_stay';

    public static function file(): string
    {
        return rtrim(constant('BASE_PATH'), '/') . '/storage/migrated.flag';
    }

    /** Is this server marked as migrated? */
    public static function isEngaged(): bool
    {
        return is_file(self::file());
    }

    /** @return array{newUrl:string,jobId:string,at:string}|null */
    public static function info(): ?array
    {
        if (!self::isEngaged()) { return null; }
        $d = json_decode((string) @file_get_contents(self::file()), true);
        return is_array($d) ? $d : null;
    }

    /** Retire this server, pointing visitors at $newUrl. */
    public static function engage(string $newUrl, string $jobId = ''): bool
    {
        $payload = [
            'newUrl' => rtrim(trim($newUrl), '/'),
            'jobId'  => $jobId,
            'at'     => gmdate('c'),
        ];
        return (bool) @file_put_contents(self::file(),
            json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), LOCK_EX);
    }

    /** Bring this server back into service (undo the flip). */
    public static function revert(): bool
    {
        return !self::isEngaged() || @unlink(self::file());
    }

    /**
     * Should the current request see the "migrated" notice?
     *
     * No when: the flag is absent, the viewer chose to stay (cookie/query), or the
     * viewer is the owner (who needs the console to manage or revert the flip).
     */
    public static function shouldBlock(): bool
    {
        if (!self::isEngaged()) { return false; }
        if (!empty($_COOKIE[self::BYPASS]) || isset($_GET[self::BYPASS])) { return false; }
        // Owner and logged-in staff keep working; only the public is redirected.
        try {
            if (class_exists(\PpPhp\Core\Security::class)
                && (\PpPhp\Core\Security::IsLoggedIn() || \PpPhp\Core\Security::IsOwner())) {
                return false;
            }
        } catch (\Throwable $e) {
            // If auth cannot be resolved this early, err on the side of showing the
            // notice to strangers rather than exposing a retired site.
        }
        return true;
    }

    /** Render the standalone "site has moved" notice and stop the request. */
    public static function renderNotice(): void
    {
        $info = self::info() ?? [];
        $new  = (string) ($info['newUrl'] ?? '');
        $h    = static fn ($s) => htmlspecialchars((string) $s, ENT_QUOTES);
        http_response_code(503);
        header('Retry-After: 3600');
        header('Content-Type: text/html; charset=utf-8');
        $stay = '?' . self::BYPASS . '=1';
        echo '<!doctype html><html lang="en"><head><meta charset="utf-8">'
            . '<meta name="viewport" content="width=device-width,initial-scale=1">'
            . '<title>This site has moved</title><style>'
            . ':root{color-scheme:light dark}'
            . 'body{margin:0;min-height:100vh;display:grid;place-items:center;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;'
            . 'background:#0b1120;color:#e5e7eb}'
            . '.card{max-width:520px;padding:2.4rem;margin:1rem;text-align:center;background:#141824;border:1px solid #242a3a;border-radius:18px}'
            . 'h1{font-size:1.5rem;margin:.2rem 0 .6rem}p{color:#9aa3b2;line-height:1.6}'
            . 'a.btn{display:inline-block;margin-top:1.2rem;padding:.7rem 1.4rem;border-radius:10px;background:linear-gradient(135deg,#4f46e5,#8b5cf6);color:#fff;text-decoration:none;font-weight:700}'
            . '.small{display:block;margin-top:1rem;font-size:.8rem}.small a{color:#818cf8}'
            . '</style></head><body><div class="card">'
            . '<h1>This site has moved</h1>'
            . '<p>We\'ve migrated to a new server. Please continue at our new address.</p>'
            . ($new !== '' ? '<a class="btn" href="' . $h($new) . '">Go to the new site →</a>' : '')
            . '<span class="small"><a href="' . $h($stay) . '">Continue to the old site anyway</a></span>'
            . '</div></body></html>';
        exit;
    }
}
