<?php

declare(strict_types=1);

function inventory_schema_ready(): bool
{
    return schema_table_exists(table('inventory_suppliers'))
        && schema_table_exists(table('inventory_branch_stocks'))
        && schema_table_exists(table('inventory_documents'))
        && schema_table_exists(table('inventory_document_lines'))
        && schema_table_exists(table('inventory_document_history'))
        && schema_column_exists(table('inventory_document_lines'),'source_line_id')
        && schema_column_exists(table('inventory_items'),'barcode')
        && schema_column_exists(table('inventory_documents'),'usage_destination')
        && schema_column_exists(table('inventory_documents'),'chair_id')
        && schema_column_exists(table('inventory_documents'),'evidence_file_path')
        && schema_column_exists(table('inventory_documents'),'evidence_original_name')
        && schema_column_exists(table('inventory_lots'),'branch_id')
        && schema_column_exists(table('inventory_transactions'),'movement_no')
        && schema_table_exists(table('sterilization_equipment'))
        && schema_table_exists(table('instrument_sets'))
        && schema_table_exists(table('sterilization_batch_sets'))
        && schema_table_exists(table('sterilization_batch_events'))
        && schema_table_exists(table('instrument_usage'))
        && schema_column_exists(table('sterilization_batches'),'cycle_status');
}

function inventory_is_admin(): bool
{
    return has_role(['super_admin','clinic_admin']);
}

function inventory_procedure_branch_expression(string $procedureAlias='pr',string $encounterAlias='ce',string $patientAlias='p'): string
{
    $parts=[];
    if(schema_column_exists(table('procedures'),'branch_id'))$parts[]=$procedureAlias.'.branch_id';
    $parts[]=$encounterAlias.'.branch_id';
    $parts[]=$patientAlias.'.branch_id';
    return 'COALESCE(' . implode(',', $parts) . ')';
}

function inventory_user_branch_id(): int
{
    return signed_user_branch_id();
}

function inventory_require_branch(int $branchId): void
{
    if ($branchId <= 0) throw new RuntimeException('Select a clinic branch.');
    if (!has_role('super_admin') && inventory_user_branch_id() !== $branchId) {
        throw new RuntimeException('You cannot manage inventory for another branch.');
    }
}

function inventory_document_number(PDO $pdo, string $type): string
{
    $prefix = [
        'purchase_order'=>'PO','goods_receipt'=>'GR','issue'=>'ISS','return'=>'RET','transfer'=>'TRF',
        'stock_count'=>'CNT','adjustment'=>'ADJ','damage'=>'DMG','expiry'=>'EXP','disposal'=>'DSP',
    ][$type] ?? 'INV';
    for ($i=0;$i<8;$i++) {
        $value=$prefix . '-' . date('ymd') . '-' . strtoupper(bin2hex(random_bytes(3)));
        $stmt=$pdo->prepare('SELECT COUNT(*) FROM `' . table('inventory_documents') . '` WHERE document_no=?');
        $stmt->execute([$value]);
        if (!(int)$stmt->fetchColumn()) return $value;
    }
    throw new RuntimeException('Unable to generate an inventory document number.');
}

function inventory_movement_number(PDO $pdo): string
{
    for ($i=0;$i<8;$i++) {
        $value='MOV-' . date('ymdHis') . '-' . strtoupper(bin2hex(random_bytes(2)));
        $stmt=$pdo->prepare('SELECT COUNT(*) FROM `' . table('inventory_transactions') . '` WHERE movement_no=?');
        $stmt->execute([$value]);
        if (!(int)$stmt->fetchColumn()) return $value;
    }
    throw new RuntimeException('Unable to generate an inventory movement number.');
}

function inventory_record_history(PDO $pdo, int $documentId, ?string $previous, string $next, string $notes=''): void
{
    $pdo->prepare('INSERT INTO `' . table('inventory_document_history') . '` (document_id,previous_status,new_status,notes,changed_by,created_at) VALUES (?,?,?,?,?,NOW())')
        ->execute([$documentId,$previous,$next,$notes !== '' ? $notes : null,(int)(current_user()['id'] ?? 0) ?: null]);
}

