## ----setup, include = FALSE---------------------------------------------------
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  eval = FALSE
)

## ----connection_setup---------------------------------------------------------
# library(multichainr)
# 
# # 1.1.1 Path Configuration
# # Specify the directory containing MultiChain executables (multichaind, multichain-util).
# # This ensures the R session can interact with the underlying blockchain engine.
# mc_set_path(Sys.getenv("MULTICHAIN_PATH"))
# 
# # 1.1.2 Unique Chain Initialization
# # We generate a unique name for our pharmaceutical ledger to avoid collision
# # with existing validated environments.
# chain_name <- paste0("pharma_chain_", round(as.numeric(Sys.time())))
# 
# # Create the blockchain configuration and start the node as a background daemon.
# mc_node_init(chain_name)
# mc_node_start(chain_name)
# 
# # 1.1.3 Establishing the communication link
# # mc_get_config reads the automatically generated 'multichain.conf' to retrieve
# # the RPC username, password, and port required for authentication.
# conf <- mc_get_config(chain_name)
# conn <- mc_connect(conf)
# 
# # 1.1.4 Verification of Initialization Status
# # We must confirm the node is fully initialized before proceeding with
# # regulatory data entries.
# status <- mc_get_init_status(conn)
# if (status$initialized) {
#   message("Infrastructure Status: Blockchain network is fully initialized and ready for GxP operations.")
# }

## ----departmental_identities--------------------------------------------------
# # 1.2.1 Quality Assurance (QA) Address
# # The QA department acts as the 'Document Owner' and 'System Administrator'.
# addr_qa <- mc_get_new_address(conn)
# 
# # 1.2.2 Production Department Address
# # The Production unit represents the 'Data Consumers' who execute the SOPs.
# addr_prod <- mc_get_new_address(conn)
# 
# # 1.2.3 Regulatory Auditor Address
# # Represents an external inspector (e.g., FDA or EMA) who requires read-only access.
# addr_auditor <- mc_get_new_address(conn)
# 
# cat("QA Department Digital Signature Address:     ", addr_qa, "\n")
# cat("Production Department Digital Identity:      ", addr_prod, "\n")
# cat("External Regulatory Auditor Access Point:   ", addr_auditor, "\n")

## ----permission_governance----------------------------------------------------
# # 1.3.1 Identify the Master Administrator
# # At genesis, the address that initialized the chain holds 'admin' rights.
# admin_perms <- mc_list_permissions(conn, "admin")
# master_admin <- admin_perms$address[1]
# 
# # 1.3.2 QA Privileges: FULL MANAGEMENT
# # QA is granted 'send' (publish data), 'receive' (interact), and 'create' (spawn new registries).
# mc_grant(conn, addr_qa, "send,receive,create")
# 
# # 1.3.3 Production Privileges: RESTRICTED ACCESS
# # Production is restricted to 'receive' only, preventing them from
# # tampering with the Master Document Registry.
# mc_grant(conn, addr_prod, "receive")
# 
# # 1.3.4 Auditor Privileges: READ-ONLY OBSERVATION
# # Auditors are granted 'receive' to monitor transactions without
# # having authority to alter the ledger.
# mc_grant(conn, addr_auditor, "receive")

## ----verification-------------------------------------------------------------
# # 1.4.1 Logical Verification
# # Ensure that the Change Control process can be initiated by QA but not by unauthorized parties.
# can_qa_publish <- mc_verify_permission(conn, addr_qa, "send")
# can_prod_read <- mc_verify_permission(conn, addr_prod, "receive")
# 
# if (can_qa_publish && can_prod_read) {
#   message("Quality Control: RBAC model successfully deployed.")
# } else {
#   # If the permissions are not correctly set, we stop the workflow to prevent compliance breaches.
#   stop("Regulatory Alert: Permission mismatch detected. The validated session cannot continue.")
# }
# 
# # 1.4.2 Documenting the Initial State
# # We pull a full list of active permissions to be included in the validation report.
# audit_trail_permissions <- mc_list_permissions(conn)
# print(audit_trail_permissions)

## ----registry_initialization--------------------------------------------------
# # 2.1.1 Define Registry Metadata
# # These fields provide context for the entire SOP ledger, such as the facility
# # location and the governing quality standards.
# registry_metadata <- list(
#   site_id = "PLANT-01",
#   quality_standard = "ISO-9001:2015 / GMP",
#   department_owner = "Quality Assurance"
# )
# 
# # 2.1.2 Create a Restricted Stream
# # 'open = FALSE' is critical: it prevents the 'Production' department or
# # any guest from writing to this stream. Only QA will be granted 'write' access.
# tx_stream_id <- mc_create_stream_from(
#   conn,
#   from_address = addr_qa,
#   name = "SOP_Registry",
#   open = FALSE,                  # Enforce strict write-access control
#   custom_fields = registry_metadata
# )
# 
# message("Registry Status: Master SOP Stream created. Transaction ID: ", tx_stream_id)
# 
# # 2.1.3 Ensure Data Persistence
# # We block execution until the registry creation is confirmed in a block.
# # This ensures that the registry exists before we attempt to grant write permissions.
# mc_wait_for_confirmation(conn, tx_stream_id)

