<?php

declare(strict_types=1);

require_once DENTAL_ROOT . '/app/bootstrap.php';
require_login();
require_roles(['super_admin']);
require_once DENTAL_ROOT . '/app/layout.php';
if (is_file(DENTAL_ROOT . '/app/setup_readiness.php')) { require_once DENTAL_ROOT . '/app/setup_readiness.php'; }

if (is_file(DENTAL_ROOT . '/app/release_hardening.php')) {
    require_once DENTAL_ROOT . '/app/release_hardening.php';
}
if (is_file(DENTAL_ROOT . '/app/chair_resource.php')) {
    require_once DENTAL_ROOT . '/app/chair_resource.php';
}
if (is_file(DENTAL_ROOT . '/app/clinical_billing.php')) {
    require_once DENTAL_ROOT . '/app/clinical_billing.php';
}
if (is_file(DENTAL_ROOT . '/app/Services/DemoAccountService.php')) {
    require_once DENTAL_ROOT . '/app/Services/DemoAccountService.php';
}
if (is_file(DENTAL_ROOT . '/app/sms_service.php')) {
    require_once DENTAL_ROOT . '/app/sms_service.php';
}
if (is_file(DENTAL_ROOT . '/app/treatment_flow.php')) {
    require_once DENTAL_ROOT . '/app/treatment_flow.php';
}
if (is_file(DENTAL_ROOT . '/app/query_integrity.php')) {
    require_once DENTAL_ROOT . '/app/query_integrity.php';
}
if (is_file(DENTAL_ROOT . '/app/data_integrity.php')) {
    require_once DENTAL_ROOT . '/app/data_integrity.php';
}

$pdo = db();

function smoke_safe_bool(callable $callback, bool $default = false): bool
{
    try {
        return (bool) $callback();
    } catch (Throwable $exception) {
        error_log('DentalCare QA smoke test safe bool failure: ' . $exception->getMessage());
        return $default;
    }
}

function smoke_safe_value(callable $callback, mixed $default = null): mixed
{
    try {
        return $callback();
    } catch (Throwable $exception) {
        error_log('DentalCare QA smoke test safe value failure: ' . $exception->getMessage());
        return $default;
    }
}

function smoke_count(PDO $pdo, string $tableName, string $where = '1=1'): int
{
    return (int) smoke_safe_value(static function () use ($pdo, $tableName, $where): int {
        $physicalTable = table($tableName);
        if (!schema_table_exists($physicalTable)) return 0;
        return (int) $pdo->query('SELECT COUNT(*) FROM `' . $physicalTable . '` WHERE ' . $where)->fetchColumn();
    }, 0);
}

function smoke_route_ready(string $route): bool
{
    return smoke_safe_bool(static function () use ($route): bool {
        $routesPath = DENTAL_ROOT . '/config/routes.php';
        if (!is_file($routesPath)) return false;
        $routes = require $routesPath;
        return is_array($routes) && isset($routes[$route]) && is_file((string) $routes[$route]);
    });
}

function smoke_table_ready(string $logicalTable): bool
{
    return smoke_safe_bool(static fn(): bool => schema_table_exists(table($logicalTable)));
}

function smoke_column_ready(string $logicalTable, string $column): bool
{
    return smoke_safe_bool(static fn(): bool => schema_column_exists(table($logicalTable), $column));
}

function smoke_named_ready(string $functionName, ?callable $fallback = null): bool
{
    return smoke_safe_bool(static function () use ($functionName, $fallback): bool {
        if (function_exists($functionName)) return (bool) $functionName();
        return $fallback ? (bool) $fallback() : false;
    });
}

function smoke_status(bool $ok, bool $warn = false): string
{
    return $ok ? 'ready' : ($warn ? 'warn' : 'fail');
}

