library(testthat)
library(DataAudit)


#==========================================================
# Basic print behaviour
#==========================================================

test_that("print method returns object invisibly", {

  dat <- data.frame(
    x = 1:5
  )

  report <- audit_data(
    dat,
    include_nzv = FALSE
  )

  result <- withVisible(
    print(report)
  )

  expect_false(
    result$visible
  )

  expect_identical(
    result$value,
    report
  )
})


test_that("print method displays report header", {

  report <- audit_data(
    data.frame(
      x = 1:5
    ),
    include_nzv = FALSE
  )

  expect_output(
    print(report),
    "DataAudit Report"
  )
})


test_that("print method displays dimensions", {

  report <- audit_data(
    data.frame(
      x = 1:4,
      y = letters[1:4]
    ),
    include_nzv = FALSE
  )

  output <- capture.output(
    print(report)
  )

  expect_true(
    any(
      grepl(
        "Rows:",
        output,
        fixed = TRUE
      )
    )
  )

  expect_true(
    any(
      grepl(
        "Variables:",
        output,
        fixed = TRUE
      )
    )
  )
})


#==========================================================
# Automatic audit results
#==========================================================

test_that("print method reports variables with issues", {

  dat <- data.frame(
    x = c(
      1,
      NA,
      3
    ),
    y = 1:3
  )

  report <- audit_data(
    dat,
    include_nzv = FALSE
  )

  output <- capture.output(
    print(report)
  )

  expect_true(
    any(
      grepl(
        "Variables with issues",
        output,
        fixed = TRUE
      )
    )
  )

  expect_true(
    any(
      grepl(
        "x",
        output,
        fixed = TRUE
      )
    )
  )
})


test_that("print method reports no variable issues", {

  dat <- data.frame(
    x = 1:5,
    y = letters[1:5],
    stringsAsFactors = FALSE
  )

  report <- audit_data(
    dat,
    include_nzv = FALSE
  )

  expect_output(
    print(report),
    "No variable-level issues detected"
  )
})


test_that("print method reports duplicate rows", {

  dat <- data.frame(
    x = c(
      1,
      2,
      2,
      3
    ),
    y = c(
      "A",
      "B",
      "B",
      "C"
    )
  )

  report <- audit_data(
    dat,
    include_nzv = FALSE
  )

  output <- capture.output(
    print(report)
  )

  expect_true(
    any(
      grepl(
        "Duplicated rows",
        output,
        fixed = TRUE
      )
    )
  )

  expect_true(
    any(
      grepl(
        "2",
        output,
        fixed = TRUE
      )
    )
  )

  expect_true(
    any(
      grepl(
        "3",
        output,
        fixed = TRUE
      )
    )
  )
})


test_that("print method reports absence of duplicates", {

  report <- audit_data(
    data.frame(
      x = 1:5
    ),
    include_nzv = FALSE
  )

  expect_output(
    print(report),
    "No duplicated rows detected"
  )
})


test_that("print method displays issue counts", {

  dat <- data.frame(
    x = c(
      1,
      NA,
      Inf
    )
  )

  report <- audit_data(
    dat,
    include_nzv = FALSE
  )

  output <- capture.output(
    print(report)
  )

  expect_true(
    any(
      grepl(
        "Total issues:",
        output,
        fixed = TRUE
      )
    )
  )

  expect_true(
    any(
      grepl(
        "Rule violations:",
        output,
        fixed = TRUE
      )
    )
  )

  expect_true(
    any(
      grepl(
        "Overall issues:",
        output,
        fixed = TRUE
      )
    )
  )
})


#==========================================================
# Custom audit rules
#==========================================================

test_that("print method displays custom rule validation", {

  dat <- data.frame(
    Age = c(
      5,
      40,
      -2
    )
  )

  rules <- audit_rules(
    range = list(
      Age = c(0, 30)
    )
  )

  report <- audit_data(
    dat,
    include_nzv = FALSE,
    rules = rules
  )

  output <- capture.output(
    print(report)
  )

  expect_true(
    any(
      grepl(
        "Custom rule validation",
        output,
        fixed = TRUE
      )
    )
  )

  expect_true(
    any(
      grepl(
        "Total rule violations: 2",
        output,
        fixed = TRUE
      )
    )
  )
})


test_that("print method displays rule summary", {

  dat <- data.frame(
    Age = c(
      5,
      40
    )
  )

  rules <- audit_rules(
    range = list(
      Age = c(0, 30)
    )
  )

  report <- audit_data(
    dat,
    include_nzv = FALSE,
    rules = rules
  )

  output <- capture.output(
    print(report)
  )

  expect_true(
    any(
      grepl(
        "Range",
        output,
        fixed = TRUE
      )
    )
  )

  expect_true(
    any(
      grepl(
        "Violations",
        output,
        fixed = TRUE
      )
    )
  )
})