## ----inspect_registry---------------------------------------------------------
# # Retrieve detailed technical metadata about the "SOP_Registry"
# sop_registry_info <- mc_get_stream_info(conn, "SOP_Registry", verbose = TRUE)
# 
# # The 'createtxid' acts as the permanent digital fingerprint of this registry.
# print(sop_registry_info)
# 
# # The 'restrict' field in the output confirms that 'write' access is NOT public.

## ----list_registries----------------------------------------------------------
# all_registries <- mc_list_streams(conn)
# 
# # Displaying all active document registries currently hosted on this node.
# print(all_registries[, c("name", "createtxid", "subscribed")])

## ----synchronize_registry-----------------------------------------------------
# # 2.4.1 Activate Indexing
# # Subscribing tells the node to start building a local database of the stream's contents.
# # 'rescan = TRUE' ensures that even if we joined the network late, we download all
# # historical SOP records from the genesis block.
# mc_subscribe(conn, "SOP_Registry", rescan = TRUE)
# 
# # 2.4.2 Verify Synchronization Status
# updated_list <- mc_list_streams(conn, "SOP_Registry")
# if (updated_list$subscribed[1]) {
#   message("Compliance Sync: Node is now actively indexing the SOP_Registry.")
# }

## ----authorize_qa-------------------------------------------------------------
# # 2.5.1 Grant Write Access
# # We use the master_admin (from Section 1) to authorize the QA address.
# mc_grant(conn, addr_qa, "SOP_Registry.write")
# 
# # 2.5.2 Operation Verification Check
# # Perform a final check before opening the registry for data entry.
# if (mc_verify_permission(conn, addr_qa, "SOP_Registry.write")) {
#   message("GxP Readiness: QA Department successfully authorized to manage SOP records.")
# }

## ----publishing_metadata------------------------------------------------------
# # 3.1.1 Define SOP Attributes
# # We define the document key (SOP-QC-001) and its regulatory attributes.
# sop_key <- "SOP-QC-001"
# sop_metadata <- list(
#   json = list(
#     title = "Standard Procedure for Raw Material Sampling",
#     version = "1.0",
#     file_hash = "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
#     format = "PDF",
#     classification = "Restricted"
#   )
# )
# 
# # 3.1.2 Attributable Publication
# # By using mc_publish_from with addr_qa, we satisfy the 'Attributable' requirement.
# # The blockchain will forever link this record to the QA Department's digital identity.
# tx_pub_1 <- mc_publish_from(conn, addr_qa, "SOP_Registry", sop_key, sop_metadata)
# 
# message("Audit Trail: SOP-QC-001 version 1.0 registered. Transaction ID: ", tx_pub_1)

## ----atomic_publishing--------------------------------------------------------
# # 3.2.1 Define Linked Records
# # We bundle the SOP registration and the Personnel Assignment into a single list.
# multi_items <- list(
#   # Item 1: The document metadata
#   list(
#     key = "SOP-PROD-002",
#     data = list(json = list(title = "Cleaning Validation", version = "2.0"))
#   ),
#   # Item 2: The assignment of responsibility
#   list(
#     key = "ASSIGNMENT-PROD-002",
#     data = list(json = list(assignee = "Production_Manager_01", role = "Custodian"))
#   )
# )
# 
# # 3.2.2 Atomic Execution
# # mc_publish_multi_from ensures that both items share the exact same Block Time and TXID.
# tx_pub_multi <- mc_publish_multi_from(conn, addr_qa, "SOP_Registry", multi_items)
# 
# message("Compliance: Atomic SOP registration and Custodian assignment complete. TXID: ", tx_pub_multi)

## ----binary_cache_workflow----------------------------------------------------
# # 3.3.1 Initialize Cache Item
# # Create a temporary, unique staging identifier on the MultiChain node.
# cache_id <- mc_create_binary_cache(conn)
# 
# # 3.3.2 Data Upload (Staging)
# # Simulate a large manufacturing specification (e.g., detailed equipment settings).
# large_spec_content <- paste0("MBR_START_", paste(rep("DATA_SEGMENT_", 1000), collapse=""), "_MBR_END")
# 
# # Upload the data to the node's local cache.
# # The function returns the total size in bytes once the upload is confirmed.
# actual_size <- mc_append_binary_cache(conn, cache_id, list(text = large_spec_content))
# message("System: Large document staged in binary cache. Size: ", actual_size, " bytes.")
# 
# # 3.3.3 Commit Staged Data to Blockchain
# # We publish a reference to the cache item. The node will automatically
# # retrieve the binary data and wrap it into a permanent transaction.
# sop_large_key <- "SOP-TECH-003"
# tx_pub_cache <- mc_publish_from(conn, addr_qa, "SOP_Registry",
#                                 sop_large_key,
#                                 list(cacheitem = cache_id))
# 
# # 3.3.4 Buffer Cleanup
# # Once the data is recorded in the blockchain, the temporary cache item is deleted
# # to maintain node hygiene and storage efficiency.
# mc_delete_binary_cache(conn, cache_id)
# 
# message("Data Integrity: Technical Dossier SOP-TECH-003 committed to ledger. TXID: ", tx_pub_cache)