function smoke_card(string $title, string $status, string $summary, array $details = [], string $href = ''): void
{
    $label = $status === 'ready' ? 'Ready' : ($status === 'warn' ? 'Review' : 'Fix');
    $badgeClass = $status === 'ready' ? 'success' : ($status === 'warn' ? 'warning' : 'danger');
    $visibleDetails = array_slice($details, 0, 5);
    $hiddenDetails = array_slice($details, 5);
    ?>
    <article class="smoke-card <?= e($status) ?>">
        <header><h3><?= e($title) ?></h3><span class="badge <?= e($badgeClass) ?>"><?= e($label) ?></span></header>
        <p><?= e($summary) ?></p>
        <?php if ($visibleDetails): ?><ul><?php foreach ($visibleDetails as $detail): ?><li><?= e((string) $detail) ?></li><?php endforeach; ?></ul><?php endif; ?>
        <?php if ($hiddenDetails): ?>
            <details class="compact-details"><summary><?= count($hiddenDetails) ?> more detail<?= count($hiddenDetails) === 1 ? '' : 's' ?></summary><div class="compact-details-body"><ul><?php foreach ($hiddenDetails as $detail): ?><li><?= e((string) $detail) ?></li><?php endforeach; ?></ul></div></details>
        <?php endif; ?>
        <?php if ($href !== ''): ?><a class="btn ghost compact" href="<?= e($href) ?>">Open</a><?php endif; ?>
    </article>
    <?php
}

$checks = [];
$routes = [
    'Admin dashboard' => 'dashboard',
    'Reception appointments' => 'appointments',
    'Clinic queue' => 'queue',
    'Dentist treatment dashboard' => 'doctor_treatment',
    'Clinical workspace' => 'clinical_workspace',
    'Cashier/POS billing' => 'billing',
    'Services editor' => 'services',
    'Chairs editor' => 'chairs',
    'Public first appointment' => 'appointment-new',
    'Patient portal' => 'patient-portal',
    'First-time setup wizard' => 'setup_wizard',
    'Go-live checklist' => 'go_live_checklist',
    'Demo accounts/patients seeder' => 'demo_accounts',
    'Patient numbering controls' => 'patient_numbering',
    'SMS readiness' => 'sms_settings',
    'Backup & Restore Center' => 'backup_restore',
    'Permission Matrix' => 'permission_matrix',
    'Production Monitor' => 'production_monitoring',
];
foreach ($routes as $title => $route) {
    $checks[] = ['title' => $title, 'ok' => smoke_route_ready($route), 'group' => 'routes'];
}

$checks[] = ['title' => 'Core application schema', 'ok' => smoke_named_ready('application_schema_ready'), 'group' => 'schema'];
$checks[] = ['title' => 'Doctor workflow schema', 'ok' => smoke_named_ready('doctor_workflow_schema_ready'), 'group' => 'schema'];
$checks[] = ['title' => 'Chair resource schema', 'ok' => smoke_named_ready('chair_resource_schema_ready', static fn(): bool => smoke_table_ready('clinic_chairs')), 'group' => 'schema'];
$checks[] = ['title' => 'Public intake schema', 'ok' => smoke_named_ready('public_intake_schema_ready'), 'group' => 'schema'];
$checks[] = ['title' => 'Clinical billing handoff schema', 'ok' => smoke_named_ready('clinical_billing_schema_ready', static fn(): bool => smoke_table_ready('clinical_procedures')), 'group' => 'schema'];
$checks[] = ['title' => 'Queue provider reset column', 'ok' => smoke_column_ready('queue_entries', 'inhouse_provider_name'), 'group' => 'schema'];
$checks[] = ['title' => 'Services editable fields', 'ok' => smoke_column_ready('service_catalog', 'description') && smoke_column_ready('service_catalog', 'duration_minutes') && smoke_column_ready('service_catalog', 'standard_fee'), 'group' => 'edit'];
$checks[] = ['title' => 'Chairs editable fields', 'ok' => smoke_column_ready('clinic_chairs', 'chair_name') && smoke_column_ready('clinic_chairs', 'usage_mode') && smoke_column_ready('clinic_chairs', 'status'), 'group' => 'edit'];