function inventory_document_snapshot(array $document, array $lines): string
{
    $payload=[
        'document_no'=>(string)$document['document_no'],'type'=>(string)$document['document_type'],
        'branch_id'=>(int)$document['branch_id'],'source_branch_id'=>(int)($document['source_branch_id']??0),
        'destination_branch_id'=>(int)($document['destination_branch_id']??0),'supplier_id'=>(int)($document['supplier_id']??0),
        'requested_for_user_id'=>(int)($document['requested_for_user_id']??0),'patient_id'=>(int)($document['patient_id']??0),
        'encounter_id'=>(int)($document['encounter_id']??0),'procedure_id'=>(int)($document['procedure_id']??0),
        'usage_destination'=>(string)($document['usage_destination']??''),'chair_id'=>(int)($document['chair_id']??0),
        'external_reference'=>(string)($document['external_reference']??''),'evidence_reference'=>(string)($document['evidence_reference']??''),
        'evidence_file_path'=>(string)($document['evidence_file_path']??''),'source_document_id'=>(int)($document['source_document_id']??0),
        'reason'=>(string)$document['reason'],'status'=>(string)$document['status'],
        'requested_by'=>(int)($document['requested_by']??0),'approved_by'=>(int)($document['approved_by']??0),
        'posted_by'=>(int)($document['posted_by']??0),'received_by'=>(int)($document['received_by']??0),
        'lines'=>array_map(static fn(array $l): array => [
            'source_line_id'=>(int)($l['source_line_id']??0),'item_id'=>(int)$l['item_id'],'lot_id'=>(int)($l['lot_id']??0),'lot_number'=>(string)($l['lot_number']??''),
            'expiry_date'=>(string)($l['expiry_date']??''),'quantity'=>(float)$l['quantity'],'unit_cost'=>(float)($l['unit_cost']??0),
            'recorded_quantity'=>isset($l['recorded_quantity'])?(float)$l['recorded_quantity']:null,
            'counted_quantity'=>isset($l['counted_quantity'])?(float)$l['counted_quantity']:null,
            'variance_quantity'=>isset($l['variance_quantity'])?(float)$l['variance_quantity']:null,
        ],$lines),
    ];
    return hash('sha256',json_encode($payload,JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE|JSON_PRESERVE_ZERO_FRACTION));
}

function inventory_reconcile(PDO $pdo, int $itemId, int $branchId): float
{
    $stmt=$pdo->prepare('SELECT COALESCE(SUM(quantity_remaining),0) FROM `' . table('inventory_lots') . '` WHERE item_id=? AND branch_id=? AND status="active"');
    $stmt->execute([$itemId,$branchId]);
    $stock=round((float)$stmt->fetchColumn(),2);
    $reorderStmt=$pdo->prepare('SELECT reorder_level FROM `' . table('inventory_branch_stocks') . '` WHERE item_id=? AND branch_id=?');
    $reorderStmt->execute([$itemId,$branchId]);
    $reorder=$reorderStmt->fetchColumn();
    if($reorder===false){
        $q=$pdo->prepare('SELECT reorder_level FROM `' . table('inventory_items') . '` WHERE id=?');$q->execute([$itemId]);$reorder=(float)$q->fetchColumn();
    }
    $pdo->prepare('INSERT INTO `' . table('inventory_branch_stocks') . '` (item_id,branch_id,current_stock,reorder_level,last_reconciled_at,updated_at) VALUES (?,?,?,?,NOW(),NOW()) ON DUPLICATE KEY UPDATE current_stock=VALUES(current_stock),last_reconciled_at=NOW(),updated_at=NOW()')
        ->execute([$itemId,$branchId,$stock,(float)$reorder]);
    $sum=$pdo->prepare('SELECT COALESCE(SUM(current_stock),0) FROM `' . table('inventory_branch_stocks') . '` WHERE item_id=?');$sum->execute([$itemId]);
    $pdo->prepare('UPDATE `' . table('inventory_items') . '` SET current_stock=?,updated_at=NOW() WHERE id=?')->execute([round((float)$sum->fetchColumn(),2),$itemId]);
    return $stock;
}