## ----verify_publishing--------------------------------------------------------
# # 3.4.1 Wait for Confirmation
# # Ensure the last transaction is included in a block and confirmed by the network validators.
# mc_wait_for_confirmation(conn, tx_pub_cache)
# 
# # 3.4.2 Retrieve and Verify Metadata
# # We use mc_get_stream_item to pull the original JSON record by its TXID.
# item_info <- mc_get_stream_item(conn, "SOP_Registry", tx_pub_1)
# 
# # Output the verified data to show that the 'Source' remains unchanged.
# cat("--- VERIFIED SOP RECORD ---\n")
# cat("SOP Identifier: ", item_info$key[[1]], "\n")
# cat("Approved Title: ", item_info$data$json$title, "\n")
# cat("Document Hash:   ", item_info$data$json$file_hash, "\n")
# cat("Time of Entry:  ", as.character(as.POSIXct(item_info$blocktime, origin="1970-01-01")), "\n")

## ----digital_signing----------------------------------------------------------
# # 4.1.1 Define the Approval Target
# # We use the SHA-256 hash of the document (from Step 3) as the "message".
# # This ensures that even a single-character change in the PDF would invalidate the signature.
# doc_hash <- "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
# 
# # 4.1.2 Apply Cryptographic Signature
# # mc_sign_message uses the private key associated with addr_qa to sign the hash.
# # This results in a Base64-encoded signature string.
# qa_signature <- mc_sign_message(conn, addr_qa, doc_hash)
# 
# cat("Regulatory Status: SOP-QC-001 has been digitally signed by QA.\n")
# cat("Approval Signature: ", qa_signature, "\n")

## ----signature_verification---------------------------------------------------
# # 4.2.1 Perform Independent Audit
# # The auditor runs mc_verify_message. The blockchain logic returns TRUE
# # only if the signature was indeed generated by the QA address for this specific hash.
# is_legit <- mc_verify_message(conn, addr_qa, qa_signature, doc_hash)
# 
# if (is_legit) {
#   message("Audit Success: Signature is authentic. Document integrity and authorship verified.")
# } else {
#   # In a real environment, this would trigger a major compliance investigation.
#   stop("Compliance Alert: Digital signature verification failed! Data integrity breach.")
# }

## ----training_tokens----------------------------------------------------------
# # 4.3.1 Define the Competency Asset
# training_asset_name <- "TRAIN-QC-001"
# 
# # 4.3.2 Metadata-Enriched Permit
# # We attach training-specific context to the asset itself, fulfilling ALCOA+
# # requirements for comprehensive documentation.
# cert_metadata <- list(
#   sop_reference = "SOP-QC-001",
#   trainer = "Lead_Pharmacist_01",
#   training_method = "Read and Understand",
#   expiry_date = "2027-04-03"
# )
# 
# # 4.3.3 Distribute the Permit
# # We issue 1 unit of the permit directly to the Production Department.
# tx_issue_token <- mc_issue(
#   conn,
#   address = addr_prod,
#   name = training_asset_name,
#   quantity = 1,                 # One permit per training session
#   units = 1,                    # Asset is non-divisible
#   custom_fields = cert_metadata
# )
# 
# message("Learning Management: Training permit issued to Production. TXID: ", tx_issue_token)
# mc_wait_for_confirmation(conn, tx_issue_token)

## ----access_control-----------------------------------------------------------
# # 4.4.1 Query Active Qualifications
# # We check the balances for the Production Department's wallet.
# prod_balances <- mc_get_address_balances(conn, addr_prod)
# 
# # 4.4.2 Decision Logic
# if (any(prod_balances$name == training_asset_name)) {
#   current_token <- prod_balances[prod_balances$name == training_asset_name, ]
#   message("Access Control: Production Department is QUALIFIED for this operation.")
# 
#   # Display the token details for the operator
#   print(current_token[, c("name", "qty")])
# } else {
#   stop("Access Denied: Required training permit not found in the department wallet.")
# }

## ----validation_library-------------------------------------------------------
# # 5.1.1 Define Reusable JavaScript Logic
# # This function uses a Regular Expression to ensure the ID starts with 'SOP-'
# # followed by numeric digits.
# lib_js <- "
# function isValidSopFormat(key) {
#     var regex = /^SOP-\\d+$/;
#     return regex.test(key);
# }
# "
# 
# # 5.1.2 Deploy the Library
# # We use 'instant' update mode so the logic is available immediately for our demo.
# tx_lib <- mc_create_library(conn, "PharmaUtils", updatemode = "instant", js_code = lib_js)
# message("System: Validation library 'PharmaUtils' deployed. TXID: ", tx_lib)

