<?php

declare(strict_types=1);

require DENTAL_ROOT . '/app/bootstrap.php';
require_login();
require_roles('super_admin');
require DENTAL_ROOT . '/app/layout.php';
require_once DENTAL_ROOT . '/app/Services/SystemBackupService.php';
require_once DENTAL_ROOT . '/app/production_readiness.php';
require_once DENTAL_ROOT . '/app/release_candidate.php';

$pdo = db();
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Pragma: no-cache');

function backup_center_table_ready(PDO $pdo): bool
{
    try {
        return schema_table_exists(table('backup_logs'));
    } catch (Throwable) {
        return false;
    }
}

function backup_center_column_ready(string $column): bool
{
    try {
        return schema_column_exists(table('backup_logs'), $column);
    } catch (Throwable) {
        return false;
    }
}

function backup_center_size(int $bytes): string
{
    if ($bytes >= 1073741824) return number_format($bytes / 1073741824, 2) . ' GB';
    if ($bytes >= 1048576) return number_format($bytes / 1048576, 2) . ' MB';
    if ($bytes >= 1024) return number_format($bytes / 1024, 1) . ' KB';
    return (string) $bytes . ' B';
}

function backup_center_rows(PDO $pdo): array
{
    if (!backup_center_table_ready($pdo)) return [];
    try {
        $columns = ['b.*'];
        $join = '';
        if (schema_table_exists(table('users'))) {
            $join = ' LEFT JOIN `' . table('users') . '` u ON u.id=b.created_by ';
            $columns[] = 'u.name creator_name';
            $columns[] = 'u.email creator_email';
        }
        return $pdo->query('SELECT ' . implode(',', $columns) . ' FROM `' . table('backup_logs') . '` b ' . $join . ' ORDER BY b.created_at DESC,b.id DESC LIMIT 100')->fetchAll() ?: [];
    } catch (Throwable $exception) {
        error_log('Backup center list failed: ' . $exception->getMessage());
        return [];
    }
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $action = (string) ($_POST['action'] ?? '');
    $returnTarget = $action === 'mark_restore_tested' ? 'backup_restore.php#restore-drill' : 'backup_restore.php';
    verify_csrf_or_redirect($returnTarget, 'backup or restore request');
    try {
        if (!backup_center_table_ready($pdo)) {
            throw new RuntimeException('Backup tables are not ready. Apply all pending updates first.');
        }
        if ($action === 'create_backup') {
            $type = (string) ($_POST['backup_type'] ?? 'complete');
            $password = trim((string) ($_POST['backup_password'] ?? ''));
            $result = (new SystemBackupService($pdo))->create($type, $password !== '' ? $password : null);
            setting_set('system','last_backup_created_at',date('Y-m-d H:i:s'));
            setting_set('system','backup_restore_center_reviewed','1');
            flash('success', ucfirst($type) . ' backup created: ' . (string) $result['file_name'] . '. Click Verify before using it as production recovery evidence.');
            header('Location: backup_restore.php'); exit;
        }
        if ($action === 'verify_backup') {
            $backupId = (int) ($_POST['backup_id'] ?? 0);
            $stmt = $pdo->prepare('SELECT * FROM `' . table('backup_logs') . '` WHERE id=? LIMIT 1');
            $stmt->execute([$backupId]);
            $backup = $stmt->fetch();
            if (!$backup) throw new RuntimeException('Backup record not found.');
            (new SystemBackupService($pdo))->verify($backup);
            setting_set('system','last_backup_verified_at',date('Y-m-d H:i:s'));
            flash('success','Backup verified. The checksum and package structure are readable.');
            header('Location: backup_restore.php'); exit;
        }
        if ($action === 'delete_backup') {
            $backupId = (int) ($_POST['backup_id'] ?? 0);
            if ((string)($_POST['confirm_delete'] ?? '') !== 'DELETE') throw new RuntimeException('Type DELETE to remove the backup package.');
            $stmt = $pdo->prepare('SELECT * FROM `' . table('backup_logs') . '` WHERE id=? LIMIT 1');
            $stmt->execute([$backupId]);
            $backup = $stmt->fetch();
            if (!$backup) throw new RuntimeException('Backup record not found.');
            $file = basename((string)($backup['file_name'] ?? ''));
            if ($file !== '') @unlink(backup_directory() . '/' . $file);
            $pdo->prepare('DELETE FROM `' . table('backup_logs') . '` WHERE id=?')->execute([$backupId]);
            audit('backup_deleted','backup',$backupId,['file'=>$file]);
            flash('success','Backup record removed.');
            header('Location: backup_restore.php'); exit;
        }
        if ($action === 'mark_restore_tested') {
            $record = dental_release_candidate_record_restore_test(
                $pdo,
                (int) ($_POST['backup_id'] ?? 0),
                trim((string) ($_POST['restore_environment'] ?? '')),
                (string) ($_POST['confirmation'] ?? ''),
                (string) ($_POST['restore_note'] ?? '')
            );
            audit_required('restore_test_marked','backup',(int)($record['backup_id'] ?? 0),[
                'version'=>(string)($record['version'] ?? ''),
                'environment'=>(string)($record['environment'] ?? ''),
                'evidence_id'=>(string)($record['evidence_id'] ?? ''),
            ]);
            flash('success','Version-bound restore evidence recorded against the verified complete backup.');
            header('Location: backup_restore.php'); exit;
        }
        if ($action === 'upload_backup') {
            if (!isset($_FILES['backup_file']) || $_FILES['backup_file']['error'] !== UPLOAD_ERR_OK) {
                throw new RuntimeException('Choose a SQL, GZ, or ZIP backup file.');
            }
            $file = $_FILES['backup_file'];
            $original = basename((string) $file['name']);
            $extension = strtolower(pathinfo($original, PATHINFO_EXTENSION));
            if (!in_array($extension, ['sql','gz','zip'], true)) throw new RuntimeException('Only .sql, .gz, and .zip backup files are accepted.');
            if ((int)$file['size'] <= 0 || (int)$file['size'] > 200 * 1024 * 1024) throw new RuntimeException('Backup file must be between 1 byte and 200 MB.');
            $fileName = 'uploaded_' . date('Ymd_His') . '_' . bin2hex(random_bytes(4)) . '.' . $extension;
            $path = backup_directory() . '/' . $fileName;
            if (!move_uploaded_file((string)$file['tmp_name'], $path)) throw new RuntimeException('The backup could not be stored. Check storage/backups permissions.');
            @chmod($path, 0640);
            $size = (int)(filesize($path) ?: 0);
            $checksum = hash_file('sha256',$path) ?: '';
            $type = $extension === 'zip' ? 'complete' : 'database';
            $pdo->prepare('INSERT INTO `' . table('backup_logs') . '` (file_name,file_size,status,created_by,notes,created_at,backup_type,checksum_sha256,file_count,is_encrypted) VALUES (?,? ,"uploaded",?,?,NOW(),?,?,0,0)')
                ->execute([$fileName,$size,(int)(current_user()['id'] ?? 0),'Uploaded backup: ' . $original,$type,$checksum]);
            audit('backup_uploaded','backup',null,['file'=>$fileName,'original'=>$original,'size'=>$size]);
            flash('success','Backup uploaded securely. Uploading does not automatically restore the system.');
            header('Location: backup_restore.php'); exit;
        }
    } catch (Throwable $exception) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        flash('error',$exception->getMessage());
        header('Location: backup_restore.php'); exit;
    }
}

