## ----include = FALSE----------------------------------------------------------
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  eval = FALSE
)

## ----prerequisites------------------------------------------------------------
# # 1. Shiny >= 1.12.0 (when OTEL support was added)
# packageVersion("shiny") # Should be >= 1.12.0
# 
# # 2. OpenTelemetry packages
# install.packages("otel")
# install.packages("otelsdk")
# 
# # 3. bidux with OTEL support
# install.packages("bidux") # or development version from GitHub

## ----basic_setup--------------------------------------------------------------
# library(shiny)
# 
# # Enable OTEL collection for all events
# options(shiny.otel.collect = "all")
# 
# # Or use environment variable (set before starting R)
# # Sys.setenv(SHINY_OTEL_COLLECT = "all")
# 
# # Your Shiny app code
# ui <- fluidPage(
#   titlePanel("Sales Dashboard"),
#   sidebarLayout(
#     sidebarPanel(
#       selectInput("region", "Region:",
#                   choices = c("North", "South", "East", "West")),
#       dateRangeInput("date_range", "Date Range:")
#     ),
#     mainPanel(
#       plotOutput("sales_plot")
#     )
#   )
# )
# 
# server <- function(input, output, session) {
#   output$sales_plot <- renderPlot({
#     # Your plotting logic
#   })
# }
# 
# shinyApp(ui, server)

## ----collection_levels--------------------------------------------------------
# # For development and UX analysis - collect everything
# options(shiny.otel.collect = "all")
# 
# # For production with moderate overhead
# options(shiny.otel.collect = "reactive_update")
# 
# # Minimal production tracking
# options(shiny.otel.collect = "session")

## ----file_export--------------------------------------------------------------
# \dontrun{
# library(otel)
# library(otelsdk)
# 
# # Configure OTLP JSON file exporter
# Sys.setenv(
#   OTEL_TRACES_EXPORTER = "otlp",
#   OTEL_EXPORTER_OTLP_PROTOCOL = "http/json",
#   OTEL_EXPORTER_OTLP_ENDPOINT = "/path/to/otel_spans.json"
# )
# 
# # Enable collection
# options(shiny.otel.collect = "all")
# 
# # Run your Shiny app
# # Spans will be exported to otel_spans.json
# }

## ----json_export, echo=FALSE--------------------------------------------------
# NA

## ----sqlite_export------------------------------------------------------------
# \dontrun{
# library(otel)
# library(otelsdk)
# 
# # Configure SQLite exporter (custom implementation)
# # Note: Requires additional setup - see otel package documentation
# Sys.setenv(
#   OTEL_TRACES_EXPORTER = "sqlite",
#   OTEL_EXPORTER_SQLITE_PATH = "/path/to/otel_spans.sqlite"
# )
# 
# options(shiny.otel.collect = "all")
# }

## ----live_export--------------------------------------------------------------
# \dontrun{
# library(otel)
# library(otelsdk)
# 
# # Configure OTLP HTTP exporter to send to collector
# Sys.setenv(
#   OTEL_TRACES_EXPORTER = "otlp",
#   OTEL_EXPORTER_OTLP_PROTOCOL = "http/protobuf",
#   OTEL_EXPORTER_OTLP_ENDPOINT = "https://collector.example.com:4318",
#   OTEL_EXPORTER_OTLP_HEADERS = "Authorization=Bearer YOUR_TOKEN",
#   OTEL_RESOURCE_ATTRIBUTES = "service.name=my-shiny-app,environment=production"
# )
# 
# options(shiny.otel.collect = "all")
# }

