<?php

declare(strict_types=1);

require_once DENTAL_ROOT . '/app/storage_health.php';
require_once DENTAL_ROOT . '/app/operational_state_cleanup.php';

/**
 * Non-destructive production-readiness checks for DentalCare.
 *
 * The checks intentionally avoid mutating clinical or financial records. They
 * verify deployment health, required schema safeguards, stale workflow states,
 * backup recency, runtime errors, and role prerequisites before go-live.
 */

function dental_production_safe(callable $callback, mixed $default = null): mixed
{
    try {
        return $callback();
    } catch (Throwable $exception) {
        if (function_exists('dental_runtime_write_log')) {
            dental_runtime_write_log('Production readiness check failed: ' . $exception->getMessage());
        } else {
            error_log('DentalCare production readiness check failed: ' . $exception->getMessage());
        }
        return $default;
    }
}

function dental_production_index_exists(PDO $pdo, string $tableName, string $indexName): bool
{
    return (bool) dental_production_safe(static function () use ($pdo, $tableName, $indexName): bool {
        $stmt = $pdo->prepare(
            'SELECT COUNT(*) FROM information_schema.statistics '
            . 'WHERE table_schema=? AND table_name=? AND index_name=?'
        );
        $stmt->execute([(string) app_config('database.name'), $tableName, $indexName]);
        return (int) $stmt->fetchColumn() > 0;
    }, false);
}

function dental_production_count(PDO $pdo, string $sql, array $arguments = []): int
{
    return (int) dental_production_safe(static function () use ($pdo, $sql, $arguments): int {
        $stmt = $pdo->prepare($sql);
        $stmt->execute($arguments);
        return (int) $stmt->fetchColumn();
    }, 0);
}

function dental_production_backup_row_evaluation(array $row, int $requiredSince = 0, bool $requireComplete = false): array
{
    $now = time();
    $futureTolerance = 300;
    $created = !empty($row['created_at']) ? (strtotime((string) $row['created_at']) ?: 0) : 0;
    $verified = !empty($row['verified_at']) ? (strtotime((string) $row['verified_at']) ?: 0) : 0;
    $status = strtolower(trim((string) ($row['status'] ?? '')));
    $checksum = strtolower(trim((string) ($row['checksum_sha256'] ?? '')));
    $type = strtolower(trim((string) ($row['backup_type'] ?? '')));
    $fileName = basename((string) ($row['file_name'] ?? ''));
    $path = $fileName !== '' ? backup_directory() . '/' . $fileName : '';
    $fileExists = $path !== '' && is_file($path) && is_readable($path);
    $actualSize = $fileExists ? (int) (filesize($path) ?: 0) : 0;
    $recordedSize = (int) ($row['file_size'] ?? 0);
    // Verification is represented by verified_at and cryptographic evidence.
    // backup_logs.status remains a lifecycle value such as created/uploaded/restored.
    $statusReady = $verified > 0 && !in_array($status, ['failed', 'deleted'], true);
    $evidenceRequired = array_key_exists('verification_evidence_json', $row);
    $evidence = [];
    if ($evidenceRequired && trim((string)($row['verification_evidence_json'] ?? '')) !== '') {
        $decodedEvidence = json_decode((string)$row['verification_evidence_json'], true);
        if (is_array($decodedEvidence)) $evidence = $decodedEvidence;
    }
    $deepEvidenceReady = !$evidenceRequired || (
        !empty($evidence['deep_verified'])
        && (int)($row['verification_file_count'] ?? 0) > 0
        && (int)($row['verification_format_version'] ?? 0) > 0
        && preg_match('/^[a-f0-9]{64}$/', strtolower((string)($evidence['outer_sha256'] ?? ''))) === 1
        && ($checksum === '' || hash_equals($checksum, strtolower((string)$evidence['outer_sha256'])))
    );
    $checksumReady = (bool) preg_match('/^[a-f0-9]{64}$/', $checksum);
    $timeOrderReady = $created > 0
        && $verified > 0
        && $verified >= $created
        && $created <= $now + $futureTolerance
        && $verified <= $now + $futureTolerance;
    $current = $requiredSince === 0 || ($created >= $requiredSince && $verified >= $requiredSince);
    $ageSeconds = $created > 0 ? $now - $created : PHP_INT_MAX;
    $ageDays = $ageSeconds >= 0 && $ageSeconds !== PHP_INT_MAX ? (int) floor($ageSeconds / 86400) : 9999;
    $ageReady = $ageSeconds >= 0 && $ageSeconds <= 7 * 86400;
    $sizeReady = $actualSize > 0 && ($recordedSize <= 0 || $recordedSize === $actualSize);
    $typeReady = !$requireComplete || $type === 'complete';

    $reasons = [];
    if (!$statusReady) $reasons[] = 'the backup has not been explicitly verified or its lifecycle status is failed/deleted';
    if (!$deepEvidenceReady) $reasons[] = 'deep archive verification evidence is missing or does not match the backup checksum';
    if (!$checksumReady) $reasons[] = 'SHA-256 evidence is missing or invalid';
    if (!$fileExists) $reasons[] = 'backup file is missing or unreadable';
    elseif (!$sizeReady) $reasons[] = 'backup file size does not match its record';
    if (!$timeOrderReady) $reasons[] = 'backup timestamps are invalid, future-dated, or out of order';
    if (!$current) $reasons[] = 'backup was not created and verified in the current release evidence window';
    if (!$ageReady) $reasons[] = 'backup is older than seven days';
    if (!$typeReady) $reasons[] = 'a complete system backup is required';

    return [
        'ready' => $reasons === [],
        'reasons' => $reasons,
        'created_timestamp' => $created,
        'verified_timestamp' => $verified,
        'age_days' => $ageDays,
        'file_exists' => $fileExists,
        'actual_size' => $actualSize,
        'recorded_size' => $recordedSize,
        'checksum_ready' => $checksumReady,
        'status_ready' => $statusReady,
        'deep_evidence_ready' => $deepEvidenceReady,
        'type_ready' => $typeReady,
    ];
}

