---
title: "Data Quality Audit Report"
subtitle: "AI-Driven Anomaly Detection for Data Quality"
author: "autoFlagR Package"
date: "`r format(Sys.Date(), '%B %d, %Y')`"
output:
  pdf_document:
    toc: true
    toc_depth: 2
    number_sections: true
    fig_caption: true
    highlight: tango
    keep_tex: true
    latex_engine: pdflatex
  html_document:
    toc: true
    toc_depth: 2
    number_sections: true
    fig_caption: true
params:
  flagged_data: NULL
  top_anomalies: NULL
  metadata: NULL
  benchmark_metrics: NULL
  method: "iforest"
  contamination: 0.05
  top_n: 100
  top_numeric_cols: NULL
---


```{r setup, include=FALSE}
# Detect output format to conditionally set chunk options
is_word <- knitr::pandoc_to() == "docx"

knitr::opts_chunk$set(
  echo = FALSE, 
  warning = FALSE, 
  message = FALSE,
  fig.width = 7,
  fig.height = 5,
  out.width = "90%",
  dpi = 300
)

# fig.align is not supported for Word output, only for PDF/HTML
if (!is_word) {
  knitr::opts_chunk$set(fig.align = "center")
}

# Load required libraries
library(autoFlagR)
library(ggplot2)
library(dplyr)
library(gt)
library(scales)
library(ggnewscale)


# Helper function to escape LaTeX special characters
# Only escape characters that commonly appear in column names
escape_latex <- function(x) {
  if (is.null(x) || length(x) == 0) return(x)
  x <- as.character(x)
  # Escape only the most common problematic characters in column names
  # Use fixed = TRUE to match literal characters, not regex
  x <- gsub("_", "\\_", x, fixed = TRUE)  # Underscore (most common)
  x <- gsub("&", "\\&", x, fixed = TRUE)  # Ampersand
  x <- gsub("%", "\\%", x, fixed = TRUE)  # Percent
  x <- gsub("$", "\\$", x, fixed = TRUE)  # Dollar
  x <- gsub("#", "\\#", x, fixed = TRUE)  # Hash
  return(x)
}

# Helper function to get color for heat map based on value
# Returns color in appropriate format based on output type
get_heatmap_color <- function(value, min_val, max_val, col_type = "anomaly", output_type = "latex") {
  if (is.na(value) || is.null(value)) return("")
  
  # Normalize value to 0-1 range
  if (max_val == min_val) {
    normalized <- 0.5
  } else {
    normalized <- (value - min_val) / (max_val - min_val)
  }
  
  if (col_type == "anomaly") {
    # For anomaly scores: light pink (low) to bright red (high)
    # Make colors VERY vibrant and visible - use strong contrast
    # Start from light pink (255, 200, 200) to bright red (255, 0, 0)
    r <- 255  # Keep red at maximum
    g <- round(200 - 200 * normalized)   # Green decreases from 200 to 0
    b <- round(200 - 200 * normalized)   # Blue decreases from 200 to 0
  } else {
    # For other numeric columns: light blue to dark blue
    # Make colors very visible with strong contrast
    # Start from light blue (200, 220, 255) to dark blue (0, 50, 150)
    r <- round(200 - 200 * normalized)    # Red decreases from 200 to 0
    g <- round(220 - 170 * normalized)   # Green decreases from 220 to 50
    b <- 255  # Blue starts at maximum
    if (normalized > 0.5) {
      b <- round(255 - 105 * (normalized - 0.5) * 2)  # Blue decreases for darker values
    }
  }
  
  if (output_type == "latex") {
    # Return LaTeX format for \cellcolor
    r_norm <- round(r / 255, 3)
    g_norm <- round(g / 255, 3)
    b_norm <- round(b / 255, 3)
    return(sprintf("[rgb]{%.3f,%.3f,%.3f}", r_norm, g_norm, b_norm))
  } else {
    # Return hex format for Word/HTML (better Word compatibility)
    hex_color <- sprintf("#%02X%02X%02X", r, g, b)
    return(hex_color)
  }
}

# Set professional color scheme
color_palette <- list(
  primary = "#2980B9",
  accent = "#E74C3C",
  success = "#27AE60",
  warning = "#F39C12",
  light_gray = "#ECF0F1",
  dark_gray = "#2C3E50",
  normal = "#95A5A6",
  anomalous = "#E74C3C"
)

# Professional ggplot theme
theme_professional <- function() {
  theme_minimal(base_size = 11) +
    theme(
      plot.title = element_text(
        size = 14, 
        face = "bold", 
        color = color_palette$dark_gray,
        margin = margin(b = 10)
      ),
      plot.subtitle = element_text(
        size = 11, 
        color = color_palette$dark_gray,
        margin = margin(b = 15)
      ),
      axis.title = element_text(
        size = 10, 
        color = color_palette$dark_gray,
        face = "bold"
      ),
      axis.text = element_text(
        size = 9, 
        color = color_palette$dark_gray
      ),
      legend.title = element_text(
        size = 10, 
        face = "bold",
        color = color_palette$dark_gray
      ),
      legend.text = element_text(
        size = 9,
        color = color_palette$dark_gray
      ),
      panel.grid.major = element_line(
        color = color_palette$light_gray, 
        linewidth = 0.5
      ),
      panel.grid.minor = element_line(
        color = color_palette$light_gray, 
        linewidth = 0.25
      ),
      plot.margin = margin(10, 10, 10, 10),
      plot.background = element_rect(fill = "white", color = NA)
    )
}
```