function inventory_find_or_create_lot(PDO $pdo, int $itemId, int $branchId, string $lotNumber, ?string $expiryDate, float $unitCost, ?int $documentId): int
{
    $lotNumber=trim($lotNumber);
    if($lotNumber==='') throw new RuntimeException('A supplier lot or batch number is required.');
    $stmt=$pdo->prepare('SELECT id,expiry_date,status FROM `' . table('inventory_lots') . '` WHERE item_id=? AND branch_id=? AND lot_number=? FOR UPDATE');
    $stmt->execute([$itemId,$branchId,$lotNumber]);$row=$stmt->fetch();
    if($row){
        if(in_array((string)$row['status'],['disposed','expired','quarantined'],true))throw new RuntimeException('This lot is disposed, expired, or quarantined and cannot receive new stock.');
        if($expiryDate && !empty($row['expiry_date']) && (string)$row['expiry_date']!==$expiryDate)throw new RuntimeException('The supplied expiry date does not match the existing lot.');
        if($expiryDate && empty($row['expiry_date']))$pdo->prepare('UPDATE `' . table('inventory_lots') . '` SET expiry_date=?,updated_at=NOW() WHERE id=?')->execute([$expiryDate,(int)$row['id']]);
        return (int)$row['id'];
    }
    $pdo->prepare('INSERT INTO `' . table('inventory_lots') . '` (item_id,branch_id,lot_number,expiry_date,quantity_received,quantity_remaining,received_at,status,received_document_id,unit_cost,created_at,updated_at) VALUES (?,?,?,?,0,0,CURDATE(),"active",?,?,NOW(),NOW())')
        ->execute([$itemId,$branchId,$lotNumber,$expiryDate,$documentId,$unitCost]);
    return (int)$pdo->lastInsertId();
}

function inventory_post_movement(PDO $pdo, array $args): int
{
    $itemId=(int)$args['item_id'];$lotId=(int)$args['lot_id'];$branchId=(int)$args['branch_id'];
    $type=(string)$args['transaction_type'];$quantity=round((float)$args['quantity'],2);
    if($itemId<=0||$lotId<=0||$branchId<=0||abs($quantity)<0.01)throw new RuntimeException('A valid item, lot, branch, and movement quantity are required.');
    $lot=$pdo->prepare('SELECT * FROM `' . table('inventory_lots') . '` WHERE id=? AND item_id=? AND branch_id=? FOR UPDATE');
    $lot->execute([$lotId,$itemId,$branchId]);$row=$lot->fetch();
    if(!$row)throw new RuntimeException('The selected lot does not belong to this item and branch.');
    $before=round((float)$row['quantity_remaining'],2);$after=round($before+$quantity,2);
    if($after<0)throw new RuntimeException('This movement would create negative lot stock.');
    if($quantity<0 && in_array((string)$row['status'],['disposed','quarantined'],true))throw new RuntimeException('This lot is not available for issue.');
    if($quantity<0 && !empty($row['expiry_date']) && (string)$row['expiry_date']<date('Y-m-d') && !in_array($type,['expiration','disposal','stock_count','adjustment'],true))throw new RuntimeException('Expired stock cannot be issued or transferred.');
    if($type==='expiration')$status='expired';
    elseif($type==='disposal'&&$after<=0)$status='disposed';
    elseif($after<=0)$status='depleted';
    elseif(in_array((string)$row['status'],['expired','quarantined'],true))$status=(string)$row['status'];
    else $status='active';
    $receivedIncrease=$quantity>0 && in_array($type,['purchase','transfer_in','return'],true) ? $quantity : 0.0;
    $pdo->prepare('UPDATE `' . table('inventory_lots') . '` SET quantity_remaining=?,quantity_received=quantity_received+?,status=?,updated_at=NOW() WHERE id=?')
        ->execute([$after,$receivedIncrease,$status,$lotId]);
    $branchAfter=inventory_reconcile($pdo,$itemId,$branchId);
    $movementNo=inventory_movement_number($pdo);
    $pdo->prepare('INSERT INTO `' . table('inventory_transactions') . '` (item_id,lot_id,branch_id,document_id,document_line_id,movement_no,counterpart_transaction_id,transaction_type,quantity,unit_cost,balance_before,balance_after,status,reference_type,reference_id,notes,user_id,approved_by,posted_at,created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,"posted",?,?,?,?,?,NOW(),NOW())')
        ->execute([$itemId,$lotId,$branchId,$args['document_id']??null,$args['document_line_id']??null,$movementNo,$args['counterpart_transaction_id']??null,$type,$quantity,$args['unit_cost']??null,$before,$after,$args['reference_type']??null,$args['reference_id']??null,$args['notes']??null,(int)(current_user()['id']??0)?:null,$args['approved_by']??null]);
    return (int)$pdo->lastInsertId();
}