function dental_production_backup_evidence(PDO $pdo, int $requiredSince = 0, bool $requireComplete = false): array
{
    if (!schema_table_exists(table('backup_logs'))) {
        return ['ready' => false, 'selected' => null, 'eligible' => null, 'latest' => null, 'age_days' => 9999, 'evaluation' => []];
    }

    $columns = ['id', 'file_name', 'created_at'];
    foreach (['status', 'verified_at', 'checksum_sha256', 'backup_type', 'file_size', 'verification_evidence_json', 'verification_file_count', 'verification_format_version'] as $column) {
        if (schema_column_exists(table('backup_logs'), $column)) $columns[] = $column;
    }

    $rows = dental_production_safe(static function () use ($pdo, $columns): array {
        $stmt = $pdo->query(
            'SELECT ' . implode(',', array_map(static fn(string $column): string => '`' . $column . '`', $columns))
            . ' FROM `' . table('backup_logs') . '` ORDER BY `created_at` DESC,`id` DESC LIMIT 50'
        );
        return $stmt->fetchAll() ?: [];
    }, []);

    $latest = $rows[0] ?? null;
    $eligible = null;
    $eligibleEvaluation = [];
    foreach ($rows as $row) {
        $evaluation = dental_production_backup_row_evaluation($row, $requiredSince, $requireComplete);
        if (!empty($evaluation['ready'])) {
            $eligible = $row;
            $eligibleEvaluation = $evaluation;
            break;
        }
    }

    $selected = $eligible ?: $latest;
    $selectedEvaluation = $selected ? dental_production_backup_row_evaluation($selected, $requiredSince, $requireComplete) : [];
    return [
        'ready' => $eligible !== null,
        'selected' => $selected,
        'eligible' => $eligible,
        'latest' => $latest,
        'age_days' => (int) ($selectedEvaluation['age_days'] ?? 9999),
        'evaluation' => $eligible !== null ? $eligibleEvaluation : $selectedEvaluation,
    ];
}

function dental_production_route_exists(string $route): bool
{
    return (bool) dental_production_safe(static function () use ($route): bool {
        $routes = require DENTAL_ROOT . '/config/routes.php';
        return is_array($routes) && isset($routes[$route]) && is_file((string) $routes[$route]);
    }, false);
}

function dental_production_check(
    string $key,
    string $category,
    string $title,
    string $status,
    string $detail,
    string $metric = '',
    string $href = '',
    string $actionLabel = 'Review'
): array {
    $allowed = ['pass', 'review', 'blocked', 'not_configured'];
    if (!in_array($status, $allowed, true)) $status = 'review';
    return compact('key', 'category', 'title', 'status', 'detail', 'metric', 'href', 'actionLabel');
}

function dental_production_tail(string $path, int $maxBytes = 262144): string
{
    if (!is_file($path) || !is_readable($path)) return '';
    clearstatcache(true, $path);
    $size = (int) (filesize($path) ?: 0);
    $handle = @fopen($path, 'rb');
    if (!$handle) return '';
    if ($size > $maxBytes) @fseek($handle, -$maxBytes, SEEK_END);
    $contents = stream_get_contents($handle);
    fclose($handle);
    return is_string($contents) ? $contents : '';
}

/**
 * Returns the protected log files that are part of the current monitoring
 * window. DentalCare intentionally evaluates only today and yesterday so an
 * old archived log can never hold a current release gate open forever.
 *
 * @return array<int,string>
 */
function dental_production_monitor_files(string $type): array
{
    $prefix = $type === 'slow_requests' ? 'performance-' : 'dentalcare-';
    $suffix = $type === 'slow_requests' ? '.jsonl' : '.log';
    $directory = DENTAL_ROOT . '/storage/runtime/logs';
    $files = [];
    foreach ([date('Y-m-d'), date('Y-m-d', strtotime('-1 day'))] as $date) {
        $path = $directory . '/' . $prefix . $date . $suffix;
        if (is_file($path)) $files[] = $path;
    }
    return $files;
}

function dental_production_monitor_checkpoint(string $type): int
{
    $allowed = ['runtime_errors', 'slow_requests'];
    if (!in_array($type, $allowed, true)) return 0;
    $raw = (string) dental_production_safe(static fn(): string => (string) setting_get('production_acceptance', $type . '_reviewed_through', ''), '');
    return $raw !== '' ? (strtotime($raw) ?: 0) : 0;
}

/**
 * File-position checkpoints are immune to timezone differences and to
 * multiline stack traces whose continuation lines do not carry timestamps.
 *
 * @return array{active:bool,recorded_at:string,files:array<string,int>}
 */