```{=latex}
\newpage
```

# Executive Summary

```{r executive-summary-box, echo=FALSE, results='asis'}
# Detect output format
is_latex <- knitr::is_latex_output()

# Create a professional summary
n_total <- nrow(params$flagged_data)
n_flagged <- sum(params$flagged_data$is_anomaly, na.rm = TRUE)
anomaly_rate <- mean(params$flagged_data$is_anomaly, na.rm = TRUE) * 100

if (is_latex) {
  # LaTeX table - output raw LaTeX
  cat("\\begin{table}[h]\n")
  cat("\\centering\n")
  cat("\\begin{tabular}{ll}\n")
  cat("\\hline\n")
  cat("\\textbf{Metric} & \\textbf{Value} \\\\\n")
  cat("\\hline\n")
  cat("Total Records Processed & ", format(n_total, big.mark = ","), " \\\\\n")
  cat("Total Records Flagged & ", format(n_flagged, big.mark = ","), " \\\\\n")
  cat("Anomaly Rate & ", sprintf("%.2f\\%%", anomaly_rate), " \\\\\n")
  cat("Detection Method & ", toupper(params$method), " \\\\\n")
  cat("Contamination Parameter & ", params$contamination, " \\\\\n")
  cat("\\hline\n")
  cat("\\end{tabular}\n")
  cat("\\end{table}\n")
} else {
  # HTML table for Word/HTML
  cat("<table style='width:100%; border-collapse: collapse; margin: 10px 0;'>\n")
  cat("<thead><tr style='background-color: #f2f2f2;'><th style='border: 1px solid #ddd; padding: 8px; text-align: left;'>Metric</th><th style='border: 1px solid #ddd; padding: 8px; text-align: left;'>Value</th></tr></thead>\n")
  cat("<tbody>\n")
  cat("<tr><td style='border: 1px solid #ddd; padding: 8px;'><strong>Total Records Processed</strong></td><td style='border: 1px solid #ddd; padding: 8px;'>", format(n_total, big.mark = ","), "</td></tr>\n")
  cat("<tr><td style='border: 1px solid #ddd; padding: 8px;'><strong>Total Records Flagged</strong></td><td style='border: 1px solid #ddd; padding: 8px;'>", format(n_flagged, big.mark = ","), "</td></tr>\n")
  cat("<tr><td style='border: 1px solid #ddd; padding: 8px;'><strong>Anomaly Rate</strong></td><td style='border: 1px solid #ddd; padding: 8px;'>", sprintf("%.2f%%", anomaly_rate), "</td></tr>\n")
  cat("<tr><td style='border: 1px solid #ddd; padding: 8px;'><strong>Detection Method</strong></td><td style='border: 1px solid #ddd; padding: 8px;'>", toupper(params$method), "</td></tr>\n")
  cat("<tr><td style='border: 1px solid #ddd; padding: 8px;'><strong>Contamination Parameter</strong></td><td style='border: 1px solid #ddd; padding: 8px;'>", params$contamination, "</td></tr>\n")
  cat("</tbody></table>\n")
}
```

## Key Performance Indicators