$rows = backup_center_rows($pdo);
$latest = $rows[0] ?? null;
$requiredSince = dental_release_candidate_required_since();
$strictBackupEvidence = dental_production_backup_evidence($pdo, $requiredSince, true);
$eligibleBackup = (array)($strictBackupEvidence['eligible'] ?? []);
$latestVerified = !empty($strictBackupEvidence['ready']);
$eligibleRestoreBackups = [];
foreach ($rows as $backupRow) {
    $evaluation = dental_production_backup_row_evaluation($backupRow, $requiredSince, true);
    if (!empty($evaluation['ready'])) $eligibleRestoreBackups[] = $backupRow;
}
$restoreEvidence = dental_release_candidate_restore_evidence();
$restoreEvidenceBackup = dental_release_candidate_backup_by_id($pdo, (int)($restoreEvidence['backup_id'] ?? 0));
$restoreEvidenceEvaluation = $restoreEvidenceBackup ? dental_production_backup_row_evaluation($restoreEvidenceBackup, $requiredSince, true) : ['ready'=>false];
$restoreTested = dental_release_candidate_timestamp_ready((string)($restoreEvidence['tested_at'] ?? ''), $requiredSince)
    && (string)($restoreEvidence['version'] ?? '') === app_release_version()
    && !empty($restoreEvidenceEvaluation['ready'])
    && trim((string)($restoreEvidence['backup_checksum_sha256'] ?? '')) !== ''
    && hash_equals((string)($restoreEvidenceBackup['checksum_sha256'] ?? ''),(string)($restoreEvidence['backup_checksum_sha256'] ?? ''));