function dental_production_monitor_cursor(string $type): array
{
    $default = ['active' => false, 'recorded_at' => '', 'files' => []];
    if (!in_array($type, ['runtime_errors', 'slow_requests'], true)) return $default;
    $raw = (string) dental_production_safe(
        static fn(): string => (string) setting_get('production_acceptance', $type . '_review_cursor', ''),
        ''
    );
    if ($raw === '') return $default;
    $decoded = json_decode($raw, true);
    if (!is_array($decoded) || !isset($decoded['files']) || !is_array($decoded['files'])) return $default;
    $files = [];
    foreach ($decoded['files'] as $name => $offset) {
        $safeName = basename((string) $name);
        if ($safeName === '') continue;
        $files[$safeName] = max(0, (int) $offset);
    }
    return [
        'active' => true,
        'recorded_at' => (string) ($decoded['recorded_at'] ?? ''),
        'files' => $files,
    ];
}

/**
 * Capture exact byte offsets after the operator has reviewed the currently
 * displayed incidents. Newly appended records start after these offsets.
 *
 * @return array{version:int,recorded_at:string,files:array<string,int>}
 */
function dental_production_monitor_cursor_snapshot(string $type): array
{
    $files = [];
    foreach (dental_production_monitor_files($type) as $path) {
        clearstatcache(true, $path);
        $files[basename($path)] = max(0, (int) (filesize($path) ?: 0));
    }
    return [
        'version' => 1,
        'recorded_at' => date(DATE_ATOM),
        'files' => $files,
    ];
}

function dental_production_read_after_offset(string $path, int $offset, int $maxBytes = 1048576): string
{
    if (!is_file($path) || !is_readable($path)) return '';
    clearstatcache(true, $path);
    $size = max(0, (int) (filesize($path) ?: 0));
    if ($offset < 0 || $offset > $size) $offset = 0; // file was rotated or truncated
    if ($offset >= $size) return '';
    if (($size - $offset) > $maxBytes) $offset = max(0, $size - $maxBytes);
    $handle = @fopen($path, 'rb');
    if (!$handle) return '';
    if ($offset > 0 && @fseek($handle, $offset, SEEK_SET) !== 0) {
        fclose($handle);
        return '';
    }
    $contents = stream_get_contents($handle);
    fclose($handle);
    return is_string($contents) ? $contents : '';
}

function dental_production_line_timestamp(string $line): int
{
    if (preg_match('/^\[([^\]]+)\]/', $line, $match)) {
        return strtotime((string) $match[1]) ?: 0;
    }
    return 0;
}

function dental_production_runtime_error_summary(): array
{
    $entries = [];
    $seen = [];
    $checkpoint = dental_production_monitor_checkpoint('runtime_errors');
    $cursor = dental_production_monitor_cursor('runtime_errors');
    foreach (dental_production_monitor_files('runtime_errors') as $path) {
        $name = basename($path);
        $content = !empty($cursor['active'])
            ? dental_production_read_after_offset($path, (int) (($cursor['files'][$name] ?? 0)))
            : dental_production_tail($path);
        $lines = preg_split('/\R/', $content) ?: [];
        foreach ($lines as $rawLine) {
            $line = trim((string) $rawLine);
            if ($line === '') continue;

            // Only a timestamped first line is an incident. Stack-trace and
            // SQLSTATE continuation lines are details of that incident and
            // must never be counted again after a review checkpoint.
            if (!preg_match('/^\[([^\]]+)\]\s+(?:\[([A-F0-9]{8,16})\]\s+)?(.*)$/i', $line, $match)) continue;
            $recordedAt = strtotime((string) $match[1]) ?: 0;
            $reference = strtoupper(trim((string) ($match[2] ?? '')));
            $message = trim((string) ($match[3] ?? ''));
            if (!preg_match('/Fatal PHP error|DentalCare request failure|request failure|Uncaught|SQLSTATE/i', $message)) continue;
            if (empty($cursor['active']) && $checkpoint > 0 && $recordedAt > 0 && $recordedAt <= $checkpoint) continue;

            $key = $reference !== ''
                ? 'ref:' . $reference
                : 'msg:' . sha1($name . '|' . preg_replace('/\s+/', ' ', $message));
            if (isset($seen[$key])) continue;
            $seen[$key] = true;
            $entries[] = [
                'reference' => $reference,
                'summary' => mb_substr($message, 0, 500),
                'file' => $name,
                'recorded_at' => $recordedAt > 0 ? date(DATE_ATOM, $recordedAt) : '',
            ];
        }
    }
    usort($entries, static fn(array $a, array $b): int => ((string) ($b['recorded_at'] ?? '')) <=> ((string) ($a['recorded_at'] ?? '')));
    $total = count($entries);
    return [
        'count' => $total,
        'entries' => array_slice($entries, 0, 12),
        'checkpoint' => $checkpoint,
        'cursor_active' => !empty($cursor['active']),
        'cursor_recorded_at' => (string) ($cursor['recorded_at'] ?? ''),
    ];
}

