<?php

/**
 * Web migration runner with progress for shared hosting (no SSH).
 *
 * Usage:
 * 1) Upload this file as public/migrate-web.php
 * 2) Set MIGRATE_ONCE_KEY in server .env
 * 3) Open: https://your-domain/migrate-web.php?key=YOUR_KEY
 * 4) Run "next" repeatedly (recommended) or "run all"
 * 5) Delete public/migrate-web.php immediately after finishing
 */

declare(strict_types=1);

@ini_set('display_errors', '1');
@ini_set('display_startup_errors', '1');
error_reporting(E_ALL);

register_shutdown_function(static function (): void {
    $error = error_get_last();
    if (! $error) {
        return;
    }
    $fatalTypes = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR];
    if (! in_array($error['type'] ?? 0, $fatalTypes, true)) {
        return;
    }

    if (! headers_sent()) {
        header('Content-Type: text/plain; charset=utf-8');
        http_response_code(500);
    }
    echo "FATAL ERROR\n";
    echo "Type: " . ($error['type'] ?? 'unknown') . "\n";
    echo "Message: " . ($error['message'] ?? 'unknown') . "\n";
    echo "File: " . ($error['file'] ?? 'unknown') . "\n";
    echo "Line: " . ($error['line'] ?? 'unknown') . "\n";
});

header('Content-Type: text/html; charset=utf-8');

$keyFromUrl = (string) ($_GET['key'] ?? '');
$action = (string) ($_GET['action'] ?? 'status');

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