$release = smoke_safe_value(static fn(): array => function_exists('dental_release_hardening_summary') ? dental_release_hardening_summary($GLOBALS['pdo']) : ['ready' => false, 'package_policy_ready' => false], ['ready' => false, 'package_policy_ready' => false]);
if (!is_array($release)) $release = ['ready' => false, 'package_policy_ready' => false];
$checks[] = ['title' => 'Release package deny-list', 'ok' => !empty($release['package_policy_ready']), 'group' => 'release'];
$checks[] = ['title' => 'Installer builder source exists', 'ok' => is_file(DENTAL_ROOT . '/app/Services/InstallerPackageService.php'), 'group' => 'release'];
$checks[] = ['title' => 'Clean package policy marker', 'ok' => smoke_safe_bool(static fn(): bool => (string) setting_get('system', 'release_deny_list_confirmed', '0') === '1' || (string) setting_get('system', 'release_package_runtime_guard_confirmed', '0') === '1'), 'group' => 'release'];
$checks[] = ['title' => 'Demo patients seeder marker', 'ok' => smoke_safe_bool(static fn(): bool => (string) setting_get('system', 'demo_patients_seeder_ready', '0') === '1'), 'group' => 'demo'];
$checks[] = ['title' => 'Full demo clinic dataset marker', 'ok' => smoke_safe_bool(static fn(): bool => (string) setting_get('system', 'full_demo_clinic_dataset_ready', '0') === '1' || (string) setting_get('system', 'full_demo_clinic_dataset_feature_ready', '0') === '1'), 'group' => 'demo'];
$checks[] = ['title' => 'Orientation mode foundation', 'ok' => smoke_safe_bool(static fn(): bool => (string) setting_get('system', 'orientation_mode_foundation_ready', '0') === '1'), 'group' => 'demo'];
$checks[] = ['title' => 'Service requires-dentist runtime guard', 'ok' => smoke_safe_bool(static fn(): bool => (string) setting_get('system', 'service_requires_dentist_runtime_guard_ready', '0') === '1'), 'group' => 'schema'];
$checks[] = ['title' => 'Cashier POS payment integrity', 'ok' => smoke_safe_bool(static fn(): bool => (string) setting_get('system', 'cashier_pos_payment_integrity_ready', '0') === '1'), 'group' => 'finance'];
$checks[] = ['title' => 'SMS readiness schema', 'ok' => smoke_safe_bool(static fn(): bool => function_exists('dental_sms_schema_ready') && dental_sms_schema_ready() && (string) setting_get('system', 'sms_readiness_ready', '0') === '1'), 'group' => 'communications'];
$checks[] = ['title' => 'Backup & Restore Center route', 'ok' => smoke_route_ready('backup_restore') && (string) setting_get('system','backup_restore_center_ready','0') === '1', 'group' => 'recovery'];
$checks[] = ['title' => 'Permission Matrix route', 'ok' => smoke_route_ready('permission_matrix') && (string) setting_get('system','permission_matrix_ready','0') === '1', 'group' => 'security'];
$checks[] = ['title' => 'Production monitoring and acceptance', 'ok' => smoke_route_ready('production_monitoring') && (string) setting_get('system','production_monitoring_v0790_ready','0') === '1', 'group' => 'release'];
$checks[] = ['title' => 'Request telemetry', 'ok' => (string) setting_get('system','request_telemetry_v0790_ready','0') === '1', 'group' => 'release'];
$checks[] = ['title' => 'Tooth favicon installed', 'ok' => is_file(DENTAL_ROOT . '/public/favicon.svg') && is_file(DENTAL_ROOT . '/public/favicon.ico'), 'group' => 'branding'];
$checks[] = ['title' => 'Treatment flow finalization', 'ok' => smoke_safe_bool(static fn(): bool => function_exists('treatment_flow_schema_ready') && treatment_flow_schema_ready() && (string)setting_get('system','treatment_flow_finalization_ready','0') === '1'), 'group' => 'clinical'];
$queryIntegrity = function_exists('dental_query_integrity_snapshot')
    ? dental_query_integrity_snapshot($pdo)
    : ['overall'=>'blocked','counts'=>['pass'=>0,'review'=>0,'blocked'=>1,'total'=>1],'checks'=>[['label'=>'Query regression helper','status'=>'blocked','detail'=>'app/query_integrity.php is missing.']]];
$checks[] = ['title' => 'SQL query regression safety', 'ok' => (string)($queryIntegrity['overall'] ?? 'blocked') === 'pass', 'group' => 'release'];
$dataIntegrity = function_exists('dental_data_integrity_snapshot')
    ? dental_data_integrity_snapshot($pdo)
    : ['overall'=>'blocked','counts'=>['pass'=>0,'review'=>0,'blocked'=>1,'total'=>1],'checks'=>[['label'=>'Data-integrity helper','status'=>'blocked','detail'=>'app/data_integrity.php is missing.']]];
$checks[] = ['title' => 'Production data-integrity baseline', 'ok' => (string)($dataIntegrity['overall'] ?? 'blocked') !== 'blocked', 'group' => 'release'];

