library(testthat)
library(DataAudit)


test_that("summary returns correct class", {

  result <- apply_audit_rules(
    data.frame(
      x = 1:5
    ),
    audit_rules()
  )

  s <- summary(result)

  expect_s3_class(
    s,
    "summary.DataAuditRuleResult"
  )

  expect_type(
    s,
    "list"
  )
})


test_that("summary contains expected components", {

  result <- apply_audit_rules(
    data.frame(
      x = 1:5
    ),
    audit_rules()
  )

  s <- summary(result)

  expect_equal(
    names(s),
    c(
      "status",
      "total_violations",
      "by_rule",
      "violations"
    )
  )
})


test_that("summary preserves status", {

  result <- apply_audit_rules(
    data.frame(
      x = 1:5
    ),
    audit_rules()
  )

  s <- summary(result)

  expect_equal(
    s$status,
    "Passed"
  )

  expect_equal(
    s$total_violations,
    0L
  )
})


test_that("summary preserves rule counts", {

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

  result <- apply_audit_rules(
    dat,
    audit_rules(
      range = list(
        Age = c(0, 30)
      )
    )
  )

  s <- summary(result)

  expect_equal(
    s$by_rule,
    result$summary
  )

  expect_equal(
    s$total_violations,
    2L
  )
})


test_that("summary returns empty violation table when clean", {

  result <- apply_audit_rules(
    data.frame(
      Age = c(
        5,
        10,
        20
      )
    ),
    audit_rules(
      range = list(
        Age = c(0, 30)
      )
    )
  )

  s <- summary(result)

  expect_true(
    is.data.frame(
      s$violations
    )
  )

  expect_equal(
    nrow(s$violations),
    0L
  )

  expect_equal(
    names(s$violations),
    c(
      "Rule",
      "Variable",
      "Row",
      "Value"
    )
  )
})


test_that("summary collects range violations", {

  result <- apply_audit_rules(
    data.frame(
      Age = c(
        5,
        40,
        -2
      )
    ),
    audit_rules(
      range = list(
        Age = c(0, 30)
      )
    )
  )

  s <- summary(result)

  expect_equal(
    s$violations$Rule,
    c(
      "Range",
      "Range"
    )
  )

  expect_equal(
    s$violations$Variable,
    c(
      "Age",
      "Age"
    )
  )

  expect_equal(
    s$violations$Row,
    c(
      2L,
      3L
    )
  )

  expect_equal(
    s$violations$Value,
    c(
      "40",
      "-2"
    )
  )
})


test_that("summary collects all rule types", {

  dat <- data.frame(
    ID = c(
      "A001",
      "A002",
      "A002",
      "BAD"
    ),
    Species = c(
      "Dog",
      "Horse",
      "",
      "Cat"
    ),
    Age = c(
      5,
      40,
      -2,
      10
    ),
    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}$"
    )
  )

  result <- apply_audit_rules(
    dat,
    rules
  )

  s <- summary(result)

  expect_true(
    all(
      c(
        "Range",
        "Category",
        "Unique",
        "Required",
        "Pattern"
      ) %in%
        s$violations$Rule
    )
  )
})


test_that("summary violation count matches result", {

  dat <- data.frame(
    ID = c(
      "A001",
      "A002",
      "A002"
    ),
    Age = c(
      5,
      40,
      -2
    )
  )

  result <- apply_audit_rules(
    dat,
    audit_rules(
      range = list(
        Age = c(0, 30)
      ),
      unique = "ID"
    )
  )

  s <- summary(result)

  expect_equal(
    nrow(s$violations),
    result$total_violations
  )
})


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

test_that("summary rejects object without correct class", {

  x <- list()

  expect_error(
    summary.DataAuditRuleResult(x),
    "Invalid 'DataAuditRuleResult' object"
  )
})


test_that("summary rejects malformed object", {

  x <- list(
    status = "Passed"
  )

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

  expect_error(
    summary(x),
    "Invalid 'DataAuditRuleResult' object"
  )
})


test_that("summary rejects non-data-frame rule summary", {

  result <- apply_audit_rules(
    data.frame(
      x = 1:5
    ),
    audit_rules()
  )

  result$summary <- "invalid"

  expect_error(
    summary(result),
    "Invalid rule summary in 'DataAuditRuleResult'"
  )
})


test_that("summary rejects malformed rule summary", {

  result <- apply_audit_rules(
    data.frame(
      x = 1:5
    ),
    audit_rules()
  )

  result$summary <- data.frame(
    Wrong = 1
  )

  expect_error(
    summary(result),
    "Invalid rule summary in 'DataAuditRuleResult'"
  )
})
test_that("summary skips malformed internal rule table", {

  result <- apply_audit_rules(
    data.frame(
      Age = c(
        5,
        40
      )
    ),
    audit_rules(
      range = list(
        Age = c(0, 30)
      )
    )
  )

  result$range$Age <-
    "malformed"

  expect_no_error(
    s <- summary(result)
  )

  expect_equal(
    nrow(s$violations),
    0L
  )
})


test_that("summary handles rule table without Row or Value", {

  result <- apply_audit_rules(
    data.frame(
      Age = c(
        5,
        40
      )
    ),
    audit_rules(
      range = list(
        Age = c(0, 30)
      )
    )
  )

  result$range$Age <- data.frame(
    Violation = c(
      FALSE,
      TRUE
    )
  )

  s <- summary(result)

  expect_equal(
    s$violations$Row,
    2L
  )

  expect_true(
    is.na(
      s$violations$Value
    )
  )
})