function inventory_load_document(PDO $pdo, int $documentId, bool $lock=false): array
{
    $stmt=$pdo->prepare('SELECT * FROM `' . table('inventory_documents') . '` WHERE id=?' . ($lock?' FOR UPDATE':''));$stmt->execute([$documentId]);$doc=$stmt->fetch();
    if(!$doc)throw new RuntimeException('Inventory document not found.');
    $line=$pdo->prepare('SELECT l.*,i.name item_name,i.sku,i.unit FROM `' . table('inventory_document_lines') . '` l JOIN `' . table('inventory_items') . '` i ON i.id=l.item_id WHERE l.document_id=? ORDER BY l.id');$line->execute([$documentId]);
    return [$doc,$line->fetchAll()];
}

function inventory_set_document_status(PDO $pdo, int $documentId, string $expected, string $next, string $note=''): void
{
    $stmt=$pdo->prepare('UPDATE `' . table('inventory_documents') . '` SET status=?,updated_at=NOW() WHERE id=? AND status=?');$stmt->execute([$next,$documentId,$expected]);
    if($stmt->rowCount()!==1)throw new RuntimeException('This inventory document changed in another session. Refresh and try again.');
    inventory_record_history($pdo,$documentId,$expected,$next,$note);
}

function sterilization_record_event(PDO $pdo, int $batchId, ?string $previous, string $next, array $data=[], string $notes=''): void
{
    $pdo->prepare('INSERT INTO `' . table('sterilization_batch_events') . '` (batch_id,previous_status,new_status,event_data_json,notes,changed_by,created_at) VALUES (?,?,?,?,?,?,NOW())')
        ->execute([$batchId,$previous,$next,$data?json_encode($data,JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE):null,$notes!==''?$notes:null,(int)(current_user()['id']??0)?:null]);
}

function inventory_sterilization_completion_schema_ready(): bool
{
    return inventory_schema_ready()
        && schema_table_exists(table('procedure_supply_templates'))
        && schema_table_exists(table('procedure_supply_template_lines'))
        && schema_table_exists(table('clinical_supply_usage_audit'))
        && schema_table_exists(table('inventory_safety_events'))
        && schema_table_exists(table('sterilization_release_reviews'))
        && schema_column_exists(table('inventory_items'), 'is_controlled_supply')
        && schema_column_exists(table('inventory_items'), 'requires_lot_for_issue')
        && schema_column_exists(table('inventory_items'), 'requires_expiry_for_receipt')
        && schema_column_exists(table('inventory_lots'), 'quarantine_reason')
        && schema_column_exists(table('inventory_documents'), 'clinical_consumption_review_status')
        && schema_column_exists(table('sterilization_batches'), 'release_review_status')
        && schema_column_exists(table('instrument_sets'), 'last_release_review_id')
        && schema_column_exists(table('instrument_usage'), 'usage_status')
        && schema_column_exists(table('sterilization_equipment'), 'maintenance_lock_reason');
}