## ----filter_simulation--------------------------------------------------------
# # 5.2.1 Define Test Logic
# # In this simulation, we check for a specific prefix.
# test_filter_js <- "
# function filterstreamitem() {
#     var item = getfilterstreamitem();
#     var primaryKey = item.keys[0];
# 
#     if (primaryKey.indexOf('SOP-') !== 0) {
#         return 'Invalid Prefix: ' + primaryKey; // Returning a string REJECTS the TX
#     }
#     return null; // Returning null ACCEPTS the TX
# }
# "
# 
# # 5.2.2 Run Simulation
# # We test the logic against tx_pub_1 (our valid record from Step 3).
# sim_res <- mc_test_stream_filter(conn, list(libraries = list()),
#                                  test_filter_js, tx = tx_pub_1, vout = 0)
# 
# # 5.2.3 Interpret Simulation Results
# if (sim_res$compiled) {
#     message("CSV Verification: JavaScript logic is syntactically valid.")
# }
# 
# if (sim_res$passed) {
#     message("Simulation Result: The transaction was ACCEPTED (Compliant data).")
# } else {
#     cat("Simulation Result: REJECTED. Reason given by system:", sim_res$reason, "\n")
# }

## ----production_filter--------------------------------------------------------
# # 5.3.1 Define Complex Multi-Step Validation
# filter_js <- "
# function filterstreamitem() {
#     var item = getfilterstreamitem();
#     var primaryKey = item.keys[0];
# 
#     // Rule 1: Check ID format using the PharmaUtils library
#     if (!isValidSopFormat(primaryKey)) {
#         return 'Rejected: Key ' + primaryKey + ' does not match SOP-XXX format.';
#     }
# 
#     // Rule 2: Enforce Version Traceability (Change Control)
#     var data = item.data.json;
#     if (data && data.is_update === true && !data.prev_version_txid) {
#         return 'Rejected: SOP update must contain a reference to the previous version TXID.';
#     }
# 
#     return null; // The record is GxP compliant
# }
# "
# 
# # 5.3.2 Global Filter Creation
# # We create the filter and declare its dependency on the 'PharmaUtils' library.
# tx_filter <- mc_create_stream_filter(conn, "SopChainValidator",
#                                      options = list(libraries = list("PharmaUtils")),
#                                      js_code = filter_js)
# 
# message("Compliance Engineering: Stream filter created. TXID: ", tx_filter)
# mc_wait_for_confirmation(conn, tx_filter)

## ----filter_activation--------------------------------------------------------
# # 5.4.1 Grant Stream-Level Admin Rights
# # The master administrator must be authorized to manage this specific registry.
# grant_admin_tx <- mc_grant(conn, master_admin, "SOP_Registry.admin")
# mc_wait_for_confirmation(conn, grant_admin_tx)
# 
# # 5.4.2 Official Attachment
# # Attach the 'SopChainValidator' logic specifically to the 'SOP_Registry' stream.
# attachment_logic <- list("for" = "SOP_Registry", approve = TRUE)
# 
# tx_appr <- mc_approve_from(conn, master_admin, "SopChainValidator",
#                            approve = attachment_logic)
# 
# message("Regulatory Governance: Filter officially attached to SOP_Registry by Admin.")
# mc_wait_for_confirmation(conn, tx_appr)

## ----negative_test------------------------------------------------------------
# bad_sop_data <- list(json = list(title = "Non-compliant entry", version = "2.0"))
# 
# tryCatch({
#     # Attempting to publish with a bad key 'sop-123'
#     mc_publish_from(conn, addr_qa, "SOP_Registry", "sop-123", bad_sop_data)
# }, error = function(e) {
#     # The blockchain rejects the transaction and R captures the error message
#     message("Success: The Smart Filter blocked the non-compliant entry!")
#     cat("Blockchain Error Message: ", e$message, "\n")
# })

## ----positive_test------------------------------------------------------------
# good_sop_data <- list(
#   json = list(
#     title = "Validated Sampling Plan",
#     version = "1.0",
#     is_update = FALSE # Satisfies the 'no previous version required' logic
#   )
# )
# 
# # This transaction passes the format check and the change control check.
# tx_good <- mc_publish_from(conn, addr_qa, "SOP_Registry", "SOP-999", good_sop_data)
# message("Compliance: Valid record accepted by the ledger. TXID: ", tx_good)

## ----create_vmp_variable------------------------------------------------------
# # 6.1.1 Define the Variable Name
# # The VMP ID is a cornerstone of pharmaceutical facility management.
# vmp_name <- "Current_VMP_ID"
# 
# # 6.1.2 Create the Variable
# # Protocol Note: We set open = TRUE as required by the MultiChain 2.x protocol.
# # Security Note: Access is still controlled; only nodes with 'create'
# # permissions can initialize these global objects.
# tx_var_create <- mc_create_variable(
#   conn,
#   name = vmp_name,
#   open = TRUE,
#   value = "VMP-2026-SITE01"
# )
# 
# message("QMS Initialization: Global VMP Variable created. TXID: ", tx_var_create)
# 
# # Standard GxP practice: wait for confirmation before attempting to use the new object.
# mc_wait_for_confirmation(conn, tx_var_create)