function dental_production_performance_summary(): array
{
    $entries = [];
    $seen = [];
    $checkpoint = dental_production_monitor_checkpoint('slow_requests');
    $cursor = dental_production_monitor_cursor('slow_requests');
    foreach (dental_production_monitor_files('slow_requests') as $path) {
        $name = basename($path);
        $content = !empty($cursor['active'])
            ? dental_production_read_after_offset($path, (int) (($cursor['files'][$name] ?? 0)))
            : dental_production_tail($path);
        $lines = preg_split('/\R/', $content) ?: [];
        foreach ($lines as $line) {
            $row = json_decode(trim((string) $line), true);
            if (!is_array($row)) continue;
            $recordedAt = strtotime((string) ($row['recorded_at'] ?? '')) ?: 0;
            if (empty($cursor['active']) && $checkpoint > 0 && $recordedAt > 0 && $recordedAt <= $checkpoint) continue;
            $requestId = trim((string) ($row['request_id'] ?? ''));
            $key = $requestId !== '' ? $requestId : sha1(json_encode($row, JSON_UNESCAPED_SLASHES) ?: '');
            if (isset($seen[$key])) continue;
            $seen[$key] = true;
            $entries[] = $row;
        }
    }
    usort($entries, static fn(array $a, array $b): int => ((string) ($b['recorded_at'] ?? '')) <=> ((string) ($a['recorded_at'] ?? '')));
    $slow = array_values(array_filter($entries, static fn(array $row): bool => (float) ($row['elapsed_ms'] ?? 0) >= 2500));
    $errors = array_values(array_filter($entries, static fn(array $row): bool => (int) ($row['status_code'] ?? 200) >= 500));
    return [
        'count' => count($entries),
        'slow_count' => count($slow),
        'error_count' => count($errors),
        'entries' => array_slice($entries, 0, 15),
        'checkpoint' => $checkpoint,
        'cursor_active' => !empty($cursor['active']),
        'cursor_recorded_at' => (string) ($cursor['recorded_at'] ?? ''),
    ];
}

function dental_production_role_codes(): array
{
    return [
        'patient' => 'Patient',
        'receptionist' => 'Receptionist',
        'staff' => 'General Staff',
        'dentist' => 'Dentist',
        'dental_assistant' => 'Dental Assistant',
        'cashier' => 'Cashier',
        'inventory_officer' => 'Inventory Officer',
        'clinic_admin' => 'Clinic Administrator',
        'super_admin' => 'Superuser',
    ];
}

function dental_production_role_signoff(string $role): array
{
    $default = [
        'status' => 'not_tested',
        'note' => '',
        'recorded_at' => '',
        'recorded_by' => '',
        'recorded_by_user_id' => 0,
        'version' => '',
        'evidence_id' => '',
        'prerequisite_fingerprint' => '',
    ];
    $raw = (string) dental_production_safe(static fn(): string => (string) setting_get('production_acceptance', 'role_' . $role, ''), '');
    if ($raw === '') return $default;
    $decoded = json_decode($raw, true);
    return is_array($decoded) ? array_merge($default, $decoded) : $default;
}