function inventory_completion_count(PDO $pdo, string $sql, array $params = []): int
{
    try {
        $stmt = $pdo->prepare($sql);
        $stmt->execute($params);
        return (int) $stmt->fetchColumn();
    } catch (Throwable $exception) {
        error_log('Inventory completion count failed: ' . $exception->getMessage());
        return 0;
    }
}

function inventory_completion_branch_clause(int $branchId, string $alias = ''): array
{
    $prefix = $alias !== '' ? $alias . '.' : '';
    if ($branchId > 0) return [' AND ' . $prefix . 'branch_id=? ', [$branchId]];
    return ['', []];
}

function inventory_completion_risk_summary(PDO $pdo, int $branchId = 0): array
{
    [$branchClause, $params] = inventory_completion_branch_clause($branchId, 'bs');
    $lowStock = inventory_completion_count($pdo,
        'SELECT COUNT(*) FROM `' . table('inventory_branch_stocks') . '` bs WHERE bs.reorder_level>0 AND bs.current_stock<=bs.reorder_level' . $branchClause,
        $params
    );

    [$lotBranchClause, $lotParams] = inventory_completion_branch_clause($branchId, 'l');
    $expiredLots = inventory_completion_count($pdo,
        'SELECT COUNT(*) FROM `' . table('inventory_lots') . '` l WHERE l.status="active" AND l.quantity_remaining>0 AND l.expiry_date IS NOT NULL AND l.expiry_date<CURDATE()' . $lotBranchClause,
        $lotParams
    );
    $expiringSoon = inventory_completion_count($pdo,
        'SELECT COUNT(*) FROM `' . table('inventory_lots') . '` l WHERE l.status="active" AND l.quantity_remaining>0 AND l.expiry_date BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 30 DAY)' . $lotBranchClause,
        $lotParams
    );

    [$docBranchClause, $docParams] = inventory_completion_branch_clause($branchId, 'd');
    $pendingControlledDocs = inventory_completion_count($pdo,
        'SELECT COUNT(*) FROM `' . table('inventory_documents') . '` d WHERE d.status IN ("draft","submitted","approved","dispatched")' . $docBranchClause,
        $docParams
    );
    $pendingClinicalReview = schema_column_exists(table('inventory_documents'), 'clinical_consumption_review_status')
        ? inventory_completion_count($pdo,
            'SELECT COUNT(*) FROM `' . table('inventory_documents') . '` d WHERE d.document_type="issue" AND d.usage_destination="active_treatment" AND d.clinical_consumption_review_status IN ("pending","needs_review")' . $docBranchClause,
            $docParams
        ) : 0;

    [$equipmentBranchClause, $equipmentParams] = inventory_completion_branch_clause($branchId, 'e');
    $equipmentOverdue = inventory_completion_count($pdo,
        'SELECT COUNT(*) FROM `' . table('sterilization_equipment') . '` e WHERE e.status="active" AND e.next_service_date IS NOT NULL AND e.next_service_date<CURDATE()' . $equipmentBranchClause,
        $equipmentParams
    );

    [$setBranchClause, $setParams] = inventory_completion_branch_clause($branchId, 's');
    $unreturnedSets = inventory_completion_count($pdo,
        'SELECT COUNT(*) FROM `' . table('instrument_usage') . '` u JOIN `' . table('instrument_sets') . '` s ON s.id=u.instrument_set_id WHERE u.returned_at IS NULL' . $setBranchClause,
        $setParams
    );
    $quarantinedSets = inventory_completion_count($pdo,
        'SELECT COUNT(*) FROM `' . table('instrument_sets') . '` s WHERE s.status="quarantined"' . $setBranchClause,
        $setParams
    );

    [$batchBranchClause, $batchParams] = inventory_completion_branch_clause($branchId, 'b');
    $blockedBatches = schema_column_exists(table('sterilization_batches'), 'release_review_status')
        ? inventory_completion_count($pdo,
            'SELECT COUNT(*) FROM `' . table('sterilization_batches') . '` b WHERE b.release_review_status IN ("pending","quarantined","failed")' . $batchBranchClause,
            $batchParams
        ) : 0;

    return [
        'low_stock' => $lowStock,
        'expired_lots' => $expiredLots,
        'expiring_soon' => $expiringSoon,
        'pending_controlled_documents' => $pendingControlledDocs,
        'pending_clinical_consumption_review' => $pendingClinicalReview,
        'equipment_service_overdue' => $equipmentOverdue,
        'unreturned_instrument_sets' => $unreturnedSets,
        'quarantined_instrument_sets' => $quarantinedSets,
        'blocked_sterilization_batches' => $blockedBatches,
        'critical_total' => $expiredLots + $equipmentOverdue + $blockedBatches + $quarantinedSets,
        'warning_total' => $lowStock + $expiringSoon + $pendingControlledDocs + $pendingClinicalReview + $unreturnedSets,
    ];
}