```{r kpi-metrics, echo=FALSE, results='asis'}
# Detect output format
is_latex <- knitr::is_latex_output()

# Create KPI-style metrics
n_total <- nrow(params$flagged_data)
n_flagged <- sum(params$flagged_data$is_anomaly, na.rm = TRUE)
anomaly_rate <- mean(params$flagged_data$is_anomaly, na.rm = TRUE) * 100

# Determine status color
if (anomaly_rate > 10) {
  status_text <- "High"
  status_color <- "rgb(232, 77, 61)"  # Red
} else if (anomaly_rate > 5) {
  status_text <- "Moderate"
  status_color <- "rgb(242, 156, 18)"  # Orange
} else {
  status_text <- "Normal"
  status_color <- "rgb(38, 174, 97)"  # Green
}

if (is_latex) {
  # LaTeX table
  cat("\\begin{table}[h]\n")
  cat("\\centering\n")
  cat("\\begin{tabular}{p{0.3\\textwidth}p{0.3\\textwidth}p{0.3\\textwidth}}\n")
  cat("\\hline\n")
  cat("\\textbf{Metric} & \\textbf{Value} & \\textbf{Status} \\\\\n")
  cat("\\hline\n")
  cat("Records Processed & ", format(n_total, big.mark = ","), " & \\textcolor[rgb]{0.16,0.50,0.73}{OK} \\\\\n")
  cat("Anomalies Detected & ", format(n_flagged, big.mark = ","), " & \\textcolor[rgb]{0.91,0.30,0.24}{\\textbf{Review Required}} \\\\\n")
  cat("Anomaly Rate & ", sprintf("%.2f\\%%", anomaly_rate), " & \\textcolor[rgb]{", 
      if(anomaly_rate > 10) "0.91,0.30,0.24" else if(anomaly_rate > 5) "0.95,0.61,0.07" else "0.15,0.68,0.38", 
      "}{", status_text, "} \\\\\n")
  cat("\\hline\n")
  cat("\\end{tabular}\n")
  cat("\\end{table}\n")
} else {
  # HTML table for Word/HTML
  cat("<table style='width:100%; border-collapse: collapse; margin: 10px 0;'>\n")
  cat("<thead><tr style='background-color: #f2f2f2;'><th style='border: 1px solid #ddd; padding: 8px; text-align: left;'>Metric</th><th style='border: 1px solid #ddd; padding: 8px; text-align: left;'>Value</th><th style='border: 1px solid #ddd; padding: 8px; text-align: left;'>Status</th></tr></thead>\n")
  cat("<tbody>\n")
  cat("<tr><td style='border: 1px solid #ddd; padding: 8px;'><strong>Records Processed</strong></td><td style='border: 1px solid #ddd; padding: 8px;'>", format(n_total, big.mark = ","), "</td><td style='border: 1px solid #ddd; padding: 8px; color: rgb(41, 128, 186);'>OK</td></tr>\n")
  cat("<tr><td style='border: 1px solid #ddd; padding: 8px;'><strong>Anomalies Detected</strong></td><td style='border: 1px solid #ddd; padding: 8px;'>", format(n_flagged, big.mark = ","), "</td><td style='border: 1px solid #ddd; padding: 8px; color: rgb(232, 77, 61);'><strong>Review Required</strong></td></tr>\n")
  cat("<tr><td style='border: 1px solid #ddd; padding: 8px;'><strong>Anomaly Rate</strong></td><td style='border: 1px solid #ddd; padding: 8px;'>", sprintf("%.2f%%", anomaly_rate), "</td><td style='border: 1px solid #ddd; padding: 8px; color: ", status_color, ";'>", status_text, "</td></tr>\n")
  cat("</tbody></table>\n")
}
```