function dental_production_role_prerequisite_fingerprint(array $prerequisite): string
{
    return hash('sha256', json_encode([
        'ready' => !empty($prerequisite['ready']),
        'detail' => trim((string) ($prerequisite['detail'] ?? '')),
    ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '');
}

function dental_production_record_role_signoff(string $role, string $status, string $note, ?PDO $pdo = null): void
{
    $roles = dental_production_role_codes();
    if (!isset($roles[$role])) throw new RuntimeException('Choose a valid operational role.');
    if (!in_array($status, ['passed', 'blocked', 'not_tested'], true)) throw new RuntimeException('Choose a valid acceptance result.');
    if ($status !== 'not_tested' && mb_strlen(trim($note)) < 5) {
        throw new RuntimeException('Add a short acceptance note describing what was tested or what remains blocked.');
    }

    $pdo = $pdo ?: db();
    $prerequisites = dental_production_role_prerequisites($pdo);
    $prerequisite = (array) ($prerequisites[$role] ?? ['ready' => false, 'detail' => 'Role prerequisites could not be verified.']);
    if ($status === 'passed' && empty($prerequisite['ready'])) {
        throw new RuntimeException('This role cannot be marked Passed while its prerequisites are blocked: ' . (string) ($prerequisite['detail'] ?? 'Review role setup.'));
    }

    $user = current_user() ?: [];
    setting_set('production_acceptance', 'role_' . $role, json_encode([
        'status' => $status,
        'note' => mb_substr(trim($note), 0, 1000),
        'recorded_at' => date('c'),
        'recorded_by' => (string) ($user['name'] ?? $user['email'] ?? 'Superuser'),
        'recorded_by_user_id' => (int) ($user['id'] ?? 0),
        'version' => app_release_version(),
        'evidence_id' => bin2hex(random_bytes(12)),
        'prerequisite_fingerprint' => dental_production_role_prerequisite_fingerprint($prerequisite),
    ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
}

function dental_production_role_prerequisites(PDO $pdo): array
{
    $route = static fn(string $key): bool => dental_production_route_exists($key);
    $tableReady = static fn(string $logical): bool => (bool) dental_production_safe(static fn(): bool => schema_table_exists(table($logical)), false);
    $columnReady = static fn(string $logical, string $column): bool => (bool) dental_production_safe(static fn(): bool => schema_column_exists(table($logical), $column), false);
    $doctorWorkflowReady = (bool) dental_production_safe(static fn(): bool => function_exists('doctor_workflow_schema_ready') && doctor_workflow_schema_ready(), false);

    return [
        'patient' => [
            'ready' => $route('patient-login') && $route('patient-portal') && $tableReady('patient_portal_accounts'),
            'detail' => 'Patient login, ownership-scoped portal, lifetime appointment history, completed-care ledger, itemized invoices, payments, allocations, returns, and records routes are installed.',
        ],
        'receptionist' => [
            'ready' => $route('patients') && $route('appointments') && $route('queue') && $tableReady('patients') && $tableReady('appointments') && $tableReady('queue_entries'),
            'detail' => 'Patient registration, appointment scheduling, check-in, walk-in, queue, and handoff prerequisites are installed.',
        ],
        'staff' => [
            'ready' => $route('patients') && $route('appointments') && $route('queue') && $route('billing')
                && $tableReady('patients') && $tableReady('appointments') && $tableReady('queue_entries')
                && $tableReady('invoices') && $tableReady('payments') && $tableReady('cashier_sessions')
                && $columnReady('payments', 'request_token') && dental_production_index_exists($pdo, table('payments'), 'uq_payments_request_token'),
            'detail' => 'General Staff registration, scheduling, queue, cashier session, payment idempotency, receipt, and checkout prerequisites are installed.',
        ],
        'dentist' => [
            'ready' => $route('doctor_treatment') && $route('clinical_workspace') && $tableReady('clinical_encounters') && $tableReady('procedures') && $doctorWorkflowReady
                && $columnReady('clinical_encounters','care_path') && $columnReady('clinical_encounters','vitals_skip_reason'),
            'detail' => 'Dentist dashboard, adaptive teeth-first care paths, conditional safety requirements, procedure, signing, and finalization prerequisites are installed.',
        ],
        'dental_assistant' => [
            'ready' => $route('queue') && $route('clinical_workspace') && $route('sterilization') && $tableReady('clinical_encounters') && $tableReady('procedures'),
            'detail' => 'Chairside assistance, treatment-entry verification boundary, sterilization, and queue prerequisites are installed.',
        ],
        'cashier' => [
            'ready' => $route('billing') && $tableReady('invoices') && $tableReady('payments') && $tableReady('cashier_sessions')
                && $columnReady('payments', 'request_token') && dental_production_index_exists($pdo, table('payments'), 'uq_payments_request_token'),
            'detail' => 'Cashier session, invoice locking, payment idempotency, receipt, and checkout prerequisites are installed.',
        ],
        'inventory_officer' => [
            'ready' => $route('inventory') && $tableReady('inventory_items') && $tableReady('inventory_lots') && $tableReady('inventory_transactions'),
            'detail' => 'Inventory catalog, lots, stock movements, receiving, issuing, and audit prerequisites are installed.',
        ],
        'clinic_admin' => [
            'ready' => $route('administration') && $route('staff') && $route('services') && $route('permission_matrix') && $tableReady('settings'),
            'detail' => 'Clinic setup, staff, services, permissions, communication, and operating controls are installed.',
        ],
        'super_admin' => [
            'ready' => $route('system_setup') && $route('backup_restore') && $route('production_monitoring') && $route('installer_builder'),
            'detail' => 'Updates, monitoring, backups, release packaging, licensing, and recovery controls are installed.',
        ],
    ];
}

function dental_production_health_snapshot(PDO $pdo): array
{
    $checks = [];
    $release = app_release_version();
    $installed = installed_application_version();
    $aligned = version_compare($installed, $release, '==');
    $checks[] = dental_production_check(
        'version_alignment', 'Deployment', 'Application and database version',
        $aligned ? 'pass' : 'blocked',
        $aligned ? 'Installed application records are aligned with the uploaded release.' : (version_compare($installed, $release, '>') ? 'The database version is ahead of the uploaded application files. Restore matching application files before continuing.' : 'The database version is behind the uploaded application files.'),
        $installed . ' / ' . $release,
        'system_setup.php?tab=updates'
    );

    $pending = (bool) dental_production_safe(static fn(): bool => app_update_pending(), true);
    $checks[] = dental_production_check(
        'pending_updates', 'Deployment', 'Pending migrations and readiness flags',
        !$pending ? 'pass' : 'blocked',
        !$pending ? 'No pending application or schema update is detected.' : 'The update engine still reports a pending release or incomplete readiness flag.',
        $pending ? 'Pending' : 'Clear',
        'system_setup.php?tab=updates'
    );

    $adaptiveReady = schema_column_exists(table('clinical_encounters'),'care_path')
        && schema_column_exists(table('clinical_encounters'),'vitals_skip_reason')
        && schema_column_exists(table('clinical_encounters'),'care_path_decided_by')
        && schema_column_exists(table('clinical_encounters'),'care_path_decided_at')
        && (string) setting_get('system','adaptive_encounter_workflow_v0804_ready','0') === '1';
    $checks[] = dental_production_check(
        'adaptive_encounter_schema', 'Clinical workflow', 'Adaptive dental encounter controls',
        $adaptiveReady ? 'pass' : 'blocked',
        $adaptiveReady ? 'Focused, comprehensive, emergency, and follow-up paths are installed with auditable conditional vital-sign decisions.' : 'Apply the v0.8.0.4 update before using adaptive or teeth-first clinical documentation.',
        $adaptiveReady ? 'Ready' : 'Update required',
        'system_setup.php?tab=updates',
        'Apply update'
    );

    $dbStarted = microtime(true);
    $dbReady = (bool) dental_production_safe(static fn(): bool => (int) $pdo->query('SELECT 1')->fetchColumn() === 1, false);
    $dbMs = round((microtime(true) - $dbStarted) * 1000, 1);
    $dbStatus = !$dbReady ? 'blocked' : ($dbMs > 750 ? 'review' : 'pass');
    $checks[] = dental_production_check(
        'database_latency', 'Infrastructure', 'Database connectivity and response',
        $dbStatus,
        !$dbReady ? 'The application could not complete a database health query.' : ($dbMs > 750 ? 'The database answered, but response time is high for a simple health query.' : 'The database responded normally.'),
        $dbMs . ' ms'
    );

    $engine = dental_production_safe(static function () use ($pdo): array {
        $prefix = (string) app_config('database.prefix', 'dent_');
        $stmt = $pdo->prepare("SELECT COUNT(*) total, SUM(ENGINE='InnoDB') innodb_count FROM information_schema.tables WHERE table_schema=? AND LEFT(table_name,CHAR_LENGTH(?))=?");
        $stmt->execute([(string) app_config('database.name'), $prefix, $prefix]);
        return $stmt->fetch() ?: ['total' => 0, 'innodb_count' => 0];
    }, ['total' => 0, 'innodb_count' => 0]);
    $tableTotal = (int) ($engine['total'] ?? 0);
    $innodbTotal = (int) ($engine['innodb_count'] ?? 0);
    $engineReady = $tableTotal > 0 && $tableTotal === $innodbTotal;
    $checks[] = dental_production_check(
        'transaction_engine', 'Concurrency', 'Transactional database engine',
        $engineReady ? 'pass' : ($tableTotal > 0 ? 'blocked' : 'review'),
        $engineReady ? 'All detected DentalCare tables use InnoDB transaction and row-lock support.' : 'One or more application tables do not report InnoDB support, or no application tables were detected.',
        $innodbTotal . '/' . $tableTotal . ' InnoDB'
    );

    $paths = [
        'Sessions' => DENTAL_ROOT . '/storage/runtime/sessions',
        'Runtime logs' => DENTAL_ROOT . '/storage/runtime/logs',
        'Backups' => DENTAL_ROOT . '/storage/backups',
        'Private files' => DENTAL_ROOT . '/storage/private',
    ];
    $badPaths = [];
    foreach ($paths as $label => $path) {
        if ((!is_dir($path) && !@mkdir($path, 0750, true)) || !is_writable($path)) $badPaths[] = $label;
    }
    $checks[] = dental_production_check(
        'storage_permissions', 'Infrastructure', 'Protected storage permissions',
        !$badPaths ? 'pass' : 'blocked',
        !$badPaths ? 'Session, log, backup, and private storage directories are writable.' : 'These protected directories are not writable: ' . implode(', ', $badPaths) . '.',
        !$badPaths ? 'Writable' : count($badPaths) . ' blocked'
    );

    $storageHealth = dental_storage_health_snapshot();
    $checks[] = dental_production_check(
        'disk_capacity', 'Infrastructure', 'Storage capacity and write safety',
        (string)($storageHealth['status'] ?? 'review'),
        (string)($storageHealth['detail'] ?? 'Storage capacity could not be measured.'),
        (string)($storageHealth['metric'] ?? 'Unknown'),
        'production_monitoring.php#storage-health',
        'Open storage health'
    );

    $requiredExtensions = ['pdo_mysql', 'openssl', 'mbstring', 'json', 'fileinfo'];
    $missingExtensions = array_values(array_filter($requiredExtensions, static fn(string $extension): bool => !extension_loaded($extension)));
    $checks[] = dental_production_check(
        'php_extensions', 'Infrastructure', 'Required PHP extensions',
        !$missingExtensions ? 'pass' : 'blocked',
        !$missingExtensions ? 'All required PHP extensions are loaded.' : 'Missing PHP extensions: ' . implode(', ', $missingExtensions) . '.',
        !$missingExtensions ? 'Ready' : count($missingExtensions) . ' missing'
    );
    $checks[] = dental_production_check(
        'zip_extension', 'Recovery', 'ZIP backup and installer support',
        class_exists('ZipArchive') ? 'pass' : 'not_configured',
        class_exists('ZipArchive') ? 'PHP ZIP support is available for complete backups and installer generation.' : 'PHP ZIP is not enabled. Database-only backups may still work, but full packages cannot be created.',
        class_exists('ZipArchive') ? 'Available' : 'Unavailable',
        'backup_restore.php'
    );

    $backupEvidence = dental_production_backup_evidence($pdo, 0, false);
    $backup = (array)($backupEvidence['selected'] ?? []);
    if (!$backup) {
        $checks[] = dental_production_check('backup_freshness', 'Recovery', 'Verified backup freshness', 'not_configured', 'No managed backup record is available. Create and verify a backup before production use.', 'None', 'backup_restore.php');
    } else {
        $ageDays = (int)($backupEvidence['age_days'] ?? 9999);
        $verified = !empty($backupEvidence['ready']);
        $backupStatus = $verified ? 'pass' : ($ageDays <= 7 ? 'review' : 'blocked');
        $checks[] = dental_production_check(
            'backup_freshness', 'Recovery', 'Verified backup freshness',
            $backupStatus,
            $verified
                ? 'A readable backup created within seven days has an explicit verification status, a valid SHA-256 record, and valid timestamps. Newer failed or unverified attempts do not hide this recovery evidence.'
                : 'No verified managed backup from the last seven days is available. Verify a recent backup before production use.',
            $ageDays . ' day' . ($ageDays === 1 ? '' : 's') . ' old',
            'backup_restore.php'
        );
    }

    $mail = (array) app_config('mail', []);
    $driver = strtolower((string) ($mail['driver'] ?? 'log'));
    $fromEmail = trim((string) ($mail['from_email'] ?? ''));
    $mailReady = false;
    $mailDetail = '';
    if ($driver === 'smtp') {
        $host = trim((string) (($mail['smtp']['host'] ?? '')));
        $mailReady = $host !== '' && filter_var($fromEmail, FILTER_VALIDATE_EMAIL);
        $mailDetail = $mailReady ? 'SMTP sender and host are configured. Run the test-delivery action before go-live.' : 'SMTP is selected but the sender address or server host is incomplete.';
    } elseif ($driver === 'gmail_api') {
        $gmail = (array) ($mail['gmail_api'] ?? []);
        $mailReady = trim((string) ($gmail['client_id'] ?? '')) !== '' && trim((string) ($gmail['refresh_token'] ?? '')) !== '' && filter_var((string) ($gmail['sender_email'] ?? $fromEmail), FILTER_VALIDATE_EMAIL);
        $mailDetail = $mailReady ? 'Gmail API credentials and sender are configured. Run a test delivery before go-live.' : 'Gmail API is selected but OAuth credentials or the sender address are incomplete.';
    } elseif ($driver === 'mail') {
        $mailReady = filter_var($fromEmail, FILTER_VALIDATE_EMAIL) !== false;
        $mailDetail = 'PHP mail is configured but delivery depends on the hosting server. Verify with a test message.';
    } else {
        $mailDetail = 'The log-only mail driver is active. Real appointment, account, and reminder email will not be delivered.';
    }
    $checks[] = dental_production_check(
        'mail_configuration', 'Communication', 'Outbound email configuration',
        $mailReady ? ($driver === 'mail' ? 'review' : 'pass') : 'not_configured',
        $mailDetail,
        strtoupper($driver),
        'email_settings.php'
    );

    $errors = dental_production_runtime_error_summary();
    $errorCount = (int) ($errors['count'] ?? 0);
    $checks[] = dental_production_check(
        'runtime_errors', 'Monitoring', 'Recent fatal and request errors',
        $errorCount === 0 ? 'pass' : ($errorCount >= 5 ? 'blocked' : 'review'),
        $errorCount === 0 ? 'No recent fatal or uncaught request failure appears in the protected runtime logs.' : 'Review the recent protected runtime errors and resolve recurring references before production launch.',
        (string) $errorCount,
        'production_monitoring.php#runtime-errors',
        'Review errors'
    );

    $performance = dental_production_performance_summary();
    $slowCount = (int) ($performance['slow_count'] ?? 0);
    $checks[] = dental_production_check(
        'slow_requests', 'Performance', 'Slow request telemetry',
        $slowCount === 0 ? 'pass' : ($slowCount >= 20 ? 'blocked' : 'review'),
        $slowCount === 0 ? 'No request exceeding the 2.5-second monitoring threshold was recently recorded.' : 'Review the recorded slow routes. Long update, report, billing, or clinical requests should be profiled before launch.',
        (string) $slowCount,
        'production_monitoring.php#request-telemetry',
        'Review slow routes'
    );

    $criticalIssues = schema_table_exists(table('visit_integrity_issues'))
        ? dental_production_count($pdo, 'SELECT COUNT(*) FROM `' . table('visit_integrity_issues') . '` WHERE status IN ("open","in_review") AND severity IN ("critical","urgent")')
        : 0;
    $checks[] = dental_production_check(
        'critical_exceptions', 'Workflow', 'Critical reconciliation exceptions',
        $criticalIssues === 0 ? 'pass' : 'blocked',
        $criticalIssues === 0 ? 'No unresolved critical or urgent visit-integrity exception is recorded.' : 'Resolve critical workflow exceptions before accepting new production activity.',
        (string) $criticalIssues,
        'visit_exceptions.php'
    );

    $operationalState = dental_production_safe(static fn(): array => dental_operational_state_snapshot($pdo), [
        'counts'=>['chairs'=>0,'cashier_empty'=>0,'cashier_requires_reconciliation'=>0],
    ]);
    $operationalCounts = (array)($operationalState['counts'] ?? []);
    $operationalErrors = array_values(array_filter(array_map('strval', (array)($operationalState['errors'] ?? []))));
    $staleChairs = (int)($operationalCounts['chairs'] ?? 0);
    $checks[] = dental_production_check(
        'stale_chairs', 'Workflow', 'Finalized encounters retaining chairs',
        $operationalErrors ? 'blocked' : ($staleChairs === 0 ? 'pass' : 'blocked'),
        $operationalErrors
            ? 'DentalCare could not complete the operational chair-state inspection. Open Production Monitor for the protected diagnostic reference; no record is changed by this check.'
            : ($staleChairs === 0
                ? 'No finalized encounter is retaining a treatment chair.'
                : 'These are historical queue assignments, not active treatment transactions. Use the audited safe-state repair in Production Monitor to release them and preserve the correct checkout stage.'),
        $operationalErrors ? 'Inspection error' : (string) $staleChairs,
        'production_monitoring.php#operational-state-repair',
        'Review and repair'
    );

    $staleCashierEmpty = (int)($operationalCounts['cashier_empty'] ?? 0);
    $staleCashierFinancial = (int)($operationalCounts['cashier_requires_reconciliation'] ?? 0);
    $staleCashier = $staleCashierEmpty + $staleCashierFinancial;
    $checks[] = dental_production_check(
        'stale_cashier_sessions', 'Workflow', 'Stale cashier windows',
        $operationalErrors ? 'blocked' : ($staleCashierFinancial > 0 ? 'review' : ($staleCashierEmpty > 0 ? 'review' : 'pass')),
        $operationalErrors
            ? 'DentalCare could not complete the cashier-session inspection. Open Production Monitor for the protected diagnostic reference; no cashier session is changed by this check.'
            : ($staleCashier === 0
                ? 'No cashier window has remained open beyond the operational threshold.'
                : ($staleCashierFinancial > 0
                    ? $staleCashierEmpty . ' empty session(s) can be closed safely; ' . $staleCashierFinancial . ' session(s) contain transaction evidence and require drawer reconciliation.'
                    : $staleCashierEmpty . ' old empty cashier session(s) remain open. They contain no payment, checkout, or locked-patient activity and can be closed through the audited safe-state repair.')),
        $operationalErrors ? 'Inspection error' : ($staleCashierEmpty . ' empty; ' . $staleCashierFinancial . ' financial'),
        'production_monitoring.php#operational-state-repair',
        'Review and repair'
    );

    $negativeItems = schema_table_exists(table('inventory_items'))
        ? dental_production_count($pdo, 'SELECT COUNT(*) FROM `' . table('inventory_items') . '` WHERE current_stock < -0.0001')
        : 0;
    $negativeLots = schema_table_exists(table('inventory_lots'))
        ? dental_production_count($pdo, 'SELECT COUNT(*) FROM `' . table('inventory_lots') . '` WHERE quantity_remaining < -0.0001')
        : 0;
    $negativeStock = $negativeItems + $negativeLots;
    $checks[] = dental_production_check(
        'negative_inventory', 'Workflow', 'Negative inventory balances',
        $negativeStock === 0 ? 'pass' : 'blocked',
        $negativeStock === 0 ? 'No item or tracked lot has a negative stock balance.' : 'Negative inventory balances indicate a concurrency, correction, or import problem that must be reconciled.',
        (string) $negativeStock,
        'inventory.php'
    );

    $duplicateActiveQueue = schema_table_exists(table('queue_entries'))
        ? dental_production_count($pdo,
            'SELECT COUNT(*) FROM (SELECT patient_id FROM `' . table('queue_entries') . '` '
            . 'WHERE status NOT IN ("completed","cancelled") GROUP BY patient_id HAVING COUNT(*)>1) duplicate_queue'
        )
        : 0;
    $checks[] = dental_production_check(
        'duplicate_active_queue', 'Concurrency', 'Duplicate active patient queues',
        $duplicateActiveQueue === 0 ? 'pass' : 'blocked',
        $duplicateActiveQueue === 0 ? 'No patient has more than one active queue entry.' : 'At least one patient has multiple active queue tickets. Reconcile the duplicate visit before continuing.',
        (string) $duplicateActiveQueue,
        'queue.php'
    );

    $paymentGuard = schema_column_exists(table('payments'), 'request_token')
        && dental_production_index_exists($pdo, table('payments'), 'uq_payments_request_token');
    $checks[] = dental_production_check(
        'payment_idempotency', 'Concurrency', 'Duplicate-payment database guard',
        $paymentGuard ? 'pass' : 'blocked',
        $paymentGuard ? 'Payment requests are protected by a persistent unique request token in addition to invoice row locks.' : 'The persistent unique payment request-token safeguard is incomplete.',
        $paymentGuard ? 'Active' : 'Missing',
        'billing.php'
    );

    $auditGuard = schema_column_exists(table('audit_logs'), 'record_hash')
        && dental_production_index_exists($pdo, table('audit_logs'), 'idx_audit_record_hash');
    $checks[] = dental_production_check(
        'audit_integrity', 'Security', 'Critical audit integrity fields',
        $auditGuard ? 'pass' : 'blocked',
        $auditGuard ? 'Critical clinical and financial actions can record integrity fingerprints before commit.' : 'Audit integrity fields or indexes are incomplete.',
        $auditGuard ? 'Active' : 'Missing'
    );

    // The installer package service is the authoritative release-safety implementation.
    // Older builds also shipped a standalone CLI preflight script, but that optional wrapper
    // is not required by the web installer builder and must not block production approval.
    $releaseScanner = false;
    $releaseScannerMetric = 'Missing';
    $releaseScannerDetail = 'The installer package safety service is missing or could not be validated.';
    $releaseServicePath = DENTAL_ROOT . '/app/Services/InstallerPackageService.php';
    if (is_file($releaseServicePath)) {
        try {
            require_once $releaseServicePath;
            $releaseScanner = class_exists('InstallerPackageService')
                && method_exists('InstallerPackageService', 'releaseSafetyChecklist')
                && method_exists('InstallerPackageService', 'releaseSafetyReady')
                && InstallerPackageService::releaseSafetyReady();
            $releaseScannerMetric = $releaseScanner ? 'Protected' : 'Needs review';
            $releaseScannerDetail = $releaseScanner
                ? 'The built-in installer allowlist, runtime-data exclusions, manifest controls, checksums, and mandatory sensitive-content scan are active.'
                : 'The installer package service is present, but one or more release-safety policies failed. Open Installer Builder to review the exact policy result.';
        } catch (Throwable $exception) {
            $releaseScanner = false;
            $releaseScannerMetric = 'Check error';
            $releaseScannerDetail = 'Release-package safety could not be evaluated: ' . $exception->getMessage();
            if (function_exists('dental_runtime_write_log')) {
                dental_runtime_write_log('Production readiness release-safety check failed: ' . $exception->getMessage());
            }
        }
    }
    $checks[] = dental_production_check(
        'release_scanner', 'Deployment', 'Clean production-package controls',
        $releaseScanner ? 'pass' : 'blocked',
        $releaseScannerDetail,
        $releaseScannerMetric,
        'installer_builder.php'
    );

    $counts = ['pass' => 0, 'review' => 0, 'blocked' => 0, 'not_configured' => 0];
    foreach ($checks as $check) $counts[$check['status']]++;

    return [
        'checks' => $checks,
        'counts' => $counts,
        'errors' => $errors,
        'performance' => $performance,
        'roles' => dental_production_role_prerequisites($pdo),
        'generated_at' => date('c'),
    ];
}