$total = count($checks);
$passed = count(array_filter($checks, static fn(array $row): bool => !empty($row['ok'])));
$score = $total > 0 ? round(($passed / $total) * 100) : 0;
$doctorAccounts = smoke_count($pdo, 'users', 'role_id IN (SELECT id FROM `' . table('roles') . '` WHERE code="dentist")');
$doctorProfiles = smoke_count($pdo, 'dentist_profiles');
$activeDoctorProfiles = smoke_count($pdo, 'dentist_profiles', 'profile_status="active"');
$services = smoke_count($pdo, 'service_catalog');
$chairs = smoke_count($pdo, 'clinic_chairs');
$patients = smoke_count($pdo, 'patients');
$routeReadyCount = count(array_filter($routes, static fn(string $route): bool => smoke_route_ready($route)));
$setupSnapshot = function_exists('dental_setup_readiness_snapshot') ? dental_setup_readiness_snapshot($pdo) : ['percent'=>0,'complete'=>0,'total'=>0,'steps'=>[]];
$demoAccountReadiness = class_exists('DemoAccountService') ? DemoAccountService::readiness($pdo) : ['ready'=>false,'checks'=>[]];
$demoAccountCounts = class_exists('DemoAccountService') ? DemoAccountService::counts($pdo) : ['total'=>0,'active'=>0,'inactive'=>0,'dentist_profiles'=>0,'patients'=>0,'appointments'=>0,'invoices'=>0,'services'=>0,'inventory_items'=>0,'sterilization_records'=>0];
$orientationModeActive = function_exists('app_orientation_mode_active') && app_orientation_mode_active();

render_header('QA Smoke Test');
?>
<section class="role-welcome card">
    <div>
        <span class="eyebrow">Superuser QA</span>
        <h2>DentalCare role-flow smoke test</h2>
        <p>Use this before client demos, after reset, and after updates. It checks main role routes, required schema, edit pages, package-safety policy, and guided setup readiness. Failed policy checks now show the exact item to repair instead of a generic warning.</p>
    </div>
    <div class="smoke-score"><?= e((string) $score) ?>%</div>
</section>
<section class="stats mini-stats">
    <?php stat_card('Checks passed', $passed . ' / ' . $total, $score >= 90 ? 'Ready for deeper manual testing' : 'Review failed checks'); ?>
    <?php stat_card('Dentist profiles', $doctorProfiles, $activeDoctorProfiles . ' active · ' . $doctorAccounts . ' Dentist accounts'); ?>
    <?php stat_card('Services', $services, 'Editable service catalog records'); ?>
    <?php stat_card('Demo patients', (string)($demoAccountCounts['patients'] ?? 0), (string)($demoAccountCounts['appointments'] ?? 0) . ' appointments · ' . (string)($demoAccountCounts['invoices'] ?? 0) . ' invoices'); ?>
    <?php stat_card('Orientation mode', $orientationModeActive ? 'ON' : 'OFF', (string)($demoAccountCounts['inventory_items'] ?? 0) . ' demo inventory items'); ?>
    <?php stat_card('SMS readiness', (function_exists('dental_sms_schema_ready') && dental_sms_schema_ready()) ? 'Ready' : 'Pending', (string)setting_get('sms','provider','log')); ?>
    <?php stat_card('Recovery', smoke_count($pdo, 'backup_logs'), 'Backups recorded'); ?>
    <?php stat_card('Chairs/resources', $chairs, 'Editable chair/resource records'); ?>