test_that("print method omits custom rule section without rules", {

  report <- audit_data(
    data.frame(
      x = 1:5
    ),
    include_nzv = FALSE
  )

  output <- capture.output(
    print(report)
  )

  expect_false(
    any(
      grepl(
        "Custom rule validation",
        output,
        fixed = TRUE
      )
    )
  )
})


test_that("print method displays multiple rule types", {

  dat <- data.frame(
    ID = c(
      "A001",
      "A002",
      "A002"
    ),
    Species = c(
      "Dog",
      "Horse",
      ""
    ),
    Age = c(
      5,
      40,
      -2
    ),
    stringsAsFactors = FALSE
  )

  rules <- audit_rules(
    range = list(
      Age = c(0, 30)
    ),
    category = list(
      Species = c(
        "Dog",
        "Cat"
      )
    ),
    unique = "ID",
    required = "Species",
    pattern = list(
      ID = "^A[0-9]{3}$"
    )
  )

  report <- audit_data(
    dat,
    include_nzv = FALSE,
    rules = rules
  )

  output <- capture.output(
    print(report)
  )

  for (
    rule_name in c(
      "Range",
      "Category",
      "Unique",
      "Required",
      "Pattern"
    )
  ) {

    expect_true(
      any(
        grepl(
          rule_name,
          output,
          fixed = TRUE
        )
      )
    )
  }
})


#==========================================================
# Defensive validation
#==========================================================

test_that("print method rejects object without class", {

  x <- list(
    overview = data.frame(),
    variables = data.frame(),
    duplicates = data.frame(),
    summary = data.frame(),
    rules = NULL
  )

  expect_error(
    print.DataAuditReport(x),
    "Invalid 'DataAuditReport' object"
  )
})


test_that("print method rejects malformed report", {

  x <- list(
    overview = data.frame()
  )

  class(x) <- c(
    "DataAuditReport",
    "list"
  )

  expect_error(
    print(x),
    "Invalid 'DataAuditReport' object"
  )
})


test_that("print method rejects non-data-frame summary", {

  x <- list(
    overview = data.frame(),
    variables = data.frame(
      Issue = logical()
    ),
    duplicates = data.frame(
      Row = integer(),
      Duplicate = logical()
    ),
    summary = "invalid",
    rules = NULL
  )

  class(x) <- c(
    "DataAuditReport",
    "list"
  )

  expect_error(
    print(x),
    "Invalid summary in 'DataAuditReport'"
  )
})


test_that("print method rejects summary missing required columns", {

  x <- list(
    overview = data.frame(),
    variables = data.frame(
      Issue = logical()
    ),
    duplicates = data.frame(
      Row = integer(),
      Duplicate = logical()
    ),
    summary = data.frame(
      Wrong = 1
    ),
    rules = NULL
  )

  class(x) <- c(
    "DataAuditReport",
    "list"
  )

  expect_error(
    print(x),
    "Invalid summary in 'DataAuditReport'"
  )
})


test_that("print method rejects invalid variable summary", {

  report <- audit_data(
    data.frame(
      x = 1:5
    ),
    include_nzv = FALSE
  )

  report$variables <- "invalid"

  expect_error(
    print(report),
    "Invalid variable summary in 'DataAuditReport'"
  )
})


test_that("print method rejects variable summary without Issue", {

  report <- audit_data(
    data.frame(
      x = 1:5
    ),
    include_nzv = FALSE
  )

  report$variables <- data.frame(
    Variable = "x"
  )

  expect_error(
    print(report),
    "Invalid variable summary in 'DataAuditReport'"
  )
})


test_that("print method rejects invalid duplicate summary", {

  report <- audit_data(
    data.frame(
      x = 1:5
    ),
    include_nzv = FALSE
  )

  report$duplicates <- "invalid"

  expect_error(
    print(report),
    "Invalid duplicate summary in 'DataAuditReport'"
  )
})


test_that("print method rejects duplicate summary without Duplicate", {

  report <- audit_data(
    data.frame(
      x = 1:5
    ),
    include_nzv = FALSE
  )

  report$duplicates <- data.frame(
    Row = 1:5
  )

  expect_error(
    print(report),
    "Invalid duplicate summary in 'DataAuditReport'"
  )
})


test_that("print method rejects invalid rule result", {

  report <- audit_data(
    data.frame(
      x = 1:5
    ),
    include_nzv = FALSE
  )

  report$rules <- list(
    invalid = TRUE
  )

  expect_error(
    print(report),
    "Invalid rule results in 'DataAuditReport'"
  )
})
