<?php
require_once __DIR__ . '/../app/helpers/functions.php';
require_once __DIR__ . '/../app/migrations.php';
start_session();

function installer_post(string $key, string $default = ''): string
{
    return trim((string)($_POST[$key] ?? $default));
}

function installer_config_from_post(): array
{
    return [
        'app' => [
            'name' => installer_post('app_name', 'INS Communications Suite'),
            'base_url' => installer_post('app_base_url', ((isset($_SERVER['REQUEST_SCHEME']) ? $_SERVER['REQUEST_SCHEME'] : 'http') . '://' . ($_SERVER['HTTP_HOST'] ?? 'localhost') . rtrim(dirname($_SERVER['SCRIPT_NAME'] ?? '/'), '/'))),
            'timezone' => installer_post('app_timezone', 'America/Chicago'),
            'key' => installer_post('app_key', bin2hex(random_bytes(16))),
        ],
        'db' => [
            'host' => installer_post('db_host', 'localhost'),
            'port' => (int)installer_post('db_port', '3306'),
            'database' => installer_post('db_database', ''),
            'username' => installer_post('db_username', ''),
            'password' => installer_post('db_password', ''),
            'charset' => 'utf8mb4',
        ],
        'twilio' => [
            'sid' => installer_post('twilio_sid', ''),
            'token' => installer_post('twilio_token', ''),
            'from_number' => installer_post('twilio_from_number', ''),
            'validate_signature' => true,
        ],
        'mail' => [
            'from_name' => installer_post('mail_from_name', 'INS Communications'),
            'from_email' => installer_post('mail_from_email', ''),
            'smtp_enabled' => !empty($_POST['mail_smtp_enabled']),
            'smtp_host' => installer_post('mail_smtp_host', ''),
            'smtp_port' => (int)installer_post('mail_smtp_port', '587'),
            'smtp_username' => installer_post('mail_smtp_username', ''),
            'smtp_password' => installer_post('mail_smtp_password', ''),
            'smtp_secure' => installer_post('mail_smtp_secure', 'tls'),
        ],
        'imap' => [
            'host' => installer_post('imap_host', ''),
            'port' => (int)installer_post('imap_port', '993'),
            'flags' => installer_post('imap_flags', '/imap/ssl'),
            'username' => installer_post('imap_username', ''),
            'password' => installer_post('imap_password', ''),
            'mailbox' => installer_post('imap_mailbox', 'INBOX'),
        ],
    ];
}

function installer_write_config(array $config): void
{
    $export = "<?php\nreturn " . var_export($config, true) . ";\n";
    file_put_contents(app_config_path(), $export);
}

function installer_pdo(array $config, bool $withDb = true): PDO
{
    $dsn = 'mysql:host=' . $config['db']['host'] . ';port=' . $config['db']['port'];
    if ($withDb && $config['db']['database'] !== '') {
        $dsn .= ';dbname=' . $config['db']['database'];
    }
    $dsn .= ';charset=' . ($config['db']['charset'] ?? 'utf8mb4');
    return new PDO($dsn, $config['db']['username'], $config['db']['password'], [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    ]);
}

function installer_current_config(): array
{
    if (!app_config_exists()) {
        throw new RuntimeException('The application is not installed yet. Run the installer first.');
    }
    $config = require app_config_path();
    if (!is_array($config) || empty($config['db'])) {
        throw new RuntimeException('Unable to load config/config.php.');
    }
    return $config;
}

function installer_app_root(): string
{
    $root = realpath(__DIR__ . '/..');
    return $root ?: dirname(__DIR__);
}

function installer_storage_dir(string $suffix = ''): string
{
    $path = installer_app_root() . '/storage/updates';
    if ($suffix !== '') {
        $path .= '/' . ltrim($suffix, '/');
    }
    return $path;
}

function installer_mkdir(string $path): void
{
    if (!is_dir($path) && !mkdir($path, 0775, true) && !is_dir($path)) {
        throw new RuntimeException('Unable to create directory: ' . $path);
    }
}

function installer_can_manage_updates(): bool
{
    if (!app_config_exists()) {
        return false;
    }
    $user = current_user();
    if (!$user) {
        return false;
    }
    if (($user['role'] ?? '') === 'admin') {
        return true;
    }
    try {
        return can_access('settings.manage');
    } catch (Throwable $e) {
        return false;
    }
}