$tableReady = backup_center_table_ready($pdo);
$zipReady = class_exists('ZipArchive');

render_header('Backup & Restore Center');
?>
<section class="admin-hero card">
    <div><span class="eyebrow">Superuser recovery</span><h2>Backup & Restore Center</h2><p>Create protected backups, verify package integrity, upload recovery files, and record restore testing before go-live.</p><div class="status-actions"><a class="btn ghost" href="system_setup.php?tab=backups">System Setup backup tab</a><a class="btn ghost" href="go_live_checklist.php">Go-Live Checklist</a></div></div>
    <div class="admin-hero-icon"><?=dental_icon('backup')?></div>
</section>
<?php if (!$tableReady): ?>
<div class="card"><h2>Backup schema update required</h2><p>Apply all pending updates before using managed backups.</p><a class="btn primary" href="system_setup.php?tab=updates">Open Updates & Database</a></div>
<?php else: ?>
<section class="stats mini-stats">
    <?php stat_card('Backups', count($rows), 'Stored in protected backup directory'); ?>
    <?php stat_card('Latest backup', $latest ? date('M j, Y g:i A', strtotime((string)$latest['created_at'])) : 'None', $latest ? (string)($latest['backup_type'] ?? 'database') : 'Create one before updates'); ?>
    <?php stat_card('Eligible verification', $latestVerified ? 'Verified' : 'Not verified', $latestVerified ? date('M j, Y', strtotime((string)($eligibleBackup['verified_at'] ?? 'now'))) : 'Create a complete backup, then click Verify'); ?>
    <?php stat_card('Restore tested', $restoreTested ? 'Recorded' : 'Not recorded', $restoreTested ? (string)($restoreEvidence['tested_at'] ?? '') : 'Link a current verified complete backup'); ?>