</section>
<?php if ($score < 100): ?>
<div class="alert warning"><strong>Manual QA still required.</strong> This smoke test confirms readiness signals only. Continue with the full flow: appointment request → check-in → chair assignment → treatment → billing handoff → cashier payment → receipt.</div>
<?php else: ?>
<div class="alert success"><strong>Baseline checks passed.</strong> Continue with manual role-flow testing using real sample records before client release.</div>
<?php endif; ?>
<section class="smoke-grid">
<?php
smoke_card('Role route coverage', smoke_status($routeReadyCount === count($routes)), 'Main role pages resolve to existing module files.', array_map(static fn($title, $route): string => $title . ': ' . (smoke_route_ready($route) ? 'ready' : 'missing'), array_keys($routes), $routes));
smoke_card('Doctor profile empty state', smoke_status(smoke_route_ready('doctor_profile') && smoke_route_ready('doctor_treatment')), 'Doctor pages should render guided setup screens instead of blank/white screens when profiles are missing.', ['Dentist accounts: ' . $doctorAccounts, 'Doctor profiles: ' . $doctorProfiles, 'Active profiles: ' . $activeDoctorProfiles], 'doctor_profile.php');
smoke_card('Services edit readiness', smoke_status(smoke_column_ready('service_catalog', 'description') && smoke_column_ready('service_catalog', 'duration_minutes') && smoke_column_ready('service_catalog', 'standard_fee')), 'Service management can edit name, description, duration, provider model, booking scope, and billing policy.', ['Catalog records: ' . $services], 'services.php');
smoke_card('Chairs edit readiness', smoke_status(smoke_table_ready('clinic_chairs') && smoke_column_ready('clinic_chairs', 'usage_mode') && smoke_table_ready('chair_session_assignments')), 'Chair/resource management can edit profiles, default providers, temporary sessions, and active-visit moves.', ['Chair/resource records: ' . $chairs], 'chairs.php');
smoke_card('Reset/schema fallback', smoke_status(smoke_column_ready('queue_entries', 'inhouse_provider_name')), 'Queue provider names are reset-safe and no longer crash chair/resource boards before runtime use.', ['Column queue_entries.inhouse_provider_name: ' . (smoke_column_ready('queue_entries', 'inhouse_provider_name') ? 'present' : 'missing')], 'system_setup.php?tab=updates');
smoke_card('Clinical billing handoff', smoke_status(smoke_named_ready('clinical_billing_schema_ready', static fn(): bool => smoke_table_ready('clinical_procedures'))), 'Treatment procedure billing readiness check now loads the billing helper before testing schema.', ['clinical_billing_schema_ready(): ' . (function_exists('clinical_billing_schema_ready') ? 'loaded' : 'not loaded')], 'system_setup.php?tab=updates');
smoke_card('Setup wizard readiness', smoke_status(smoke_route_ready('setup_wizard') && function_exists('dental_setup_readiness_snapshot')), 'Guided setup page is available for clinic profile, hours, chairs, services, staff, cashier, portal, and QA readiness.', ['Setup completion: ' . (string)($setupSnapshot['percent'] ?? 0) . '%', 'Checklist items: ' . (string)($setupSnapshot['complete'] ?? 0) . ' / ' . (string)($setupSnapshot['total'] ?? 0)], 'setup_wizard.php');
smoke_card('Go-live checklist readiness', smoke_status(smoke_route_ready('go_live_checklist')), 'Final deployment gate is available before demo, orientation, or production use.', ['Manual role-flow tested marker: ' . ((string)setting_get('system','manual_role_flow_reviewed_at','') !== '' ? 'recorded' : 'not yet recorded')], 'go_live_checklist.php');
smoke_card('Demo accounts and patients seeder', smoke_status(smoke_route_ready('demo_accounts') && !empty($demoAccountReadiness['ready']) && (string)setting_get('system','demo_patients_seeder_ready','0') === '1'), 'Superuser can create fictional role-based demo logins and fictional patient workflow records for client orientation without creating a demo Superuser.', ['Existing demo accounts: ' . (string)($demoAccountCounts['total'] ?? 0), 'Active demo accounts: ' . (string)($demoAccountCounts['active'] ?? 0), 'Demo dentist profiles: ' . (string)($demoAccountCounts['dentist_profiles'] ?? 0), 'Demo patients: ' . (string)($demoAccountCounts['patients'] ?? 0), 'Demo appointments: ' . (string)($demoAccountCounts['appointments'] ?? 0), 'Demo invoices: ' . (string)($demoAccountCounts['invoices'] ?? 0)], 'demo_accounts.php');
smoke_card('Patient numbering controls', smoke_status(smoke_route_ready('patient_numbering') && function_exists('dental_patient_numbering_settings')), 'Superuser/Admin can control patient number mode, prefix, date segment, padding, and next counter.', ['Route: ' . (smoke_route_ready('patient_numbering') ? 'ready' : 'missing'), 'Feature: ' . (function_exists('dental_patient_numbering_settings') ? 'ready' : 'missing')], 'patient_numbering.php');
smoke_card('Full demo clinic dataset and orientation mode', smoke_status((string)setting_get('system','full_demo_clinic_dataset_feature_ready','0') === '1' && (string)setting_get('system','orientation_mode_foundation_ready','0') === '1'), 'Superuser can seed a populated fictional clinic and enable orientation mode to pause real automatic email delivery during training.', ['Full dataset marker: ' . ((string)setting_get('system','full_demo_clinic_dataset_ready','0') === '1' ? 'created' : 'feature ready'), 'Demo services: ' . (string)($demoAccountCounts['services'] ?? 0), 'Demo inventory items: ' . (string)($demoAccountCounts['inventory_items'] ?? 0), 'Sterilization records: ' . (string)($demoAccountCounts['sterilization_records'] ?? 0), 'Orientation mode: ' . ($orientationModeActive ? 'active' : 'off')], 'demo_accounts.php');
smoke_card('Service requires-dentist fallback', smoke_status((string)setting_get('system','service_requires_dentist_runtime_guard_ready','0') === '1'), 'Follow-up reminder queries now handle service catalogs that use provider_type without a requires_dentist column.', ['service_catalog.requires_dentist column: ' . (smoke_column_ready('service_catalog', 'requires_dentist') ? 'present' : 'not required'), 'provider_type fallback: active'], 'system_setup.php?tab=updates');
smoke_card('Cashier POS payment integrity', smoke_status((string)setting_get('system','cashier_pos_payment_integrity_ready','0') === '1'), 'Cashier transaction focus, payment-method reference rules, duplicate non-cash reference guard, and demo receipt labels are enabled.', ['Transaction lock UI: ' . ((string)setting_get('system','cashier_transaction_lock_ui_ready','0') === '1' ? 'ready' : 'pending'), 'Reference guard: ' . ((string)setting_get('system','cashier_pos_reference_guard_ready','0') === '1' ? 'ready' : 'pending'), 'Payment rules table: ' . (smoke_table_ready('payment_method_rules') ? 'present' : 'fallback rules active')], 'billing.php');
$smsHealth = function_exists('dental_sms_health') ? dental_sms_health($pdo) : ['ready'=>false,'issues'=>['SMS service helper missing'],'provider'=>'log','pending'=>0,'held'=>0,'failed'=>0];
smoke_card('SMS readiness and templates', smoke_status(function_exists('dental_sms_schema_ready') && dental_sms_schema_ready() && (string)setting_get('system','sms_readiness_ready','0') === '1', !empty($smsHealth['issues'])), 'SMS provider settings, test-send page, SMS templates, and SMS outbox are available. Live sending remains disabled until provider credentials are validated.', ['Provider: ' . (string)($smsHealth['provider'] ?? setting_get('sms','provider','log')), 'Pending: ' . (string)($smsHealth['pending'] ?? 0), 'Held: ' . (string)($smsHealth['held'] ?? 0), 'Failed: ' . (string)($smsHealth['failed'] ?? 0), 'Issues: ' . (empty($smsHealth['issues']) ? 'none' : implode('; ', array_slice(array_map('strval',(array)$smsHealth['issues']),0,2)))], 'sms_settings.php');
smoke_card('Backup & Restore Center', smoke_status(smoke_route_ready('backup_restore') && (string)setting_get('system','backup_restore_center_ready','0') === '1'), 'Superuser can create, verify, download, upload, and document restore testing from a focused recovery center.', ['Route: ' . (smoke_route_ready('backup_restore') ? 'ready' : 'missing'), 'Backups recorded: ' . smoke_count($pdo, 'backup_logs'), 'Restore tested: ' . ((string)setting_get('system','restore_tested_at','') !== '' ? 'recorded' : 'not recorded')], 'backup_restore.php');
smoke_card('Permission Matrix and role audit', smoke_status(smoke_route_ready('permission_matrix') && (string)setting_get('system','permission_matrix_ready','0') === '1'), 'Role access and high-risk role separation can be reviewed before issuing staff accounts.', ['Route: ' . (smoke_route_ready('permission_matrix') ? 'ready' : 'missing'), 'Review marker: ' . ((string)setting_get('system','permission_matrix_reviewed_at','') !== '' ? 'recorded' : 'pending')], 'permission_matrix.php');
smoke_card('Tooth favicon', smoke_status(is_file(DENTAL_ROOT . '/public/favicon.svg') && is_file(DENTAL_ROOT . '/public/favicon.ico')), 'A tooth-only favicon is installed and used as the default browser icon when no custom favicon is configured.', ['SVG: ' . (is_file(DENTAL_ROOT . '/public/favicon.svg') ? 'ready' : 'missing'), 'ICO: ' . (is_file(DENTAL_ROOT . '/public/favicon.ico') ? 'ready' : 'missing'), 'Brand favicon: ' . ((string)setting_get('branding','favicon','') ?: 'fallback public/favicon.svg')], 'branding.php');
smoke_card('Doctor workspace treatment flow', smoke_status(function_exists('treatment_flow_schema_ready') && treatment_flow_schema_ready() && (string)setting_get('system','treatment_flow_finalization_ready','0') === '1'), 'The doctor dashboard and clinical workspace now share a canonical status rail for appointment → check-in → chair → treatment → billing → paid/closed.', ['Helper: ' . (function_exists('treatment_flow_schema_ready') ? 'loaded' : 'missing'), 'Queue table: ' . (smoke_table_ready('queue_entries') ? 'present' : 'missing'), 'Clinical encounters: ' . (smoke_table_ready('clinical_encounters') ? 'present' : 'missing'), 'Update marker: ' . ((string)setting_get('system','treatment_flow_finalization_ready','0') === '1' ? 'ready' : 'pending')], 'doctor_treatment.php');
$releaseDetails = ['Policy status: ' . (!empty($release['package_policy_ready']) ? 'protected' : 'needs review')];
foreach ((array)($release['package_policy'] ?? []) as $policyRow) {
    if (!empty($policyRow['ready'])) continue;
    $releaseDetails[] = (string)($policyRow['label'] ?? 'Unnamed policy') . ': ' . implode(', ', array_slice(array_map('strval', (array)($policyRow['failures'] ?? [])), 0, 3));
}
smoke_card('SQL query regression safety', smoke_status((string)($queryIntegrity['overall'] ?? 'blocked') === 'pass'), 'Read-only probes validate critical joined queries and source-level alias qualification before release.', array_map(static fn(array $row): string => (string)($row['label'] ?? 'Query check') . ': ' . (string)($row['status'] ?? 'review') . ' — ' . (string)($row['detail'] ?? ''), (array)($queryIntegrity['checks'] ?? [])), 'operational-health.php');
smoke_card('Production data-integrity baseline', smoke_status((string)($dataIntegrity['overall'] ?? 'blocked') !== 'blocked', (string)($dataIntegrity['overall'] ?? 'blocked') === 'review'), 'Database engines, strict SQL behavior, orphaned relationships, duplicate identifiers, request-ledger health, and transaction-boundary evidence are checked without changing business records.', array_map(static fn(array $row): string => (string)($row['label'] ?? 'Integrity check') . ': ' . (string)($row['status'] ?? 'review') . ' — ' . (string)($row['metric'] ?? ''), (array)($dataIntegrity['checks'] ?? [])), 'data-integrity-center');
smoke_card('Release package safety', smoke_status(!empty($release['package_policy_ready'])), 'Generated client/demo packages are expected to exclude live config, logs, backups, sessions, generated installers, and private uploads.', $releaseDetails, 'system_setup.php?tab=updates');
?>
</section>
<section class="card qa-checklist-card">
    <div class="card-head"><div><h2>Manual role-flow checklist</h2><p>Run this after the automated smoke test. Use fictional/demo accounts and patients during client orientation.</p></div></div>
    <ol class="qa-manual-checklist">
        <li><strong>Appointment intake:</strong> submit or create a patient appointment with service, preferred time, and provider preference.</li>
        <li><strong>Front desk:</strong> confirm the appointment, check in the patient, and move the visit through waiting/called/seated status.</li>
        <li><strong>Chair/resource:</strong> verify the patient appears on the chair board and can be assigned or moved without conflict.</li>
        <li><strong>Doctor workspace:</strong> open treatment, review patient history, dental chart, and add treatment/procedure notes.</li>
        <li><strong>Billing handoff:</strong> send completed procedures to billing with proper service amount and itemized details.</li>
        <li><strong>Cashier:</strong> receive payment, require digital/card reference when applicable, print receipt, and close transaction.</li>
    </ol>
</section>
<section class="card">
    <div class="card-head"><div><h2>Always excluded from client/demo/update packages</h2><p>These live-server paths may exist on the source installation, but must never be distributed.</p></div></div>
    <div class="release-deny-list">
        <?php foreach (['config/generated.php','error_log','api/error_log','storage/backups','storage/generated-installers','storage/runtime/sessions','storage/private','public/uploads/* except .htaccess','*.sql','*.zip','private/license secret keys'] as $path): ?><code><?= e($path) ?></code><?php endforeach; ?>
    </div>
</section>
<?php render_footer();