function installer_require_update_access(): void
{
    if (!installer_can_manage_updates()) {
        throw new RuntimeException('Sign in as an admin before applying an update package.');
    }
}

function installer_validate_relative_path(string $path): string
{
    $path = trim(str_replace('\\', '/', $path), '/');
    if ($path === '') {
        throw new RuntimeException('Encountered an empty file path in the update package.');
    }
    $segments = explode('/', $path);
    foreach ($segments as $segment) {
        if ($segment === '' || $segment === '.' || $segment === '..') {
            throw new RuntimeException('Unsafe file path in update package: ' . $path);
        }
    }
    return $path;
}

function installer_package_file_list(string $baseDir): array
{
    $filesDir = rtrim($baseDir, '/') . '/files';
    if (!is_dir($filesDir)) {
        throw new RuntimeException('The update package is missing the files directory.');
    }
    $list = [];
    $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($filesDir, FilesystemIterator::SKIP_DOTS));
    foreach ($iterator as $file) {
        if (!$file->isFile()) {
            continue;
        }
        $relative = substr($file->getPathname(), strlen($filesDir) + 1);
        $list[] = installer_validate_relative_path($relative);
    }
    sort($list);
    return $list;
}

function installer_copy_file(string $from, string $to): void
{
    installer_mkdir(dirname($to));
    if (!copy($from, $to)) {
        throw new RuntimeException('Unable to copy file to ' . $to);
    }
}

function installer_remove_tree(string $path): void
{
    if (!is_dir($path)) {
        if (is_file($path)) {
            @unlink($path);
        }
        return;
    }
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS),
        RecursiveIteratorIterator::CHILD_FIRST
    );
    foreach ($iterator as $item) {
        if ($item->isDir()) {
            @rmdir($item->getPathname());
        } else {
            @unlink($item->getPathname());
        }
    }
    @rmdir($path);
}

function installer_connect_current_db(): PDO
{
    $config = installer_current_config();
    return installer_pdo($config, true);
}

function installer_sync_schema_version(PDO $pdo, string $version): void
{
    $stmt = $pdo->prepare('INSERT INTO settings (setting_key, setting_value) VALUES (?, ?) ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), updated_at = NOW()');
    $stmt->execute(['schema_version', $version]);
}

function installer_write_json(string $path, array $payload): void
{
    installer_mkdir(dirname($path));
    $json = json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
    if ($json === false || file_put_contents($path, $json) === false) {
        throw new RuntimeException('Unable to write update metadata to ' . $path);
    }
}

function installer_read_json(string $path): array
{
    if (!is_file($path)) {
        return [];
    }
    $decoded = json_decode((string)file_get_contents($path), true);
    return is_array($decoded) ? $decoded : [];
}

function installer_schema_affecting_files(array $files): bool
{
    foreach ($files as $relative) {
        $relative = installer_validate_relative_path((string)$relative);
        if ($relative === 'sql/schema.sql' || $relative === 'app/migrations.php' || $relative === 'scripts/migrate.php' || str_starts_with($relative, 'sql/')) {
            return true;
        }
    }
    return false;
}

function installer_current_version_from_root(string $root): string
{
    $versionFile = $root . '/VERSION';
    if (is_file($versionFile)) {
        $value = trim((string)file_get_contents($versionFile));
        if ($value !== '') {
            return $value;
        }
    }
    return app_version();
}

function installer_unique_stamp(): string
{
    $stamp = date('Ymd_His');
    $candidate = $stamp;
    $root = installer_storage_dir('backups');
    $suffix = 0;
    while (is_dir($root . '/' . $candidate)) {
        $suffix++;
        $candidate = $stamp . '_' . $suffix;
    }
    return $candidate;
}

function installer_store_backup_record(string $backupPath, array $payload): void
{
    installer_write_json(rtrim($backupPath, '/') . '/backup_record.json', $payload);
}

function installer_manifest_files(array $manifest, string $fallbackDir = ''): array
{
    $files = [];
    foreach (($manifest['files'] ?? []) as $path) {
        $files[] = installer_validate_relative_path((string)$path);
    }
    if (!$files && $fallbackDir !== '') {
        $files = installer_package_file_list($fallbackDir);
    }
    $files = array_values(array_unique($files));
    sort($files);
    return $files;
}