## ----system_status_tracking---------------------------------------------------
# # 6.2.1 Initialize Operational Status
# status_var <- "QMS_Operational_Status"
# tx_status_init <- mc_create_variable(conn, status_var, open = TRUE, value = "Normal Operations")
# mc_wait_for_confirmation(conn, tx_status_init) # Brief pause for ledger indexing
# 
# # 6.2.2 Update State (Contemporaneous Documentation)
# # Scenario: An FDA inspector arrives at the site. We update the global state.
# # This creates a permanent record of the exact moment the audit mode began.
# tx_var_update <- mc_set_variable_value(conn, status_var, value = "FDA Audit in Progress")
# 
# message("Compliance Event: System status updated to 'FDA Audit in Progress'. TXID: ", tx_var_update)

## ----variable_history---------------------------------------------------------
# # 6.3.1 Retrieve Latest State
# current_status <- mc_get_variable_value(conn, status_var)
# cat("Current Validated Status: ", current_status, "\n")
# 
# # 6.3.2 Extract Historical Audit Trail
# # 'verbose = TRUE' is used to see the 'writers' (addresses) and timestamps.
# status_history <- mc_get_variable_history(conn, status_var, verbose = TRUE)
# 
# # We display the 'writers' column to prove who authorized each state change.
# print(status_history[, c("blocktime", "writers", "value")])

## ----node_configuration-------------------------------------------------------
# # 6.4.1 Set Technical Limits
# # We adjust 'maxshowndata' to 5001 bytes. This ensures that any technical
# # metadata displayed in our QMS dashboard is truncated to a manageable size,
# # preventing log-flooding while maintaining the integrity of the underlying data.
# mc_set_runtime_param(conn, "maxshowndata", 5001)
# 
# message("IT Governance: Node runtime parameter 'maxshowndata' enforced.")
# 
# # 6.4.2 Verify Active Parameters
# # It is vital to document that the technical controls are active.
# runtime_params <- mc_get_runtime_params(conn)
# cat("Active System Limit (maxshowndata): ", runtime_params$maxshowndata, " bytes.\n")

## ----system_snapshot----------------------------------------------------------
# # 6.5.1 Capture Environment Metadata
# node_info <- mc_get_info(conn)
# 
# cat("--- VALIDATED INFRASTRUCTURE SNAPSHOT ---\n")
# cat("Software Version:   ", node_info$version, "\n")
# cat("Blockchain Height:  ", node_info$blocks, "\n")
# cat("Network Node ID:    ", node_info$nodeaddress, "\n")

## ----exchange_setup-----------------------------------------------------------
# # 7.1.1 Define Permit Version Names
# # In a GxP system, these represent specific training qualifications.
# token_v1 <- "TRAIN-QC-003-V1"
# token_v2 <- "TRAIN-QC-003-V2"
# 
# # 7.1.2 Initial Distribution (Baseline)
# # Production holds the old version that needs to be surrendered.
# tx_v1 <- mc_issue(conn, addr_prod, token_v1, 1)
# # QA holds the new approved version ready for distribution.
# tx_v2 <- mc_issue(conn, addr_qa, token_v2, 1)
# 
# # Ensure the ledger reflects these issuances before attempting the swap.
# mc_wait_for_confirmation(conn, tx_v1)
# mc_wait_for_confirmation(conn, tx_v2)
# 
# message("Pre-requisite: Superseded and Active permits confirmed on-chain.")

## ----qa_locking---------------------------------------------------------------
# # 7.2.1 Define the isolated quantity
# # We must use dynamic list assignment in R to ensure the variable evaluates
# # to the asset name string "TRAIN-QC-003-V2".
# amounts_v2 <- list()
# amounts_v2[[token_v2]] <- 1
# 
# # 7.2.2 Isolate the asset (Locking)
# # mc_prepare_lock_unspent_from moves the asset into a 'reserved' state.
# locked_v2 <- mc_prepare_lock_unspent_from(
#   conn,
#   from_address = addr_qa,
#   amounts = amounts_v2
# )
# 
# message("Quality Assurance: New permit (V2) isolated and locked for issuance.")

## ----exchange_offer-----------------------------------------------------------
# # 7.3.1 Define the 'Return' requirement
# # QA requests exactly 1 unit of the superseded version (V1) in return.
# amounts_v1_req <- list()
# amounts_v1_req[[token_v1]] <- 1
# 
# # 7.3.2 Construct the Offer
# # This returns a raw hexadecimal string representing a 'Proposal'.
# offer_hex <- mc_create_raw_exchange(
#   conn,
#   txid = locked_v2$txid,
#   vout = locked_v2$vout,
#   amounts = amounts_v1_req
# )
# 
# cat("System: Atomic Exchange Offer generated (Partial Hex created).\n")