```{r benchmark-metrics, eval=!is.null(params$benchmark_metrics), echo=FALSE, results='asis'}
# Detect output format
is_latex <- knitr::is_latex_output()

if (!is.null(params$benchmark_metrics)) {
  if (is_latex) {
    cat("\\subsection*{Benchmarking Results}\n")
    cat("\\begin{table}[h]\n")
    cat("\\centering\n")
    cat("\\begin{tabular}{lr}\n")
    cat("\\hline\n")
    cat("\\textbf{Metric} & \\textbf{Value} \\\\\n")
    cat("\\hline\n")
    cat("AUC-ROC & ", sprintf("%.4f", params$benchmark_metrics$auc_roc), " \\\\\n")
    cat("AUC-PR & ", sprintf("%.4f", params$benchmark_metrics$auc_pr), " \\\\\n")
    cat("Contamination Rate & ", sprintf("%.2f\\%%", 100 * params$benchmark_metrics$contamination_rate), " \\\\\n")
    cat("\\hline\n")
    cat("\\end{tabular}\n")
    cat("\\end{table}\n")
  } else {
    cat("### Benchmarking Results\n\n")
    cat("<table style='width:100%; border-collapse: collapse; margin: 10px 0;'>\n")
    cat("<thead><tr style='background-color: #f2f2f2;'><th style='border: 1px solid #ddd; padding: 8px; text-align: left;'>Metric</th><th style='border: 1px solid #ddd; padding: 8px; text-align: right;'>Value</th></tr></thead>\n")
    cat("<tbody>\n")
    cat("<tr><td style='border: 1px solid #ddd; padding: 8px;'><strong>AUC-ROC</strong></td><td style='border: 1px solid #ddd; padding: 8px; text-align: right;'>", sprintf("%.4f", params$benchmark_metrics$auc_roc), "</td></tr>\n")
    cat("<tr><td style='border: 1px solid #ddd; padding: 8px;'><strong>AUC-PR</strong></td><td style='border: 1px solid #ddd; padding: 8px; text-align: right;'>", sprintf("%.4f", params$benchmark_metrics$auc_pr), "</td></tr>\n")
    cat("<tr><td style='border: 1px solid #ddd; padding: 8px;'><strong>Contamination Rate</strong></td><td style='border: 1px solid #ddd; padding: 8px; text-align: right;'>", sprintf("%.2f%%", 100 * params$benchmark_metrics$contamination_rate), "</td></tr>\n")
    cat("</tbody></table>\n")
  }
  
  if (length(params$benchmark_metrics$top_k_recall) > 0) {
    if (is_latex) {
      cat("\\subsubsection*{Top-K Recall Performance}\n")
      cat("\\begin{table}[h]\n")
      cat("\\centering\n")
      cat("\\begin{tabular}{lr}\n")
      cat("\\hline\n")
      cat("\\textbf{Top K Records} & \\textbf{Recall} \\\\\n")
      cat("\\hline\n")
      for (k_name in names(params$benchmark_metrics$top_k_recall)) {
        k_value <- gsub("top_", "", k_name)
        recall <- params$benchmark_metrics$top_k_recall[[k_name]]
        cat("Top ", k_value, " & ", sprintf("%.2f\\%%", 100 * recall), " \\\\\n")
      }
      cat("\\hline\n")
      cat("\\end{tabular}\n")
      cat("\\end{table}\n")
    } else {
      cat("#### Top-K Recall Performance\n\n")
      cat("<table style='width:100%; border-collapse: collapse; margin: 10px 0;'>\n")
      cat("<thead><tr style='background-color: #f2f2f2;'><th style='border: 1px solid #ddd; padding: 8px; text-align: left;'>Top K Records</th><th style='border: 1px solid #ddd; padding: 8px; text-align: right;'>Recall</th></tr></thead>\n")
      cat("<tbody>\n")
      for (k_name in names(params$benchmark_metrics$top_k_recall)) {
        k_value <- gsub("top_", "", k_name)
        recall <- params$benchmark_metrics$top_k_recall[[k_name]]
        cat("<tr><td style='border: 1px solid #ddd; padding: 8px;'>Top ", k_value, "</td><td style='border: 1px solid #ddd; padding: 8px; text-align: right;'>", sprintf("%.2f%%", 100 * recall), "</td></tr>\n")
      }
      cat("</tbody></table>\n")
    }
  }
}
```

```{=latex}
\newpage
```

# Anomaly Score Distribution

```{r score-distribution, echo=FALSE, fig.cap="Distribution of anomaly scores across all records. The red dashed line indicates the threshold for flagging anomalies."}
threshold <- attr(params$flagged_data, "anomaly_threshold")

ggplot(params$flagged_data, aes(x = anomaly_score)) +
  geom_histogram(
    bins = 50, 
    fill = color_palette$primary, 
    alpha = 0.7, 
    color = "white",
    linewidth = 0.3
  ) +
  geom_vline(
    xintercept = threshold,
    color = color_palette$accent, 
    linetype = "dashed", 
    linewidth = 1.2
  ) +
  geom_text(
    aes(x = threshold, y = Inf, label = "Threshold"),
    vjust = 1.5,
    hjust = -0.1,
    color = color_palette$accent,
    fontface = "bold",
    size = 3.5
  ) +
  labs(
    title = "Distribution of Anomaly Scores",
    subtitle = "Higher scores indicate greater likelihood of data quality issues",
    x = "Anomaly Score",
    y = "Frequency"
  ) +
  theme_professional() +
  scale_x_continuous(labels = scales::percent_format(accuracy = 1))
```

```{=latex}
\newpage
```

# Prioritized Audit Listing

The following visualization presents the top `r params$top_n` most anomalous records, prioritized by their anomaly scores. Records are sorted in descending order, with the most suspicious entries appearing first.

**Heat Map Visualization:** The heat map uses color intensity to highlight anomalous patterns:
- **Red gradient (anomaly scores):** Darker red indicates higher anomaly scores (more suspicious records)
- **Blue gradient (numeric values):** Darker blue indicates higher values, helping identify extreme values that may contribute to anomalies