function installer_create_snapshot(array $files, array $meta = []): array
{
    $files = array_values(array_unique(array_map(static fn ($path) => installer_validate_relative_path((string)$path), $files)));
    sort($files);
    if (!$files) {
        throw new RuntimeException('No files were available for snapshot creation.');
    }

    $root = installer_app_root();
    $stamp = installer_unique_stamp();
    $backupPath = installer_storage_dir('backups/' . $stamp);
    $filesRoot = $backupPath . '/files';
    installer_mkdir($filesRoot);

    foreach ($files as $relative) {
        $target = $root . '/' . $relative;
        if (is_file($target)) {
            installer_copy_file($target, $filesRoot . '/' . $relative);
        }
    }

    $manifest = [
        'package_type' => 'aio-update-package',
        'package_name' => (string)($meta['package_name'] ?? 'Rollback Snapshot'),
        'from_version' => (string)($meta['from_version'] ?? installer_current_version_from_root($root)),
        'to_version' => (string)($meta['to_version'] ?? installer_current_version_from_root($root)),
        'files' => $files,
        'notes' => (array)($meta['notes'] ?? []),
    ];
    installer_write_json($backupPath . '/manifest.json', $manifest);
    installer_store_backup_record($backupPath, [
        'type' => (string)($meta['type'] ?? 'snapshot'),
        'label' => (string)($meta['label'] ?? ($manifest['package_name'] ?? 'Rollback Snapshot')),
        'applied_at' => date('c'),
        'from_version' => (string)($manifest['from_version'] ?? 'unknown'),
        'to_version' => (string)($manifest['to_version'] ?? 'unknown'),
        'schema_affecting' => (bool)($meta['schema_affecting'] ?? installer_schema_affecting_files($files)),
        'restored_from' => $meta['restored_from'] ?? null,
        'notes' => (array)($meta['notes'] ?? []),
    ]);

    return [
        'id' => basename($backupPath),
        'path' => $backupPath,
        'files' => $files,
    ];
}

function installer_backup_record(string $dir): ?array
{
    if (!is_dir($dir)) {
        return null;
    }
    $manifestPath = rtrim($dir, '/') . '/manifest.json';
    if (!is_file($manifestPath)) {
        return null;
    }
    $manifest = installer_read_json($manifestPath);
    if (!$manifest) {
        return null;
    }
    $files = installer_manifest_files($manifest, $dir);
    if (!$files) {
        return null;
    }
    $meta = installer_read_json(rtrim($dir, '/') . '/backup_record.json');
    $appliedAt = (string)($meta['applied_at'] ?? '');
    if ($appliedAt === '') {
        $appliedAt = date('c', filemtime($manifestPath) ?: time());
    }
    return [
        'id' => basename($dir),
        'path' => $dir,
        'type' => (string)($meta['type'] ?? 'update'),
        'label' => (string)($meta['label'] ?? ($manifest['package_name'] ?? 'Update Package')),
        'from_version' => (string)($meta['from_version'] ?? ($manifest['from_version'] ?? 'unknown')),
        'to_version' => (string)($meta['to_version'] ?? ($manifest['to_version'] ?? 'unknown')),
        'files' => $files,
        'file_count' => count($files),
        'schema_affecting' => (bool)($meta['schema_affecting'] ?? installer_schema_affecting_files($files)),
        'applied_at' => $appliedAt,
        'notes' => (array)($meta['notes'] ?? ($manifest['notes'] ?? [])),
        'restored_from' => $meta['restored_from'] ?? null,
    ];
}

function installer_list_backups(): array
{
    $root = installer_storage_dir('backups');
    if (!is_dir($root)) {
        return [];
    }
    $records = [];
    foreach (glob($root . '/*', GLOB_ONLYDIR) ?: [] as $dir) {
        $record = installer_backup_record($dir);
        if ($record) {
            $records[] = $record;
        }
    }
    usort($records, static function (array $a, array $b): int {
        return strcmp((string)$b['applied_at'], (string)$a['applied_at']);
    });
    return $records;
}

function installer_find_backup(string $backupId): ?array
{
    $backupId = trim($backupId);
    if ($backupId === '' || preg_match('/^[A-Za-z0-9_-]+$/', $backupId) !== 1) {
        return null;
    }
    return installer_backup_record(installer_storage_dir('backups/' . $backupId));
}