</section>
<div class="setup-grid">
<section class="card" id="create-backup"><div class="card-head"><div><h2>Create backup</h2><p>Use complete backup before updates, migrations, or client go-live.</p></div></div>
<form method="post" class="form-grid"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="create_backup">
<label>Backup type<select name="backup_type"><option value="complete">Complete system — database and managed files</option><option value="database">Database only</option><option value="files">Files only</option></select></label>
<label>Optional ZIP password<input type="password" name="backup_password" autocomplete="new-password"><small>Only applies to ZIP backups if the server supports AES encryption.</small></label>
<div class="wide"><button class="btn primary" type="submit">Create backup now</button></div></form>
<?php if (!$zipReady): ?><div class="alert warning"><strong>ZIP extension missing.</strong> Database backup still works. Enable PHP ZIP for complete/files backups.</div><?php endif; ?>
</section>
<section class="card"><div class="card-head"><div><h2>Upload recovery file</h2><p>Store a previous backup for safekeeping. This does not restore automatically.</p></div></div>
<form method="post" enctype="multipart/form-data" class="form-grid"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="upload_backup"><label class="wide">Backup file<input type="file" name="backup_file" accept=".sql,.gz,.zip" required><small>Maximum 200 MB.</small></label><div class="wide"><button class="btn ghost" type="submit">Upload backup</button></div></form>
</section>
</div>
<section class="card" id="restore-drill"><div class="card-head"><div><h2>Restore procedure</h2><p>Automatic in-place restore is intentionally not performed inside the live clinic session. Use this guided sequence to avoid data loss.</p></div></div>
<ol class="qa-manual-checklist compact-checklist"><li>Create a fresh pre-restore backup.</li><li>Download and verify the target backup package.</li><li>Put the clinic in maintenance mode or perform restore on staging first.</li><li>Restore database.sql with the correct database prefix.</li><li>Restore private files, public uploads, branding files, configuration, and license state when present.</li><li>Run System Setup, QA Smoke Test, login test, clinical-file test, cashier test, and patient portal test.</li></ol>
<?php if ($eligibleRestoreBackups): ?>
<form method="post" class="form-grid">
<input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="mark_restore_tested">
<label>Verified complete backup<select name="backup_id" required><?php foreach($eligibleRestoreBackups as $backupOption): ?><option value="<?=(int)$backupOption['id']?>"><?=e((string)$backupOption['file_name'])?> · <?=e(date('M j, Y g:i A',strtotime((string)$backupOption['verified_at'])))?></option><?php endforeach; ?></select></label>
<label>Recovery environment<select name="restore_environment" required><option value="staging_restore">Staging restore</option><option value="clean_server_restore">Clean-server restore</option><option value="documented_recovery_drill">Documented recovery drill</option></select></label>
<label class="wide">Recovery evidence note<textarea name="restore_note" rows="3" placeholder="State what was restored or rehearsed, the checks performed, and the result." required></textarea></label>
<label>Confirmation<input name="confirmation" placeholder="RESTORE TESTED <?=e(app_release_version())?>" required></label>
<div class="wide"><button class="btn primary" type="submit">Record restore evidence</button></div>
</form>
<?php else: ?><div class="alert warning"><strong>No eligible backup.</strong> Create a complete system backup after this update and click Verify before recording a restore drill.</div><?php endif; ?></section>
<section class="card table-wrap"><div class="card-head"><div><h2>Backup repository</h2><p>Download, verify, or remove protected backup packages.</p></div></div><table><thead><tr><th>File</th><th>Type</th><th>Size</th><th>Files</th><th>Protection</th><th>Verification</th><th>Created</th><th>Actions</th></tr></thead><tbody>
<?php foreach($rows as $backup): $file = basename((string)($backup['file_name'] ?? '')); $exists = $file !== '' && is_file(backup_directory() . '/' . $file); ?>
<tr><td><strong><?=e($file)?></strong><?php if(!empty($backup['notes'])):?><small><?=e((string)$backup['notes'])?></small><?php endif;?><?php if(!empty($backup['checksum_sha256'])):?><small class="checksum">SHA-256 <?=e(substr((string)$backup['checksum_sha256'],0,18))?>…</small><?php endif;?></td><td><span class="badge"><?=e((string)($backup['backup_type'] ?? 'database'))?></span></td><td><?=e(backup_center_size((int)($backup['file_size'] ?? 0)))?></td><td><?=e((string)($backup['file_count'] ?? 0))?></td><td><?=!empty($backup['is_encrypted'])?'AES encrypted':'Server protected'?></td><td><?=!empty($backup['verified_at'])?'<span class="status-ok">Verified</span>':'Not verified'?><?=!$exists?'<br><span class="bad">File missing</span>':''?></td><td><?=e(date('M j, Y g:i A',strtotime((string)$backup['created_at'])))?><small><?=e((string)($backup['creator_name'] ?? $backup['creator_email'] ?? 'System'))?></small></td><td><div class="inline-actions"><?php if($exists):?><a href="download_backup.php?id=<?=(int)$backup['id']?>">Download</a><?php endif;?><form method="post"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="verify_backup"><input type="hidden" name="backup_id" value="<?=(int)$backup['id']?>"><button class="link-btn" type="submit">Verify</button></form><details class="compact-details"><summary>Delete</summary><form method="post" class="inline-delete-form"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="delete_backup"><input type="hidden" name="backup_id" value="<?=(int)$backup['id']?>"><input name="confirm_delete" placeholder="Type DELETE"><button class="link-btn danger-text" type="submit">Remove</button></form></details></div></td></tr>
<?php endforeach; if(!$rows): ?><tr><td colspan="8" class="muted">No backups have been created or uploaded.</td></tr><?php endif; ?>
</tbody></table></section>
<?php endif; ?>
<?php render_footer();