function inventory_completion_card_html(array $summary): string
{
    $items = [
        'Low/out stock' => $summary['low_stock'] ?? 0,
        'Expired lots' => $summary['expired_lots'] ?? 0,
        'Expiring ≤30 days' => $summary['expiring_soon'] ?? 0,
        'Pending documents' => $summary['pending_controlled_documents'] ?? 0,
        'Clinical supply review' => $summary['pending_clinical_consumption_review'] ?? 0,
        'Equipment overdue' => $summary['equipment_service_overdue'] ?? 0,
        'Unreturned sterile sets' => $summary['unreturned_instrument_sets'] ?? 0,
        'Quarantined sets' => $summary['quarantined_instrument_sets'] ?? 0,
        'Blocked loads' => $summary['blocked_sterilization_batches'] ?? 0,
    ];
    $html = '<div class="card inventory-safety-snapshot"><div class="section-heading minor"><div><h3>Inventory and sterilization safety snapshot</h3><p>Use this as the what-if guard before posting stock movement or releasing instruments.</p></div><span class="badge">v0.5.9.8.46</span></div><div class="metric-grid compact">';
    foreach ($items as $label => $value) {
        $class = ((int) $value > 0 && in_array($label, ['Expired lots','Equipment overdue','Quarantined sets','Blocked loads'], true)) ? ' danger-text' : '';
        $html .= '<div><small>' . e($label) . '</small><strong class="' . $class . '">' . e((string) (int) $value) . '</strong></div>';
    }
    $html .= '</div></div>';
    return $html;
}