## ----production_surrender-----------------------------------------------------
# # 7.4.1 Grant Surrender Authorization
# # To return a permit, the address must have 'send' and 'receive' permissions.
# grant_tx <- mc_grant(conn, addr_prod, "send,receive")
# mc_wait_for_confirmation(conn, grant_tx)
# 
# # 7.4.2 Isolate the Superseded Asset
# amounts_v1_own <- list()
# amounts_v1_own[[token_v1]] <- 1
# 
# # Production locks their V1 permit to signify readiness for the swap.
# locked_v1 <- mc_prepare_lock_unspent_from(
#   conn,
#   from_address = addr_prod,
#   amounts = amounts_v1_own
# )
# 
# message("Production: Superseded permit (V1) locked and ready for decommissioning.")

## ----finalize_exchange--------------------------------------------------------
# # 7.5.1 Define the Receipt
# # Production confirms they are receiving 1 unit of V2.
# amounts_v2_receive <- list()
# amounts_v2_receive[[token_v2]] <- 1
# 
# # 7.5.2 Complete the Multi-Party Transaction
# # mc_complete_raw_exchange merges QA's offer with Production's acceptance.
# # We include a JSON metadata object for the Audit Trail.
# final_tx_hex <- mc_complete_raw_exchange(
#   conn,
#   tx_hex = offer_hex,
#   txid = locked_v1$txid,
#   vout = locked_v1$vout,
#   amounts = amounts_v2_receive,
#   data = list(
#     change_control_id = "CC-2026-0045",
#     rationale = "Replacing V1 with V2 due to annual document review."
#   )
# )
# 
# message("Compliance: Atomic exchange finalized and ready for network commitment.")

## ----broadcast_exchange-------------------------------------------------------
# # 7.6.1 Broadcast the Final Transaction
# swap_txid <- mc_send_raw_transaction(conn, final_tx_hex)
# 
# # 7.6.2 Confirm Perpetual Record
# mc_wait_for_confirmation(conn, swap_txid)
# 
# message("Data Integrity: SOP Version Exchange complete! Transaction ID: ", swap_txid)
# 
# # 7.6.3 Audit Verification
# # Verification of final balances to prove compliance
# prod_bal <- mc_get_address_balances(conn, addr_prod)
# if (token_v2 %in% prod_bal$name && !(token_v1 %in% prod_bal$name)) {
#     message("Regulatory Verification: Change Control successfully enforced. V1 removed, V2 active.")
# }

## ----blockchain_integrity-----------------------------------------------------
# # 8.1.1 Retrieve Global Ledger Status
# # Get the current technical state of the blockchain.
# chain_info <- mc_get_blockchain_info(conn)
# cat("Current Facility Ledger Height (Total Blocks): ", chain_info$blocks, "\n")
# 
# # 8.1.2 Locate a Specific Record in the "Vault"
# # We pick our initial SOP registration (tx_pub_1 from Step 3).
# tx_audit_info <- mc_get_wallet_transaction(conn, tx_pub_1)
# block_height <- tx_audit_info$blockheight
# 
# # 8.1.3 Retrieve the Raw Block Data
# # We extract the specific block that "sealed" our SOP record.
# sop_block <- mc_get_block(conn, block_height)
# 
# # This hash is the unique, immutable fingerprint of the entire block.
# cat("Regulatory Proof: SOP-QC-001 is sealed in Block Hash: ", sop_block$hash, "\n")
# # Documenting the exact time the record became official.
# cat("Block Timestamp (Contemporaneous Entry):      ",
#     as.character(as.POSIXct(sop_block$time, origin="1970-01-01")), "\n")

## ----master_list--------------------------------------------------------------
# # 8.2.1 Generate the Document Inventory
# # This acts as the "Table of Contents" for the SOP Registry.
# sop_registry_list <- mc_list_stream_keys(conn, "SOP_Registry")
# 
# # We display the unique document IDs and the number of versions (items) for each.
# # 'confirmed' shows how many have been finalized in the blockchain.
# print(sop_registry_list[, c("key", "items", "confirmed")])

## ----document_traceability----------------------------------------------------
# # 8.3.1 Extract the Full Lifecycle of a specific SOP
# # mc_list_stream_key_items retrieves every transaction associated with this key.
# sop_history <- mc_list_stream_key_items(conn, "SOP_Registry", "SOP-QC-001")
# 
# # 8.3.2 Display the Audit Trail
# # We format the blocktime to a human-readable date for the audit report.
# sop_history$timestamp <- as.POSIXct(sop_history$blocktime, origin="1970-01-01")
# 
# # The 'publishers' column proves 'Who', 'timestamp' proves 'When', and 'txid' is the 'Proof'.
# print(sop_history[, c("timestamp", "txid", "publishers")])

## ----single_source_truth------------------------------------------------------
# # 8.4.1 Aggregate the Latest Metadata
# # This function collapses all versions into one "Current Valid State" object.
# current_sop_state <- mc_get_stream_key_summary(conn, "SOP_Registry", "SOP-QC-001")
# 
# cat("--- CURRENT EFFECTIVE SOP DASHBOARD ---\n")
# cat("SOP Title:             ", current_sop_state$title, "\n")
# cat("Effective Version:     ", current_sop_state$version, "\n")
# cat("Validated File Hash:   ", current_sop_state$file_hash, "\n")
# # This data can be used to verify the physical PDF file before a production run.