```{r top-anomalies-heatmap, echo=FALSE, fig.width=12, fig.height=8, fig.align='center', fig.cap="Heat map visualization of the top 20 most anomalous records. Red gradient indicates anomaly scores (darker red = higher anomaly), blue gradient indicates numeric values (darker blue = higher values). The 'Reason Code' column shows which feature contributed most to each record's anomaly score. Values are displayed in each cell for precise reference."}
# Create heatmap visualization with red/blue colors matching the table
if (!is.null(params$top_anomalies) && nrow(params$top_anomalies) > 0) {
  # Select columns to display
  display_cols <- c()
  if (length(params$metadata$id_cols) > 0) {
    display_cols <- c(display_cols, params$metadata$id_cols[1])
  }
  display_cols <- c(display_cols, "anomaly_score")
  if (length(params$top_numeric_cols) > 0) {
    display_cols <- c(display_cols, params$top_numeric_cols)
  }
  # Add reason_code if available
  if ("reason_code" %in% names(params$top_anomalies)) {
    display_cols <- c(display_cols, "reason_code")
  }
  
  if (length(display_cols) > 0 && all(display_cols %in% names(params$top_anomalies))) {
    # Prepare data for heatmap (top 20 records)
    heatmap_data <- params$top_anomalies %>%
      dplyr::select(dplyr::all_of(display_cols)) %>%
      dplyr::mutate(anomaly_score = round(anomaly_score, 4)) %>%
      dplyr::mutate(Rank = row_number()) %>%
      dplyr::slice_head(n = 20)
    
    # Get numeric columns (excluding Rank)
    numeric_cols <- names(heatmap_data)[sapply(heatmap_data, is.numeric)]
    numeric_cols <- setdiff(numeric_cols, "Rank")
    
    # Check if reason_code exists
    has_reason_code <- "reason_code" %in% names(heatmap_data)
    
    if (length(numeric_cols) > 0) {
      # Reshape data for heatmap - ensure consistent column structure
      heatmap_long_list <- list()
      
      for (col in numeric_cols) {
        col_data <- data.frame(
          Rank = as.integer(heatmap_data$Rank),
          Variable = as.character(col),
          Value = as.numeric(heatmap_data[[col]]),
          stringsAsFactors = FALSE
        )
        heatmap_long_list[[length(heatmap_long_list) + 1]] <- col_data
      }
      
      # Add reason_code column if available
      if (has_reason_code) {
        reason_data <- data.frame(
          Rank = as.integer(heatmap_data$Rank),
          Variable = "reason_code",
          Value = as.character(heatmap_data$reason_code),
          stringsAsFactors = FALSE
        )
        heatmap_long_list[[length(heatmap_long_list) + 1]] <- reason_data
      }
      
      # Combine all data frames
      heatmap_long <- do.call(rbind, heatmap_long_list)
      
      # Normalize values to 0-1 for consistent color scaling
      # Only normalize numeric variables, skip reason_code
      heatmap_long$Normalized <- NA_real_
      for (var in unique(heatmap_long$Variable)) {
        if (var == "reason_code") {
          # Skip normalization for reason_code (it's text)
          heatmap_long$Normalized[heatmap_long$Variable == var] <- 0.5
        } else {
          var_data <- heatmap_long$Value[heatmap_long$Variable == var]
          # Convert to numeric if needed
          var_data_numeric <- suppressWarnings(as.numeric(var_data))
          if (all(!is.na(var_data_numeric))) {
            min_val <- min(var_data_numeric, na.rm = TRUE)
            max_val <- max(var_data_numeric, na.rm = TRUE)
            if (max_val > min_val) {
              heatmap_long$Normalized[heatmap_long$Variable == var] <- 
                (var_data_numeric - min_val) / (max_val - min_val)
            } else {
              heatmap_long$Normalized[heatmap_long$Variable == var] <- 0.5
            }
          } else {
            heatmap_long$Normalized[heatmap_long$Variable == var] <- 0.5
          }
        }
      }
      
      # Set factor levels (include reason_code at the end if present)
      var_levels <- c("anomaly_score", setdiff(numeric_cols, "anomaly_score"))
      if (has_reason_code) {
        var_levels <- c(var_levels, "reason_code")
      }
      heatmap_long$Variable <- factor(heatmap_long$Variable, levels = var_levels)
      heatmap_long$Rank <- factor(heatmap_long$Rank, levels = rev(1:max(heatmap_long$Rank)))
      
      # Separate data for different color scales
      anomaly_data <- heatmap_long[heatmap_long$Variable == "anomaly_score", ]
      if (has_reason_code) {
        reason_data <- heatmap_long[heatmap_long$Variable == "reason_code", ]
        numeric_data <- heatmap_long[heatmap_long$Variable != "anomaly_score" & 
                                     heatmap_long$Variable != "reason_code", ]
      } else {
        reason_data <- data.frame()
        numeric_data <- heatmap_long[heatmap_long$Variable != "anomaly_score", ]
      }
      
      # Format values for display
      if (nrow(numeric_data) > 0) {
        # Convert Value to numeric for formatting
        numeric_data$ValueNumeric <- suppressWarnings(as.numeric(numeric_data$Value))
        numeric_data$ValueFormatted <- ifelse(
          numeric_data$Variable %in% c("patient_id", "Rank") | is.na(numeric_data$ValueNumeric),
          ifelse(is.na(numeric_data$ValueNumeric), as.character(numeric_data$Value), sprintf("%.0f", numeric_data$ValueNumeric)),
          sprintf("%.2f", numeric_data$ValueNumeric)
        )
      }
      
      if (nrow(anomaly_data) > 0) {
        anomaly_data$ValueNumeric <- suppressWarnings(as.numeric(anomaly_data$Value))
        anomaly_data$ValueFormatted <- sprintf("%.4f", anomaly_data$ValueNumeric)
      }
      
      if (nrow(reason_data) > 0) {
        # Truncate long reason codes for display
        reason_data$ValueFormatted <- ifelse(
          nchar(as.character(reason_data$Value)) > 30,
          paste0(substr(as.character(reason_data$Value), 1, 27), "..."),
          as.character(reason_data$Value)
        )
      }
      
      # Create base plot with blue gradient for numeric columns
      p <- ggplot2::ggplot() +
        ggplot2::geom_tile(data = numeric_data, 
                          ggplot2::aes(x = Variable, y = Rank, fill = Normalized),
                          color = "white", linewidth = 0.3) +
        ggplot2::geom_text(data = numeric_data,
                          ggplot2::aes(x = Variable, y = Rank, label = ValueFormatted),
                          color = "black", size = 2.5, fontface = "bold") +
        ggplot2::scale_fill_gradientn(
          colors = c("#DEEBF7", "#9ECAE1", "#3182BD"),  # Light blue to dark blue (matching table)
          na.value = "grey90",
          guide = ggplot2::guide_colorbar(title = "Numeric\nValues", barheight = 8),
          limits = c(0, 1)
        ) +
        ggplot2::labs(
          title = paste("Heat Map: Top", min(20, nrow(params$top_anomalies)), "Most Anomalous Records"),
          subtitle = if (has_reason_code) {
            "Red: anomaly scores | Blue: numeric values | Gray: reason code (primary contributing feature)"
          } else {
            "Red gradient: anomaly scores | Blue gradient: numeric values"
          },
          x = "Variable",
          y = "Rank (1 = Most Anomalous)"
        ) +
        ggplot2::theme_minimal() +
        ggplot2::theme(
          plot.title = ggplot2::element_text(size = 14, face = "bold", hjust = 0.5),
          plot.subtitle = ggplot2::element_text(size = 11, hjust = 0.5),
          axis.text.x = ggplot2::element_text(angle = 45, hjust = 1, size = 9),
          axis.text.y = ggplot2::element_text(size = 8),
          axis.title = ggplot2::element_text(size = 11, face = "bold"),
          legend.position = "right",
          panel.grid = ggplot2::element_blank()
        )
      
      # Add anomaly_score column with red gradient using ggnewscale
      if (nrow(anomaly_data) > 0) {
        p <- p + 
          ggnewscale::new_scale_fill() +
          ggplot2::geom_tile(data = anomaly_data,
                            ggplot2::aes(x = Variable, y = Rank, fill = Normalized),
                            color = "white", linewidth = 0.3) +
          ggplot2::geom_text(data = anomaly_data,
                            ggplot2::aes(x = Variable, y = Rank, label = ValueFormatted),
                            color = "white", size = 2.5, fontface = "bold") +
          ggplot2::scale_fill_gradientn(
            colors = c("#FEE0D2", "#FC9272", "#DE2D26"),  # Light pink to bright red (matching table)
            na.value = "grey90",
            guide = ggplot2::guide_colorbar(title = "Anomaly\nScore", barheight = 8),
            limits = c(0, 1)
          )
      }
      
      # Add reason_code column with neutral background
      if (nrow(reason_data) > 0) {
        p <- p +
          ggplot2::geom_tile(data = reason_data,
                            ggplot2::aes(x = Variable, y = Rank),
                            fill = "#F0F0F0",  # Light gray background
                            color = "white", linewidth = 0.3) +
          ggplot2::geom_text(data = reason_data,
                            ggplot2::aes(x = Variable, y = Rank, label = ValueFormatted),
                            color = "black", size = 2.2, fontface = "italic",
                            hjust = 0.5, vjust = 0.5)
      }
      
      print(p)
    } else {
      cat("No numeric columns available for visualization.\\n")
    }
  } else {
    cat("Unable to create visualization. Please check data structure.\\n")
  }
} else {
  cat("No anomalous records to display.\\n")
}
```