## ----env_vars-----------------------------------------------------------------
# # Core configuration
# SHINY_OTEL_COLLECT = "all"              # Collection level
# OTEL_TRACES_EXPORTER = "otlp"           # Exporter type
# 
# # OTLP configuration
# OTEL_EXPORTER_OTLP_ENDPOINT = "https://collector:4318"  # Collector URL
# OTEL_EXPORTER_OTLP_PROTOCOL = "http/json"               # Protocol format
# OTEL_EXPORTER_OTLP_HEADERS = "Authorization=Bearer token"  # Auth headers
# 
# # Resource attributes (metadata)
# OTEL_RESOURCE_ATTRIBUTES = "service.name=my-app,environment=prod,version=1.0"
# 
# # Sampling (control data volume)
# OTEL_TRACES_SAMPLER = "traceidratio"    # Sampler type
# OTEL_TRACES_SAMPLER_ARG = "0.1"         # Sample 10% of traces

## ----bidux_workflow-----------------------------------------------------------
# library(bidux)
# library(dplyr)
# 
# # Works just like shiny.telemetry!
# issues <- bid_telemetry("otel_spans.json")
# 
# # Same friction detection
# critical_issues <- issues |>
#   filter(severity == "critical") |>
#   arrange(desc(impact_rate))
# 
# # Same BID pipeline
# interpret <- bid_interpret(
#   central_question = "How to improve user experience based on OTEL data?"
# )
# 
# notices <- bid_notices(
#   issues = critical_issues,
#   previous_stage = interpret,
#   max_issues = 3
# )
# 
# # Extract telemetry flags
# flags <- bid_flags(issues)
# flags$has_critical_issues

## ----auto_detect--------------------------------------------------------------
# # Automatically detects shiny.telemetry format
# issues_st <- bid_telemetry("telemetry.sqlite")
# 
# # Automatically detects OTLP JSON format
# issues_otel <- bid_telemetry("otel_spans.json")
# 
# # Automatically detects OTEL SQLite format
# issues_otel_db <- bid_telemetry("otel_spans.sqlite")
# 
# # Same analysis, same results, regardless of source!

## ----otel_duration------------------------------------------------------------
# # Duration calculated from span timestamps
# # duration_ms = (endTimeUnixNano - startTimeUnixNano) / 1e6
# 
# # Analyze OTEL data
# issues <- bid_telemetry("otel_spans.json")
# 
# # OTEL data provides performance context
# issues |>
#   filter(issue_type == "delayed_interaction") |>
#   select(problem, evidence)
# #> Problem: Users take a long time before making their first interaction
# #> Evidence: Median time to first input is 47 seconds