function render(string $title, string $body, int $statusCode = 200): void
{
    http_response_code($statusCode);
    echo '<!doctype html><html lang="en"><head><meta charset="utf-8"><title>' . h($title) . '</title>';
    echo '<meta name="viewport" content="width=device-width, initial-scale=1">';
    echo '<style>
        body{font-family:Segoe UI,Arial,sans-serif;background:#0b0b0b;color:#f3f3f3;padding:20px;line-height:1.45}
        .card{background:#161616;border:1px solid #2d2d2d;border-radius:10px;padding:16px;margin-bottom:14px}
        .ok{color:#32d74b}.warn{color:#ffcc00}.err{color:#ff453a}
        .row{margin:6px 0}.btn{display:inline-block;padding:10px 14px;border-radius:8px;text-decoration:none;margin-right:8px;margin-bottom:8px}
        .primary{background:#2f6fed;color:#fff}.secondary{background:#2a2a2a;color:#fff}.danger{background:#8b1b1b;color:#fff}
        pre{background:#0f0f0f;border:1px solid #2a2a2a;border-radius:8px;padding:12px;overflow:auto;white-space:pre-wrap}
        code{background:#202020;padding:2px 6px;border-radius:6px}
    </style></head><body>';
    echo '<h2>' . h($title) . '</h2>';
    echo $body;
    echo '</body></html>';
    exit;
}

/**
 * Find Laravel base path by searching upwards for bootstrap/app.php and vendor/autoload.php.
 */
function resolveBaseDir(string $startDir): ?string
{
    $dir = realpath($startDir) ?: $startDir;
    for ($i = 0; $i < 6; $i++) {
        $bootstrap = $dir . DIRECTORY_SEPARATOR . 'bootstrap' . DIRECTORY_SEPARATOR . 'app.php';
        $autoload = $dir . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
        if (is_file($bootstrap) && is_file($autoload)) {
            return $dir;
        }
        $parent = dirname($dir);
        if ($parent === $dir) {
            break;
        }
        $dir = $parent;
    }

    return null;
}

/**
 * Read MIGRATE_ONCE_KEY from .env file path.
 */
function readKeyFromEnvFile(string $envPath): ?string
{
    if (! is_readable($envPath)) {
        return null;
    }
    $lines = file($envPath, FILE_IGNORE_NEW_LINES);
    if ($lines === false) {
        return null;
    }
    foreach ($lines as $line) {
        $line = trim($line);
        if ($line === '' || str_starts_with($line, '#')) {
            continue;
        }
        if (str_starts_with($line, 'MIGRATE_ONCE_KEY=')) {
            return trim(substr($line, strlen('MIGRATE_ONCE_KEY=')), " \t\"'");
        }
    }

    return null;
}

$baseDir = resolveBaseDir(__DIR__);
if (! $baseDir) {
    render('Migration Runner Error', '<div class="card err">Could not resolve Laravel base directory.</div>', 500);
}

require $baseDir . '/vendor/autoload.php';
$app = require_once $baseDir . '/bootstrap/app.php';

/** @var \Illuminate\Contracts\Console\Kernel $kernel */
$kernel = $app->make(\Illuminate\Contracts\Console\Kernel::class);
$kernel->bootstrap();

$expectedKey = (string) (
    getenv('MIGRATE_ONCE_KEY')
    ?: ($_ENV['MIGRATE_ONCE_KEY'] ?? '')
    ?: ($_SERVER['MIGRATE_ONCE_KEY'] ?? '')
);
if ($expectedKey === '') {
    $expectedKey = (string) (readKeyFromEnvFile($baseDir . DIRECTORY_SEPARATOR . '.env') ?? '');
}

if ($expectedKey === '') {
    render(
        'Migration Runner Error',
        '<div class="card err">Set <code>MIGRATE_ONCE_KEY</code> in .env or server environment first.</div>',
        403
    );
}

if (! hash_equals($expectedKey, $keyFromUrl)) {
    render('Migration Runner Error', '<div class="card err">Forbidden (invalid key).</div>', 403);
}

/** @var \Illuminate\Database\Migrations\Migrator $migrator */
$migrator = $app->make('migrator');
$repository = $migrator->getRepository();

$migrationPath = $baseDir . '/database/migrations';
$allFiles = glob($migrationPath . '/*.php') ?: [];
$allMigrations = [];
foreach ($allFiles as $file) {
    $name = pathinfo($file, PATHINFO_FILENAME);
    $allMigrations[$name] = $file;
}
ksort($allMigrations);

$ran = $repository->getRan();
$pending = array_values(array_diff(array_keys($allMigrations), $ran));

$keyParam = urlencode($keyFromUrl);
$statusUrl = '?key=' . $keyParam . '&action=status';
$runNextUrl = '?key=' . $keyParam . '&action=run-next';
$runAllUrl = '?key=' . $keyParam . '&action=run-all';

$resultLog = '';
$statusClass = 'ok';
$statusMessage = 'Ready.';

try {
    if ($action === 'run-next') {
        if (count($pending) === 0) {
            $statusClass = 'warn';
            $statusMessage = 'No pending migrations.';
        } else {
            $next = $pending[0];
            $path = $allMigrations[$next] ?? null;
            if (! $path) {
                throw new RuntimeException("Missing file path for migration: {$next}");
            }

            $statusMessage = 'Running one migration: ' . $next;
            $kernel->call('migrate', [
                '--force' => true,
                '--path' => $path,
                '--realpath' => true,
            ]);
            $resultLog .= $kernel->output();

            $ran = $repository->getRan();
            $pending = array_values(array_diff(array_keys($allMigrations), $ran));
            $statusMessage = 'One migration executed successfully.';
        }
    } elseif ($action === 'run-all') {
        if (count($pending) === 0) {
            $statusClass = 'warn';
            $statusMessage = 'No pending migrations.';
        } else {
            $statusMessage = 'Running all pending migrations...';
            foreach ($pending as $migrationName) {
                $path = $allMigrations[$migrationName] ?? null;
                if (! $path) {
                    throw new RuntimeException("Missing file path for migration: {$migrationName}");
                }

                $resultLog .= "\n=== Running {$migrationName} ===\n";
                $kernel->call('migrate', [
                    '--force' => true,
                    '--path' => $path,
                    '--realpath' => true,
                ]);
                $resultLog .= $kernel->output() . "\n";
            }

            $ran = $repository->getRan();
            $pending = array_values(array_diff(array_keys($allMigrations), $ran));
            $statusMessage = 'Finished run-all action.';
        }
    }
} catch (Throwable $e) {
    $statusClass = 'err';
    $statusMessage = 'Execution failed: ' . $e->getMessage();
    $resultLog .= "\n[EXCEPTION]\n" . $e->getMessage() . "\n\n" . $e->getTraceAsString();
}

$total = count($allMigrations);
$done = $total - count($pending);
$progress = $total > 0 ? round(($done / $total) * 100, 1) : 100;

$pendingHtml = '';
if (count($pending) === 0) {
    $pendingHtml = '<div class="row ok">No pending migrations.</div>';
} else {
    $pendingHtml = '<div class="row warn">Pending migrations (' . count($pending) . '):</div><pre>' . h(implode("\n", $pending)) . '</pre>';
}

$outputHtml = $resultLog !== '' ? '<div class="card"><h3>Command Output</h3><pre>' . h($resultLog) . '</pre></div>' : '';

$body = '';
$body .= '<div class="card">';
$body .= '<div class="row"><strong>Status:</strong> <span class="' . h($statusClass) . '">' . h($statusMessage) . '</span></div>';
$body .= '<div class="row"><strong>Progress:</strong> ' . h((string) $done) . '/' . h((string) $total) . ' (' . h((string) $progress) . '%)</div>';
$body .= '<div class="row"><strong>Action:</strong> ' . h($action) . '</div>';
$body .= '</div>';

$body .= '<div class="card">';
$body .= '<a class="btn secondary" href="' . h($statusUrl) . '">Refresh Status</a>';
$body .= '<a class="btn primary" href="' . h($runNextUrl) . '">Run Next Migration</a>';
$body .= '<a class="btn primary" href="' . h($runAllUrl) . '">Run All Pending</a>';
$body .= '<div class="row warn"><strong>Important:</strong> Delete <code>public/migrate-web.php</code> when done.</div>';
$body .= '</div>';

$body .= '<div class="card"><h3>Pending</h3>' . $pendingHtml . '</div>';
$body .= $outputHtml;

render('Laravel Migration Web Runner', $body);