<!-- Summary table removed - heatmap visualization above already displays all values with color coding -->

```{=latex}
\newpage
```

# Bivariate Anomaly Visualization

```{r bivariate-plot, eval=length(params$top_numeric_cols) >= 2, echo=FALSE, fig.cap="Bivariate scatter plot showing the relationship between key variables. Anomalous records are highlighted in red."}
if (length(params$top_numeric_cols) >= 2) {
  col1 <- params$top_numeric_cols[1]
  col2 <- params$top_numeric_cols[2]
  
  # Create a more sophisticated plot
  p <- ggplot(params$flagged_data, aes_string(x = col1, y = col2)) +
    geom_point(
      aes(color = is_anomaly, size = anomaly_score, alpha = is_anomaly), 
      shape = 21,
      stroke = 0.5
    ) +
    scale_color_manual(
      values = c("FALSE" = color_palette$normal, "TRUE" = color_palette$accent),
      labels = c("FALSE" = "Normal", "TRUE" = "Anomalous"),
      name = "Status"
    ) +
    scale_size_continuous(
      range = c(1, 4),
      guide = "none"
    ) +
    scale_alpha_manual(
      values = c("FALSE" = 0.4, "TRUE" = 0.9), 
      guide = "none"
    ) +
    labs(
      title = paste("Anomaly Detection: ", col1, " vs ", col2),
      subtitle = "Point size and color intensity reflect anomaly score magnitude",
      x = col1,
      y = col2,
      color = "Record Status"
    ) +
    theme_professional() +
    theme(
      legend.position = "bottom",
      legend.box = "horizontal"
    )
  
  print(p)
}
```