// DentalCare v0.6.8: inventory-to-treatment readiness helpers.
if (!function_exists('inventory_treatment_readiness_summary')) {
    function inventory_treatment_readiness_summary(PDO $pdo, ?int $encounterId = null, ?int $branchId = null): array
    {
        $summary = [
            'schema_ready' => false,
            'clinical_supply_count' => 0,
            'low_stock_count' => 0,
            'encounter_usage_count' => 0,
            'confirmed_unissued_count' => 0,
            'ready' => false,
        ];
        try {
            $summary['schema_ready'] = schema_table_exists(table('inventory_items'))
                && schema_table_exists(table('inventory_branch_stocks'))
                && schema_table_exists(table('inventory_documents'))
                && schema_table_exists(table('inventory_document_lines'))
                && schema_table_exists(table('clinical_usage_items'));
            if (!$summary['schema_ready']) return $summary;

            $itemWhere = schema_column_exists(table('inventory_items'), 'is_clinical_supply') ? 'WHERE is_clinical_supply=1 AND status="active"' : 'WHERE status="active"';
            $summary['clinical_supply_count'] = (int)$pdo->query('SELECT COUNT(*) FROM `' . table('inventory_items') . '` ' . $itemWhere)->fetchColumn();

            if ($branchId && $branchId > 0 && schema_table_exists(table('inventory_branch_stocks'))) {
                $stmt = $pdo->prepare('SELECT COUNT(*) FROM `' . table('inventory_branch_stocks') . '` bs JOIN `' . table('inventory_items') . '` i ON i.id=bs.item_id WHERE bs.branch_id=? AND i.status="active" AND COALESCE(bs.current_stock,0) <= COALESCE(bs.reorder_level,0)');
                $stmt->execute([$branchId]);
                $summary['low_stock_count'] = (int)$stmt->fetchColumn();
            }

            if ($encounterId && $encounterId > 0) {
                $stmt = $pdo->prepare('SELECT COUNT(*) FROM `' . table('clinical_usage_items') . '` WHERE encounter_id=? AND fulfillment_status<>"cancelled"');
                $stmt->execute([$encounterId]);
                $summary['encounter_usage_count'] = (int)$stmt->fetchColumn();

                $sql = 'SELECT COUNT(*) FROM `' . table('clinical_usage_items') . '` u '
                    . 'WHERE u.encounter_id=? AND u.fulfillment_status="confirmed" AND u.inventory_item_id IS NOT NULL '
                    . 'AND NOT EXISTS (SELECT 1 FROM `' . table('inventory_documents') . '` d JOIN `' . table('inventory_document_lines') . '` dl ON dl.document_id=d.id '
                    . 'WHERE d.document_type="issue" AND d.status="posted" AND d.encounter_id=u.encounter_id AND dl.item_id=u.inventory_item_id)';
                $stmt = $pdo->prepare($sql);
                $stmt->execute([$encounterId]);
                $summary['confirmed_unissued_count'] = (int)$stmt->fetchColumn();
            }

            $summary['ready'] = $summary['schema_ready'] && $summary['clinical_supply_count'] > 0;
            return $summary;
        } catch (Throwable) {
            return $summary;
        }
    }
}

if (!function_exists('inventory_treatment_readiness_ready')) {
    function inventory_treatment_readiness_ready(PDO $pdo): bool
    {
        $summary = inventory_treatment_readiness_summary($pdo);
        return !empty($summary['schema_ready']);
    }
}

if (!function_exists('inventory_render_treatment_guard')) {
    function inventory_render_treatment_guard(PDO $pdo, ?int $encounterId = null, ?int $branchId = null): void
    {
        $summary = inventory_treatment_readiness_summary($pdo, $encounterId, $branchId);
        $statusClass = !empty($summary['ready']) ? 'success' : 'warning';
        $title = !empty($summary['ready']) ? 'Inventory-to-treatment traceability ready' : 'Inventory-to-treatment setup needs review';
        echo '<div class="notice ' . e($statusClass) . ' clinical-supply-guard">';
        echo '<strong>' . e($title) . '</strong><br>';
        if (empty($summary['schema_ready'])) {
            echo 'Required inventory or clinical usage tables are not ready. Ask the Superuser to apply pending updates.';
        } else {
            echo 'Clinical supply catalog: <strong>' . e((string)$summary['clinical_supply_count']) . '</strong>';
            if ($branchId && $branchId > 0) echo ' · Branch low/reorder warnings: <strong>' . e((string)$summary['low_stock_count']) . '</strong>';
            if ($encounterId && $encounterId > 0) echo ' · This encounter usage lines: <strong>' . e((string)$summary['encounter_usage_count']) . '</strong> · Confirmed but not inventory-issued: <strong>' . e((string)$summary['confirmed_unissued_count']) . '</strong>';
            echo '<br><span class="muted">Record actual medicines/materials in the clinical usage section; Inventory posts the official stock issue so treatment, billing, and stock remain reconcilable.</span>';
        }
        echo '</div>';
    }
}
