library(testthat)
library(DataAudit)


test_that("audit_score returns correct class", {

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

  result <- audit_score(report)

  expect_s3_class(
    result,
    "DataAuditScore"
  )

  expect_type(
    result,
    "list"
  )
})


test_that("audit_score returns expected components", {

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

  result <- audit_score(report)

  expect_equal(
    names(result),
    c(
      "score",
      "quality",
      "automatic_issues",
      "rule_violations",
      "overall_issues",
      "rows",
      "variables",
      "total_cells"
    )
  )
})


test_that("clean data receives score 100", {

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

  result <- audit_score(report)

  expect_equal(
    result$score,
    100
  )

  expect_equal(
    result$quality,
    "Excellent"
  )
})


test_that("issues reduce audit score", {

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

  dirty <- audit_data(
    data.frame(
      x = c(
        1,
        NA,
        Inf,
        4,
        5
      )
    ),
    include_nzv = FALSE
  )

  expect_lt(
    audit_score(dirty)$score,
    audit_score(clean)$score
  )
})


test_that("rule violations reduce score", {

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

  without_rules <- audit_data(
    dat,
    include_nzv = FALSE
  )

  with_rules <- audit_data(
    dat,
    include_nzv = FALSE,
    rules = audit_rules(
      range = list(
        Age = c(0, 30)
      )
    )
  )

  expect_lt(
    audit_score(with_rules)$score,
    audit_score(without_rules)$score
  )
})


test_that("score remains between zero and 100", {

  report <- audit_data(
    data.frame(
      x = c(
        NA,
        Inf,
        -Inf
      )
    ),
    include_nzv = TRUE
  )

  result <- audit_score(report)

  expect_gte(
    result$score,
    0
  )

  expect_lte(
    result$score,
    100
  )
})


test_that("audit_score reports dataset dimensions", {

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

  result <- audit_score(report)

  expect_equal(
    result$rows,
    5L
  )

  expect_equal(
    result$variables,
    2L
  )

  expect_equal(
    result$total_cells,
    10
  )
})


test_that("audit_score reports issue counts", {

  report <- audit_data(
    data.frame(
      x = c(
        1,
        NA,
        Inf
      )
    ),
    include_nzv = FALSE
  )

  result <- audit_score(report)

  expect_equal(
    result$automatic_issues,
    report$summary$TotalIssues
  )

  expect_equal(
    result$overall_issues,
    report$summary$OverallIssues
  )
})


test_that("audit_score rejects non-report object", {

  expect_error(
    audit_score(
      data.frame(
        x = 1:5
      )
    ),
    "'x' must be a DataAuditReport object"
  )
})


test_that("audit_score rejects malformed report", {

  x <- list()

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

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


test_that("audit_score rejects invalid summary", {

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

  report$summary <- "invalid"

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


test_that("audit_score rejects summary missing fields", {

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

  report$summary <- data.frame(
    Rows = 5
  )

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


test_that("audit_score rejects invalid summary values", {

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

  report$summary$Rows <- 0

  expect_error(
    audit_score(report),
    "Invalid values in DataAudit summary"
  )
})
test_that("quality categories use expected thresholds", {

  make_report <- function(score_target) {

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

    burden <-
      100 / score_target - 1

    issues <-
      burden * 100

    report$summary$TotalIssues <-
      issues

    report$summary$RuleViolations <-
      0

    report$summary$OverallIssues <-
      issues

    report
  }

  expect_equal(
    audit_score(
      make_report(95)
    )$quality,
    "Excellent"
  )

  expect_equal(
    audit_score(
      make_report(85)
    )$quality,
    "Good"
  )

  expect_equal(
    audit_score(
      make_report(70)
    )$quality,
    "Moderate"
  )

  expect_equal(
    audit_score(
      make_report(50)
    )$quality,
    "Poor"
  )

  expect_equal(
    audit_score(
      make_report(40)
    )$quality,
    "Critical"
  )
})