```{=latex}
\newpage
```

# Variable Distribution Comparison

```{r distribution-comparison, eval=length(params$top_numeric_cols) > 0, echo=FALSE, fig.cap="Comparison of variable distributions between normal and anomalous records."}
if (length(params$top_numeric_cols) > 0) {
  for (col in params$top_numeric_cols[1:min(2, length(params$top_numeric_cols))]) {
    normal_data <- params$flagged_data[!params$flagged_data$is_anomaly, col, drop = TRUE]
    anomaly_data <- params$flagged_data[params$flagged_data$is_anomaly, col, drop = TRUE]
    
    plot_data <- data.frame(
      value = c(normal_data, anomaly_data),
      group = rep(c("Normal", "Anomalous"), 
                  c(length(normal_data), length(anomaly_data)))
    )
    
    p <- ggplot(plot_data, aes(x = value, fill = group)) +
      geom_histogram(
        alpha = 0.7, 
        position = "identity", 
        bins = 30,
        color = "white",
        linewidth = 0.3
      ) +
      scale_fill_manual(
        values = c("Normal" = color_palette$primary, "Anomalous" = color_palette$accent),
        name = "Status"
      ) +
      labs(
        title = paste("Distribution of", col, "by Anomaly Status"),
        subtitle = "Overlapping histograms show differences between normal and anomalous records",
        x = col,
        y = "Frequency",
        fill = "Record Status"
      ) +
      theme_professional() +
      theme(legend.position = "bottom")
    
    print(p)
    cat("\n\n")
  }
}
```

```{=latex}
\newpage
```

# Technical Appendix

## Algorithm Details