## ----complete_example---------------------------------------------------------
# \dontrun{
# # ============================================
# # STEP 1: Configure OTEL in your Shiny app
# # ============================================
# 
# library(shiny)
# library(otel)
# library(otelsdk)
# 
# # Enable OTEL with file export
# Sys.setenv(
#   OTEL_TRACES_EXPORTER = "otlp",
#   OTEL_EXPORTER_OTLP_ENDPOINT = "/tmp/shiny_otel.json"
# )
# options(shiny.otel.collect = "all")
# 
# # Your Shiny app
# ui <- fluidPage(
#   titlePanel("Sales Dashboard"),
#   sidebarLayout(
#     sidebarPanel(
#       selectInput("region", "Region:",
#                   choices = c("North", "South", "East", "West")),
#       selectInput("product", "Product:",
#                   choices = c("A", "B", "C")),
#       dateRangeInput("dates", "Date Range:")
#     ),
#     mainPanel(
#       tabsetPanel(
#         tabPanel("Overview", plotOutput("overview")),
#         tabPanel("Details", tableOutput("details")),
#         tabPanel("Settings", uiOutput("settings"))
#       )
#     )
#   )
# )
# 
# server <- function(input, output, session) {
#   output$overview <- renderPlot({
#     # Plotting logic
#   })
# 
#   output$details <- renderTable({
#     # Table logic
#   })
# 
#   output$settings <- renderUI({
#     # Settings UI
#   })
# }
# 
# # Run app and collect data
# shinyApp(ui, server)
# 
# # ============================================
# # STEP 2: Analyze OTEL data with bidux
# # ============================================
# 
# library(bidux)
# library(dplyr)
# 
# # Analyze collected OTEL spans
# issues <- bid_telemetry(
#   "/tmp/shiny_otel.json",
#   thresholds = bid_telemetry_presets("moderate")
# )
# 
# # Review identified issues
# print(issues)
# #> # BID Telemetry Issues Summary
# #> Found 5 issues from 342 sessions
# #>
# #> Critical: 1 issue
# #> High: 2 issues
# #> Medium: 2 issues
# 
# # Filter to critical issues
# critical <- issues |>
#   filter(severity == "critical")
# 
# print(critical)
# #> Issue: unused_input_product
# #> Problem: Users are not interacting with the 'product' input control
# #> Evidence: Only 12 out of 342 sessions (3.5%) interacted with 'product'
# #> Impact: 96.5% of sessions affected
# 
# # ============================================
# # STEP 3: Apply BID framework
# # ============================================
# 
# # Start BID workflow with OTEL insights
# interpret_result <- bid_interpret(
#   central_question = "Why aren't users engaging with the product filter?",
#   data_story = new_data_story(
#     hook = "96.5% of users never use the product filter",
#     context = "OTEL data from 342 sessions over 2 weeks",
#     tension = "Filter may be unnecessary or poorly positioned",
#     resolution = "Simplify interface or improve filter discoverability"
#   )
# )
# 
# # Convert OTEL issue to Notice
# notice_result <- bid_notices(
#   issues = critical,
#   previous_stage = interpret_result
# )[[1]]
# 
# # Continue through BID stages
# anticipate_result <- bid_anticipate(
#   previous_stage = notice_result,
#   bias_mitigations = list(
#     choice_overload = "Reduce number of visible filters",
#     default_effect = "Set smart defaults based on common patterns"
#   )
# )
# 
# # Use OTEL flags to inform structure
# flags <- bid_flags(issues)
# structure_result <- bid_structure(
#   previous_stage = anticipate_result,
#   telemetry_flags = flags
# )
# 
# # Validate
# validate_result <- bid_validate(
#   previous_stage = structure_result,
#   summary_panel = "Simplified dashboard with progressive disclosure",
#   next_steps = c(
#     "Remove or hide unused product filter",
#     "Re-run OTEL analysis to verify improvement",
#     "Monitor user engagement metrics"
#   )
# )
# 
# # Generate report
# bid_report(validate_result, format = "html")
# }

## ----dual_tracking------------------------------------------------------------
# \dontrun{
# library(shiny)
# library(shiny.telemetry)
# library(otel)
# 
# # Enable both systems
# telemetry <- Telemetry$new()
# options(shiny.otel.collect = "all")
# 
# ui <- fluidPage(
#   use_telemetry(),  # shiny.telemetry
#   # Your UI
# )
# 
# server <- function(input, output, session) {
#   telemetry$start_session()  # shiny.telemetry
#   # Your server logic
# }
# 
# # Analyze both sources
# issues_st <- bid_telemetry("telemetry.sqlite")
# issues_otel <- bid_telemetry("otel_spans.json")
# 
# # Compare results
# nrow(issues_st)
# nrow(issues_otel)
# }

## ----migration_phase1---------------------------------------------------------
# \dontrun{
# # Run both systems to compare
# options(shiny.otel.collect = "all")
# # Keep existing shiny.telemetry code
# 
# # Compare results weekly
# issues_st <- bid_telemetry("telemetry.sqlite")
# issues_otel <- bid_telemetry("otel_spans.json")
# 
# # Verify OTEL captures same issues
# }

## ----migration_phase2---------------------------------------------------------
# \dontrun{
# # Switch to OTEL as primary
# options(shiny.otel.collect = "all")
# # Keep shiny.telemetry as backup
# 
# # Use OTEL data for analysis
# issues <- bid_telemetry("otel_spans.json")
# }