## ----publisher_audit----------------------------------------------------------
# # 8.5.1 Generate Action Report for QA Department
# # We list every record ever published by the QA address (addr_qa).
# qa_audit_trail <- mc_list_stream_publisher_items(conn, "SOP_Registry", addr_qa)
# 
# message("Compliance Report: Actions performed by QA Department (", addr_qa, "):")
# cat("Total regulatory records initiated: ", nrow(qa_audit_trail), "\n")
# 
# # Provide the most recent 5 actions for quick review
# qa_audit_trail$date <- as.POSIXct(qa_audit_trail$blocktime, origin="1970-01-01")
# print(head(qa_audit_trail[, c("date", "keys", "txid")], 5))

## ----system_oversight---------------------------------------------------------
# # 8.6.1 Retrieve Global Chain Totals
# # This provides a snapshot of the volume of data in the QMS.
# totals <- mc_get_chain_totals(conn)
# 
# cat("--- FACILITY QMS OVERVIEW ---\n")
# cat("Total SOP Registry Entries: ", totals$streams, "\n")
# cat("Total Digital Permits Issued: ", totals$assets, "\n")
# cat("Total Authorized Addresses:  ", length(mc_get_addresses(conn)), "\n")

## ----discovery----------------------------------------------------------------
# # 9.1.1 Scan Departmental Inventory
# # We retrieve all unspent transaction outputs (UTXOs) for the Production address.
# unspent_prod <- mc_list_unspent(conn, addresses = addr_prod)
# 
# # 9.1.2 Automated Asset Detection
# # We search the list-columns for any asset starting with "TRAIN" (our permit prefix).
# # This logic ensures the workflow is robust against naming variations in versioning.
# discovery <- sapply(unspent_prod$assets, function(row_assets) {
#   if (length(row_assets) == 0) return(NA)
#   names_in_row <- sapply(row_assets, function(a) a$name)
#   match <- names_in_row[grepl("^TRAIN", names_in_row)][1]
#   return(match)
# })
# 
# # Identify the exact record for the manual archive pipeline
# target_row_idx <- which(!is.na(discovery))[1]
# target_asset   <- discovery[target_row_idx]
# target_utxo    <- unspent_prod[target_row_idx, ]
# 
# # 9.1.3 Extract Precision Data
# # We extract the exact quantity using unlist() to ensure we have a plain numeric value.
# # This prevents type-mismatch errors during the raw transaction construction.
# row_assets_list <- target_utxo$assets[[1]][[1]]
# target_qty <- as.numeric(unlist(row_assets_list$qty[row_assets_list$name == target_asset]))
# 
# message("Inventory Management: Discovered active permit '", target_asset, "' (Qty: ", target_qty, ")")
# 
# # Prepare the blockchain input (UTXO pointer)
# inputs <- list(list(
#   txid = target_utxo$txid,
#   vout = as.integer(target_utxo$vout)
# ))

## ----manual_construction------------------------------------------------------
# # 9.2.1 Initialize Secure Archive Address
# archive_addr <- mc_get_new_address(conn)
# 
# # 9.2.2 Regulatory Authorization (The Receive Grant)
# # No address can hold assets without an explicit grant, satisfying CFR 21 Part 11
# # requirements for authorized system access.
# grant_tx <- mc_grant(conn, archive_addr, "receive")
# mc_wait_for_confirmation(conn, grant_tx)
# 
# # 9.2.3 Construct the Balanced Output
# # We map the archive address to the exact asset quantity found in Step 9.1.
# outputs <- list()
# asset_movement <- list()
# asset_movement[[target_asset]] <- target_qty
# outputs[[archive_addr]] <- asset_movement
# 
# # 9.2.4 Generate the Raw Hex
# # This creates a "Draft" transaction that exists only in our R session memory.
# raw_tx_hex <- mc_create_raw_transaction(conn, inputs, outputs)
# 
# message("GxP Engineering: Draft transaction hex constructed for ", target_asset)

## ----data_enrichment----------------------------------------------------------
# # 9.3.1 Define Compliance Metadata
# validation_metadata <- list(
#   validation_run_id = "VAL-RUN-2026-099",
#   inspector_comment = "Archiving permit due to site-wide software update."
# )
# 
# # 9.3.2 Enforce the Audit Trail
# # We append this data to the existing raw hex. The transaction now carries
# # both the asset transfer logic and the regulatory justification.
# raw_tx_hex <- mc_append_raw_data(conn, raw_tx_hex, validation_metadata)
# 
# message("Compliance: Validation metadata successfully embedded into the draft.")