function installer_apply_update_package(array $file): array
{
    installer_require_update_access();
    if (!class_exists('ZipArchive')) {
        throw new RuntimeException('PHP ZipArchive is required for update packages. Ask your host to enable the zip extension, or use the standalone browser updater.');
    }
    if (($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
        throw new RuntimeException('Please choose a valid update package zip file.');
    }

    $tmpRoot = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'aio_update_' . bin2hex(random_bytes(8));
    installer_mkdir($tmpRoot);

    $zip = new ZipArchive();
    if ($zip->open($file['tmp_name']) !== true) {
        installer_remove_tree($tmpRoot);
        throw new RuntimeException('Unable to open the uploaded update package.');
    }
    if (!$zip->extractTo($tmpRoot)) {
        $zip->close();
        installer_remove_tree($tmpRoot);
        throw new RuntimeException('Unable to extract the update package.');
    }
    $zip->close();

    $manifestFile = $tmpRoot . '/manifest.json';
    if (!is_file($manifestFile)) {
        installer_remove_tree($tmpRoot);
        throw new RuntimeException('The update package is missing manifest.json.');
    }
    $manifest = installer_read_json($manifestFile);
    if (!$manifest) {
        installer_remove_tree($tmpRoot);
        throw new RuntimeException('manifest.json could not be read.');
    }
    if (($manifest['package_type'] ?? '') !== 'aio-update-package') {
        installer_remove_tree($tmpRoot);
        throw new RuntimeException('This file is not a valid All-in-One Communication System update package.');
    }

    $files = installer_manifest_files($manifest, $tmpRoot);
    if (!$files) {
        installer_remove_tree($tmpRoot);
        throw new RuntimeException('The update package does not contain any files to apply.');
    }

    $root = installer_app_root();
    $backup = installer_create_snapshot($files, [
        'type' => 'update',
        'label' => (string)($manifest['package_name'] ?? 'Update Package'),
        'package_name' => (string)($manifest['package_name'] ?? 'Update Package'),
        'from_version' => (string)($manifest['from_version'] ?? installer_current_version_from_root($root)),
        'to_version' => (string)($manifest['to_version'] ?? installer_current_version_from_root($root)),
        'notes' => (array)($manifest['notes'] ?? []),
        'schema_affecting' => installer_schema_affecting_files($files),
    ]);

    foreach ($files as $relative) {
        $source = $tmpRoot . '/files/' . $relative;
        if (!is_file($source)) {
            installer_remove_tree($tmpRoot);
            throw new RuntimeException('The update package is missing file: ' . $relative);
        }
    }

    foreach ($files as $relative) {
        $source = $tmpRoot . '/files/' . $relative;
        $target = $root . '/' . $relative;
        installer_copy_file($source, $target);
    }

    $repairStatus = 'Skipped';
    $targetVersion = trim((string)($manifest['to_version'] ?? ''));
    if ($targetVersion === '') {
        $targetVersion = installer_current_version_from_root($root);
    }
    if (app_config_exists()) {
        try {
            $pdo = installer_connect_current_db();
            app_run_migrations($pdo, false);
            if ($targetVersion !== '') {
                installer_sync_schema_version($pdo, $targetVersion);
            }
            $repairStatus = 'Completed';
        } catch (Throwable $e) {
            $repairStatus = 'Applied code, but post-deploy repair failed: ' . $e->getMessage();
        }
    }

    installer_remove_tree($tmpRoot);

    return [
        'mode' => 'update',
        'name' => (string)($manifest['package_name'] ?? 'Update Package'),
        'from_version' => (string)($manifest['from_version'] ?? 'unknown'),
        'to_version' => $targetVersion !== '' ? $targetVersion : (string)($manifest['to_version'] ?? 'unknown'),
        'backup_path' => $backup['path'],
        'file_count' => count($files),
        'repair_status' => $repairStatus,
        'schema_affecting' => installer_schema_affecting_files($files),
        'notes' => (array)($manifest['notes'] ?? []),
    ];
}

function installer_restore_backup(string $backupId): array
{
    installer_require_update_access();
    $record = installer_find_backup($backupId);
    if (!$record) {
        throw new RuntimeException('The selected rollback snapshot could not be found.');
    }

    $root = installer_app_root();
    $currentVersion = installer_current_version_from_root($root);
    $files = $record['files'];
    $snapshot = installer_create_snapshot($files, [
        'type' => 'rollback-snapshot',
        'label' => 'Pre-rollback snapshot before restoring ' . ($record['label'] ?: $record['id']),
        'package_name' => 'Pre-rollback snapshot',
        'from_version' => installer_current_version_from_root($root),
        'to_version' => installer_current_version_from_root($root),
        'notes' => ['Captured automatically before restoring rollback snapshot ' . $record['id'] . '.'],
        'schema_affecting' => (bool)$record['schema_affecting'],
        'restored_from' => $record['id'],
    ]);

    foreach ($files as $relative) {
        $target = $root . '/' . $relative;
        $backupFile = rtrim($record['path'], '/') . '/files/' . $relative;
        if (is_file($backupFile)) {
            installer_copy_file($backupFile, $target);
        } else {
            if (is_dir($target)) {
                installer_remove_tree($target);
            } elseif (is_file($target)) {
                @unlink($target);
            }
        }
    }

    $restoredVersion = trim((string)$record['from_version']);
    if ($restoredVersion === '') {
        $restoredVersion = installer_current_version_from_root($root);
    }

    $repairStatus = 'Skipped';
    if (app_config_exists()) {
        try {
            $pdo = installer_connect_current_db();
            if (!$record['schema_affecting'] && $restoredVersion !== '') {
                installer_sync_schema_version($pdo, $restoredVersion);
                $repairStatus = 'Schema version synchronized to restored code version.';
            } else {
                $repairStatus = 'Code restored. Database rollback is not automatic for schema-affecting releases; current schema was left in place.';
            }
        } catch (Throwable $e) {
            $repairStatus = 'Code restored, but schema version sync failed: ' . $e->getMessage();
        }
    }

    return [
        'mode' => 'rollback',
        'name' => 'Rollback to ' . ($restoredVersion !== '' ? $restoredVersion : $record['from_version']),
        'from_version' => $currentVersion,
        'to_version' => $restoredVersion !== '' ? $restoredVersion : (string)$record['from_version'],
        'backup_path' => $snapshot['path'],
        'restore_source' => $record['path'],
        'file_count' => count($files),
        'repair_status' => $repairStatus,
        'schema_affecting' => (bool)$record['schema_affecting'],
        'notes' => array_merge([
            'Restored from snapshot ' . $record['id'] . '.',
            'A pre-rollback snapshot was captured so you can move forward again if needed.',
        ], (array)$record['notes']),
    ];
}
$notice = '';
$error = '';
$action = $_POST['action'] ?? ($_GET['action'] ?? '');
$updateSummary = null;
$showUpdateSection = app_config_exists();
$updateAllowed = installer_can_manage_updates();
$backups = $showUpdateSection ? installer_list_backups() : [];

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    try {
        if ($action === 'apply_update_package') {
            verify_csrf();
            $updateSummary = installer_apply_update_package($_FILES['update_package'] ?? []);
            $notice = 'Update package applied successfully.';
            $backups = installer_list_backups();
        } elseif ($action === 'restore_backup') {
            verify_csrf();
            $updateSummary = installer_restore_backup((string)($_POST['backup_id'] ?? ''));
            $notice = 'Rollback snapshot restored successfully.';
            $backups = installer_list_backups();
        } else {
            $config = installer_config_from_post();
            if ($config['db']['database'] === '') {
                throw new RuntimeException('Database name is required.');
            }
            if ($action === 'test_db') {
                $pdo = installer_pdo($config, false);
                $pdo->exec('CREATE DATABASE IF NOT EXISTS `' . str_replace('`', '``', $config['db']['database']) . '` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci');
                $pdo = installer_pdo($config, true);
                $notice = 'Database connection succeeded. MySQL version: ' . $pdo->getAttribute(PDO::ATTR_SERVER_VERSION);
            } elseif ($action === 'run_install' || $action === 'repair') {
                $pdo = installer_pdo($config, false);
                $pdo->exec('CREATE DATABASE IF NOT EXISTS `' . str_replace('`', '``', $config['db']['database']) . '` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci');
                $pdo = installer_pdo($config, true);
                app_run_migrations($pdo, true, installer_post('admin_name', 'System Admin'), installer_post('admin_email', 'admin@example.com'), installer_post('admin_password', 'ChangeMe123!'));
                installer_write_config($config);
                @file_put_contents(dirname(app_config_path()) . '/installed.lock', date('c'));
                $notice = $action === 'repair' ? 'Repair completed. Missing tables and columns were added.' : 'Install completed. You can sign in now.';
            }
        }
    } catch (Throwable $e) {
        $error = $e->getMessage();
    }
}
?><!doctype html>
<html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Installer</title>
<style>
body{margin:0;font-family:Arial,sans-serif;background:#11345a;color:#0f172a}
.wrap{max-width:1020px;margin:24px auto;background:#f8fafc;border-radius:22px;padding:28px}
.grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.card{background:#fff;border:1px solid #d8e0ea;border-radius:18px;padding:20px;margin-top:18px}
.field label{display:block;font-size:13px;color:#475569;margin-bottom:6px}
.field input,.field select{width:100%;padding:11px;border:1px solid #cbd5e1;border-radius:10px}
h1{font-size:44px;margin:8px 0}
button{padding:12px 18px;border:0;border-radius:10px;background:#0f4c81;color:#fff;font-weight:700;cursor:pointer;margin-right:10px}
.btn-link{display:inline-block;margin-right:12px;color:#0f4c81;font-weight:700;text-decoration:none}
pre{white-space:pre-wrap;background:#f8fafc;padding:14px;border-radius:12px;border:1px solid #d8e0ea}
.notice{background:#ecfdf5;border:1px solid #a7f3d0;padding:12px;border-radius:12px}
.error{background:#fef2f2;border:1px solid #fecaca;padding:12px;border-radius:12px}
.kicker{font-size:13px;letter-spacing:.12em;color:#64748b;text-transform:uppercase}
.inline-list{margin:8px 0 0;padding-left:18px;color:#475569}
.small{font-size:13px;color:#64748b}
</style></head>
<body><div class="wrap"><div class="kicker">Corporate Messaging Suite</div><h1>Installer, Repair, and Update Center</h1><p>Use this page to test the database, install the app, repair missing tables after an upgrade, or apply a browser-deployable update package.</p>
<?php if ($notice): ?><div class="notice"><?= e($notice) ?></div><?php endif; ?>
<?php if ($error): ?><div class="error"><?= e($error) ?></div><?php endif; ?>
<?php if ($updateSummary): ?>
<div class="card">
    <h3>Update Applied</h3>
    <div class="grid">
        <div><strong><?= ($updateSummary['mode'] ?? 'update') === 'rollback' ? 'Rollback' : 'Package' ?></strong><br><?= e($updateSummary['name']) ?></div>
        <div><strong>Version</strong><br><?= e($updateSummary['from_version']) ?> → <?= e($updateSummary['to_version']) ?></div>
        <div><strong>Files updated</strong><br><?= (int)$updateSummary['file_count'] ?></div>
        <div><strong>Safety Snapshot</strong><br><code><?= e($updateSummary['backup_path']) ?></code></div>
    </div>
    <?php if (!empty($updateSummary['restore_source'])): ?><p style="margin-top:12px"><strong>Restored from:</strong> <code><?= e($updateSummary['restore_source']) ?></code></p><?php endif; ?>
    <p style="margin-top:12px"><strong><?= ($updateSummary['mode'] ?? 'update') === 'rollback' ? 'Rollback status' : 'Post-deploy repair' ?>:</strong> <?= e($updateSummary['repair_status']) ?></p>
    <?php if (!empty($updateSummary['notes'])): ?>
        <ul class="inline-list"><?php foreach ($updateSummary['notes'] as $note): ?><li><?= e((string)$note) ?></li><?php endforeach; ?></ul>
    <?php endif; ?>
</div>
<?php endif; ?>

<?php if ($showUpdateSection): ?>
<div class="card" id="update-packages">
    <div class="kicker">Browser Deployments</div>
    <h3 style="margin-top:6px">Update Packages</h3>
    <?php if ($updateAllowed): ?>
        <p>Upload an update package zip created for the All-in-One Communication System. The updater will back up changed files, apply the package, and run post-deploy repair.</p>
        <form method="post" enctype="multipart/form-data">
            <?= csrf_input() ?>
            <div class="grid">
                <div class="field"><label>Current version</label><input value="<?= e(app_version()) ?>" disabled></div>
                <div class="field"><label>Package requirement</label><input value="ZipArchive / PHP zip extension" disabled></div>
                <div class="field" style="grid-column:1 / span 2"><label>Update package zip</label><input type="file" name="update_package" accept=".zip" required></div>
            </div>
            <div style="margin-top:14px">
                <button name="action" value="apply_update_package">Apply Update Package</button>
                <a class="btn-link" href="#rollback-center">Go to Rollback Center</a>
                <a class="btn-link" href="index.php?route=settings/index">Back to app</a>
            </div>
        </form>
        <p class="small">Backups are stored under <code>storage/updates/backups/</code>. Code-only packages do not overwrite <code>config/config.php</code> or uploads unless a package explicitly includes those paths.</p>
    <?php else: ?>
        <p>Sign in as an admin to apply update packages from the browser.</p>
        <a class="btn-link" href="index.php?route=auth/login">Go to sign in</a>
    <?php endif; ?>
</div>

<div class="card" id="rollback-center">
    <div class="kicker">Version Safety</div>
    <h3 style="margin-top:6px">Rollback Center</h3>
    <?php if ($updateAllowed): ?>
        <p>Restore a previous code snapshot if a newer release causes problems. Rollback restores the saved files and automatically captures a new pre-rollback snapshot first.</p>
        <p class="small"><strong>Important:</strong> this is a code rollback. Database schema changes are not automatically reversed. For schema-affecting releases, rollback will leave the current database in place and warn you instead of pretending the DB was downgraded.</p>
        <?php if ($backups): ?>
            <table>
                <thead><tr><th>Snapshot</th><th>Version</th><th>Files</th><th>Schema</th><th>Captured</th><th></th></tr></thead>
                <tbody>
                <?php foreach ($backups as $backup): ?>
                    <tr>
                        <td>
                            <strong><?= e($backup['label']) ?></strong><br>
                            <span class="small"><?= e($backup['id']) ?></span>
                            <?php if (!empty($backup['restored_from'])): ?><br><span class="small">From restore of <?= e((string)$backup['restored_from']) ?></span><?php endif; ?>
                        </td>
                        <td><?= e($backup['from_version']) ?> → <?= e($backup['to_version']) ?></td>
                        <td><?= (int)$backup['file_count'] ?></td>
                        <td><?= $backup['schema_affecting'] ? 'Schema-affecting' : 'Code-only' ?></td>
                        <td><?= e(date('Y-m-d H:i:s', strtotime((string)$backup['applied_at']))) ?></td>
                        <td>
                            <form method="post" onsubmit="return confirm('Restore this snapshot? A new safety snapshot of the current code will be created first.');">
                                <?= csrf_input() ?>
                                <input type="hidden" name="backup_id" value="<?= e($backup['id']) ?>">
                                <button name="action" value="restore_backup">Restore</button>
                            </form>
                        </td>
                    </tr>
                    <?php if (!empty($backup['notes'])): ?>
                        <tr>
                            <td colspan="6"><span class="small"><?= e(implode(' • ', array_map('strval', (array)$backup['notes']))) ?></span></td>
                        </tr>
                    <?php endif; ?>
                <?php endforeach; ?>
                </tbody>
            </table>
        <?php else: ?>
            <p class="small">No rollback snapshots are available yet. Once you apply an update package through this page, a rollback point will appear here automatically.</p>
        <?php endif; ?>
    <?php else: ?>
        <p>Sign in as an admin to restore saved snapshots.</p>
    <?php endif; ?>
</div>
<?php endif; ?>

<form method="post"><div class="card"><h3>Application</h3><div class="grid">
<div class="field"><label>App name</label><input name="app_name" value="<?= e(installer_post('app_name', 'INS Communications Suite')) ?>"></div>
<div class="field"><label>Base URL</label><input name="app_base_url" value="<?= e(installer_post('app_base_url', ((isset($_SERVER['REQUEST_SCHEME']) ? $_SERVER['REQUEST_SCHEME'] : 'http') . '://' . ($_SERVER['HTTP_HOST'] ?? 'localhost') . rtrim(dirname($_SERVER['SCRIPT_NAME'] ?? '/'), '/')))) ?>"></div>
<div class="field"><label>Timezone</label><input name="app_timezone" value="<?= e(installer_post('app_timezone', 'America/Chicago')) ?>"></div>
<div class="field"><label>App key</label><input name="app_key" value="<?= e(installer_post('app_key', bin2hex(random_bytes(16)))) ?>"></div>
</div></div>
<div class="card"><h3>Database</h3><div class="grid">
<div class="field"><label>Host</label><input name="db_host" value="<?= e(installer_post('db_host', 'localhost')) ?>"></div>
<div class="field"><label>Port</label><input name="db_port" value="<?= e(installer_post('db_port', '3306')) ?>"></div>
<div class="field"><label>Database name</label><input name="db_database" value="<?= e(installer_post('db_database')) ?>"></div>
<div class="field"><label>Database username</label><input name="db_username" value="<?= e(installer_post('db_username')) ?>"></div>
<div class="field"><label>Database password</label><input type="password" name="db_password" value="<?= e(installer_post('db_password')) ?>"></div>
</div></div>
<div class="card"><h3>Admin user</h3><div class="grid">
<div class="field"><label>Name</label><input name="admin_name" value="<?= e(installer_post('admin_name', 'System Admin')) ?>"></div>
<div class="field"><label>Email</label><input name="admin_email" value="<?= e(installer_post('admin_email', 'admin@example.com')) ?>"></div>
<div class="field"><label>Password</label><input type="password" name="admin_password" value="<?= e(installer_post('admin_password', 'ChangeMe123!')) ?>"></div>
</div></div>
<div class="card"><h3>Optional integrations</h3><div class="grid">
<div class="field"><label>Twilio SID</label><input name="twilio_sid" value="<?= e(installer_post('twilio_sid')) ?>"></div>
<div class="field"><label>Twilio token</label><input type="password" name="twilio_token" value="<?= e(installer_post('twilio_token')) ?>"></div>
<div class="field"><label>Twilio from number</label><input name="twilio_from_number" value="<?= e(installer_post('twilio_from_number')) ?>"></div>
<div class="field"><label>Mail from name</label><input name="mail_from_name" value="<?= e(installer_post('mail_from_name', 'INS Communications')) ?>"></div>
<div class="field"><label>Mail from email</label><input name="mail_from_email" value="<?= e(installer_post('mail_from_email')) ?>"></div>
<div class="field"><label>SMTP host</label><input name="mail_smtp_host" value="<?= e(installer_post('mail_smtp_host')) ?>"></div>
<div class="field"><label>SMTP port</label><input name="mail_smtp_port" value="<?= e(installer_post('mail_smtp_port', '587')) ?>"></div>
<div class="field"><label>SMTP username</label><input name="mail_smtp_username" value="<?= e(installer_post('mail_smtp_username')) ?>"></div>
<div class="field"><label>SMTP password</label><input type="password" name="mail_smtp_password" value="<?= e(installer_post('mail_smtp_password')) ?>"></div>
<div class="field"><label>SMTP secure</label><input name="mail_smtp_secure" value="<?= e(installer_post('mail_smtp_secure', 'tls')) ?>"></div>
<div class="field"><label>IMAP host</label><input name="imap_host" value="<?= e(installer_post('imap_host')) ?>"></div>
<div class="field"><label>IMAP port</label><input name="imap_port" value="<?= e(installer_post('imap_port', '993')) ?>"></div>
<div class="field"><label>IMAP flags</label><input name="imap_flags" value="<?= e(installer_post('imap_flags', '/imap/ssl')) ?>"></div>
<div class="field"><label>IMAP username</label><input name="imap_username" value="<?= e(installer_post('imap_username')) ?>"></div>
<div class="field"><label>IMAP password</label><input type="password" name="imap_password" value="<?= e(installer_post('imap_password')) ?>"></div>
<div class="field"><label>IMAP mailbox</label><input name="imap_mailbox" value="<?= e(installer_post('imap_mailbox', 'INBOX')) ?>"></div>
</div></div>
<div style="margin-top:20px">
<button name="action" value="test_db">Test Database Connection</button>
<button name="action" value="repair">Run Repair / Migrations</button>
<button name="action" value="run_install">Run Full Install</button>
<a class="btn-link" href="index.php">Open app</a>
</div></form></div></body></html>