## ----migration_phase3---------------------------------------------------------
# \dontrun{
# # Remove shiny.telemetry code
# # library(shiny.telemetry) - remove
# # use_telemetry() - remove
# # telemetry$start_session() - remove
# 
# # OTEL only
# options(shiny.otel.collect = "all")
# }

## ----troubleshoot_1-----------------------------------------------------------
# # Solution: Install OpenTelemetry packages
# install.packages("otel")
# install.packages("otelsdk")

## ----troubleshoot_2-----------------------------------------------------------
# # Check if OTEL is enabled
# getOption("shiny.otel.collect")
# #> Should return "all" or another collection level
# 
# # Verify otel is tracing
# library(otel)
# otel::is_tracing_enabled()
# #> Should return TRUE
# 
# # Enable OTEL if disabled
# options(shiny.otel.collect = "all")

## ----troubleshoot_3-----------------------------------------------------------
# # Verify file structure
# jsonlite::fromJSON("otel_spans.json", simplifyVector = FALSE) |>
#   str(max.level = 2)
# 
# # Should contain spans with startTimeUnixNano, endTimeUnixNano, etc.
# # If not, check OTLP exporter configuration

## ----troubleshoot_4-----------------------------------------------------------
# # Check exporter endpoint
# Sys.getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
# 
# # Verify file path is writable
# file.access("otel_spans.json", mode = 2)
# #> Should return 0 (success)
# 
# # Check Shiny app had user interactions
# # Spans only created when actions occur

## ----troubleshoot_5-----------------------------------------------------------
# # Use sampling to reduce volume
# Sys.setenv(
#   OTEL_TRACES_SAMPLER = "traceidratio",
#   OTEL_TRACES_SAMPLER_ARG = "0.1"  # Sample 10% of traces
# )
# 
# # Or reduce collection level
# options(shiny.otel.collect = "reactive_update")  # Less than "all"

## ----troubleshoot_6-----------------------------------------------------------
# # Explicitly specify format
# issues <- bid_telemetry("otel_spans.json", format = "otlp_json")
# 
# # Or for OTEL SQLite
# issues <- bid_telemetry("otel_spans.sqlite", format = "otel_sqlite")

## ----custom_attributes--------------------------------------------------------
# \dontrun{
# library(otel)
# 
# # Add custom attributes to current span
# otel::add_span_attribute("user_role", "analyst")
# otel::add_span_attribute("dashboard_version", "2.1.0")
# 
# # These attributes are preserved in OTLP exports
# # and available for custom analysis
# }

## ----filter_attributes--------------------------------------------------------
# \dontrun{
# # Analyze OTEL data
# issues <- bid_telemetry("otel_spans.json")
# 
# # Access raw span data for custom filtering
# # (Advanced: requires understanding OTLP structure)
# raw_spans <- jsonlite::fromJSON("otel_spans.json")
# 
# # Filter spans by custom attributes before analysis
# # Then re-analyze with bidux
# }

## ----bp1----------------------------------------------------------------------
# options(shiny.otel.collect = "all")

## ----bp2----------------------------------------------------------------------
# Sys.setenv(OTEL_TRACES_SAMPLER_ARG = "0.1")  # 10% sampling

## ----bp3----------------------------------------------------------------------
# \dontrun{
# # Implement log rotation in your deployment
# # Example: daily rotation with retention
# file_pattern <- paste0("otel_spans_", Sys.Date(), ".json")
# }

## ----bp4----------------------------------------------------------------------
# \dontrun{
# file.size("otel_spans.json") / 1024 / 1024  # Size in MB
# }

## ----bp5----------------------------------------------------------------------
# # Schedule regular UX reviews
# issues <- bid_telemetry("otel_spans.json")
# if (any(issues$severity == "critical")) {
#   # Alert team
# }

## ----bp6----------------------------------------------------------------------
# # Use OTEL to identify friction points
# # Then interview users to understand root causes