```{r technical-details, echo=FALSE, results='asis'}
# Detect output format
is_latex <- knitr::is_latex_output()

if (is_latex) {
  cat("\\begin{table}[h]\n")
  cat("\\centering\n")
  cat("\\begin{tabular}{ll}\n")
  cat("\\hline\n")
  cat("\\textbf{Parameter} & \\textbf{Value} \\\\\n")
  cat("\\hline\n")
  cat("Detection Method & ", toupper(params$method), " \\\\\n")
  cat("Contamination Parameter & ", params$contamination, " \\\\\n")
  cat("Anomaly Threshold & ", sprintf("%.4f", attr(params$flagged_data, "anomaly_threshold")), " \\\\\n")
  cat("Package Version & ", as.character(packageVersion("autoFlagR")), " \\\\\n")
  cat("Report Generation Date & ", format(Sys.Date(), "%B %d, %Y"), " \\\\\n")
  cat("\\hline\n")
  cat("\\end{tabular}\n")
  cat("\\end{table}\n")
} else {
  cat("<table style='width:100%; border-collapse: collapse; margin: 10px 0;'>\n")
  cat("<thead><tr style='background-color: #f2f2f2;'><th style='border: 1px solid #ddd; padding: 8px; text-align: left;'>Parameter</th><th style='border: 1px solid #ddd; padding: 8px; text-align: left;'>Value</th></tr></thead>\n")
  cat("<tbody>\n")
  cat("<tr><td style='border: 1px solid #ddd; padding: 8px;'><strong>Detection Method</strong></td><td style='border: 1px solid #ddd; padding: 8px;'>", toupper(params$method), "</td></tr>\n")
  cat("<tr><td style='border: 1px solid #ddd; padding: 8px;'><strong>Contamination Parameter</strong></td><td style='border: 1px solid #ddd; padding: 8px;'>", params$contamination, "</td></tr>\n")
  cat("<tr><td style='border: 1px solid #ddd; padding: 8px;'><strong>Anomaly Threshold</strong></td><td style='border: 1px solid #ddd; padding: 8px;'>", sprintf("%.4f", attr(params$flagged_data, "anomaly_threshold")), "</td></tr>\n")
  cat("<tr><td style='border: 1px solid #ddd; padding: 8px;'><strong>Package Version</strong></td><td style='border: 1px solid #ddd; padding: 8px;'>", as.character(packageVersion("autoFlagR")), "</td></tr>\n")
  cat("<tr><td style='border: 1px solid #ddd; padding: 8px;'><strong>Report Generation Date</strong></td><td style='border: 1px solid #ddd; padding: 8px;'>", format(Sys.Date(), "%B %d, %Y"), "</td></tr>\n")
  cat("</tbody></table>\n")
}
```

## Columns Included in Scoring

### Numeric Columns

```{r numeric-cols, echo=FALSE, results='asis'}
# Detect output format
is_latex <- knitr::is_latex_output()

if(length(params$metadata$numeric_cols) > 0) {
  if (is_latex) {
    escaped_cols <- escape_latex(params$metadata$numeric_cols)
    cat("\\begin{itemize}\n")
    for (col in escaped_cols) {
      cat("\\item ", col, "\n")
    }
    cat("\\end{itemize}\n")
  } else {
    cat("\n")
    for (col in params$metadata$numeric_cols) {
      cat("- ", col, "\n")
    }
    cat("\n")
  }
} else {
  cat("None\n")
}
```

### Categorical Columns

```{r categorical-cols, echo=FALSE, results='asis'}
# Detect output format
is_latex <- knitr::is_latex_output()

if(length(params$metadata$categorical_cols) > 0) {
  if (is_latex) {
    escaped_cols <- escape_latex(params$metadata$categorical_cols)
    cat("\\begin{itemize}\n")
    for (col in escaped_cols) {
      cat("\\item ", col, "\n")
    }
    cat("\\end{itemize}\n")
  } else {
    cat("\n")
    for (col in params$metadata$categorical_cols) {
      cat("- ", col, "\n")
    }
    cat("\n")
  }
} else {
  cat("None\n")
}
```

### Excluded Columns

```{r excluded-cols, echo=FALSE, results='asis'}
# Detect output format
is_latex <- knitr::is_latex_output()

if(length(params$metadata$excluded_cols) > 0) {
  if (is_latex) {
    escaped_cols <- escape_latex(params$metadata$excluded_cols)
    cat("\\begin{itemize}\n")
    for (col in escaped_cols) {
      cat("\\item ", col, "\n")
    }
    cat("\\end{itemize}\n")
  } else {
    cat("\n")
    for (col in params$metadata$excluded_cols) {
      cat("- ", col, "\n")
    }
    cat("\n")
  }
} else {
  cat("None\n")
}
```

---

\vspace{0.5cm}

\begin{center}
\color[rgb]{0.17,0.24,0.31}
\rule{0.5\textwidth}{0.5pt}

\textit{Report generated by \textbf{autoFlagR}: AI-Driven Anomaly Detection for Data Quality}

\vspace{0.2cm}

\small{For more information, visit: \url{https://github.com/vikrant31/autoFlagR}}
\end{center}