## ----technical_audit----------------------------------------------------------
# # 9.4.1 Transparent Inspection
# # We convert the cryptic hex back into a human-readable R list.
# decoded_tx <- mc_decode_raw_transaction(conn, raw_tx_hex)
# 
# # 9.4.2 Verification of Intent
# cat("--- PRE-SIGNING REGULATORY INSPECTION ---\n")
# cat("Target Archive:    ", decoded_tx$vout[[1]]$scriptPubKey$addresses[[1]], "\n")
# cat("Asset Identified: ", decoded_tx$vout[[1]]$assets[[1]]$name, "\n")
# # We use hex_to_char to read the embedded JSON comment
# cat("Embedded Comment: ", multichainr::hex_to_char(decoded_tx$vout[[2]]$data[[1]]), "\n")

## ----signing------------------------------------------------------------------
# # 9.5.1 Provide Input Context
# # We re-package the original UTXO metadata to assist the signing engine.
# parents_info <- list(list(
#   txid         = target_utxo$txid,
#   vout         = as.integer(target_utxo$vout),
#   scriptPubKey = target_utxo$scriptPubKey[[1]],
#   amount       = as.numeric(target_utxo$amount),
#   assets       = target_utxo$assets[[1]]
# ))
# 
# # 9.5.2 Apply Departmental Signature
# # This finalizes the transaction, linking it to the department's private key.
# signed_obj <- mc_sign_raw_transaction(conn, raw_tx_hex, parents = parents_info)
# 
# if (signed_obj$complete) {
#   message("Digital Signature: Transaction successfully signed and authorized.")
# }

## ----final_commitment---------------------------------------------------------
# # 9.6.1 Broadcast to the Permanent Ledger
# final_txid <- mc_send_raw_transaction(conn, signed_obj$hex)
# mc_wait_for_confirmation(conn, final_txid)
# 
# # 9.6.2 Final Reconciliation Report
# # Verify that Production no longer holds the archived permit.
# archive_bal <- mc_get_address_balances(conn, archive_addr)
# 
# message("Audit Finalization: Permit archiving verified.")
# print(archive_bal[archive_bal$name == target_asset, c("name", "qty")])

## ----backup_prep--------------------------------------------------------------
# # 10.1.1 Define Archive Paths
# # Note: These paths are relative to the machine where the MultiChain daemon is running.
# backup_folder <- tempdir()
# binary_backup_path <- file.path(backup_folder, "Pharma_Wallet_Backup.dat")
# text_dump_path     <- file.path(backup_folder, "Pharma_Keys_Export.txt")
# 
# message("System Administration: Backup paths initialized in validated storage zone.")

## ----binary_backup------------------------------------------------------------
# # 10.2.1 Execute Binary Backup
# # mc_backup_wallet performs a safe, hot-backup of the wallet database.
# mc_backup_wallet(conn, binary_backup_path)
# 
# if (file.exists(binary_backup_path)) {
#   message("Disaster Recovery: Binary wallet backup successfully generated.")
#   cat("Binary Archive Location: ", binary_backup_path, "\n")
# }

## ----key_dump-----------------------------------------------------------------
# # 10.3.1 Export Private Keys
# # mc_dump_wallet creates a text file listing every address and its private key.
# mc_dump_wallet(conn, text_dump_path)
# 
# if (file.exists(text_dump_path)) {
#   message("Data Retention: Human-readable key export completed for safe-deposit archiving.")
#   # SAFETY WARNING: In a GxP environment, this file must be handled with
#   # extreme security as it contains the "Master Keys" to the departmental identities.
# }

## ----final_snapshot-----------------------------------------------------------
# # 10.4.1 Capture Final Ledger Metadata
# final_stats <- mc_get_blockchain_info(conn)
# 
# cat("--- FINAL REGULATORY LEDGER SUMMARY ---\n")
# cat("Chain Name:            ", chain_name, "\n")
# cat("Final Block Height:    ", final_stats$blocks, "\n")
# cat("Protocol Version:      ", final_stats$protocol, "\n")

## ----system_shutdown----------------------------------------------------------
# # 10.5.1 Terminate Blockchain Daemon
# # mc_node_stop sends the SIGTERM signal to the multichaind process.
# mc_node_stop(chain_name)
# 
# # Allow the operating system to release file locks before directory removal.
# Sys.sleep(2)
# 
# # 10.5.2 Post-Audit Cleanup
# # In a real validated environment, the chain_dir would be preserved.
# # Here we remove it to reset the test environment.
# 
# if (.Platform$OS.type == "windows") {
#   base_dir <- file.path(Sys.getenv("APPDATA"), "MultiChain")
# } else if (Sys.info()["sysname"] == "Darwin") {
#   base_dir <- file.path(Sys.getenv("HOME"), "Library/Application Support/MultiChain")
# } else {
#   base_dir <- file.path(Sys.getenv("HOME"), ".multichain")
# }
# 
# chain_dir <- file.path(base_dir, chain_name)
# 
# if (dir.exists(chain_dir)) {
#   unlink(chain_dir, recursive = TRUE)
#   message("IT Compliance: Local session data removed after successful archiving.")
# }
# 
# message("Workflow Status: Pharma SOP Registry session successfully closed.")

