# ==============================================================================
# File: tests/testthat/test-apca.R
# Description: Comprehensive Unit Testing for Advanced PCA (APCA) package
# ==============================================================================

library(testthat)

# Prepare base R dataset for testing
data_matrix <- as.matrix(mtcars)
n_comp <- 3

test_that("All 9 APCA methods return a valid S3 structure", {

  methods <- c("svd", "power", "qr", "nipals", "jacobi", "arnoldi", "als", "ppca", "gha")

  for (m in methods) {
    # Run computation
    res <- apca(data_matrix, n_components = n_comp, method = m, center = TRUE, scale = TRUE)

    # 1. Validate Object Class (DIPERBAIKI: Tanpa argumen info)
    expect_s3_class(res, "apca")

    # 2. Validate Required S3 Elements (CRAN Standard)
    expected_elements <- c("scores", "loadings", "eigenvalues", "variance_explained",
                           "method", "computation_time", "call", "dimension")
    expect_true(all(expected_elements %in% names(res)))

    # 3. Validate Output Matrix Dimensions
    expect_equal(ncol(res$scores), n_comp)
    expect_equal(ncol(res$loadings), n_comp)
    expect_equal(length(res$eigenvalues), n_comp)
  }
})

test_that("Numerical Accuracy (Gold Standard): SVD, QR, and Jacobi are equivalent", {

  res_svd <- apca(data_matrix, n_components = 3, method = "svd", scale = TRUE)
  res_qr  <- apca(data_matrix, n_components = 3, method = "qr", scale = TRUE)
  res_jac <- apca(data_matrix, n_components = 3, method = "jacobi", scale = TRUE)

  # Use 1e-6 tolerance to handle machine floating-point rounding
  expect_equal(res_svd$eigenvalues, res_qr$eigenvalues, tolerance = 1e-6)
  expect_equal(res_svd$eigenvalues, res_jac$eigenvalues, tolerance = 1e-6)

  expect_equal(res_svd$variance_explained, res_qr$variance_explained, tolerance = 1e-6)
  expect_equal(res_svd$variance_explained, res_jac$variance_explained, tolerance = 1e-6)
})

test_that("Advanced Method (PPCA) contains appropriate attributes", {
  res_ppca <- apca(data_matrix, n_components = 2, method = "ppca", scale = TRUE)

  # PPCA must contain noise variance (sigma_squared) attribute
  expect_true("sigma_squared" %in% names(res_ppca))
  expect_type(res_ppca$sigma_squared, "double")
})

test_that("S3 Methods (Print, Summary, Plot) execute without errors", {

  res <- apca(data_matrix, n_components = 2, method = "svd", scale = TRUE)

  # Test Summary
  res_sum <- summary(res)
  expect_s3_class(res_sum, "summary.apca")
  expect_true("cum_var" %in% names(res_sum))

  # Test Print (DIPERBAIKI: Menggunakan expect_output)
  expect_output(print(res))
  expect_output(print(res_sum))

  # Test Plotting (Redirect graphics to NULL to avoid popup windows during testing)
  pdf(file = NULL)
  expect_error(plot(res, pc_x = 1, pc_y = 2), NA) # NA means we expect NO error
  dev.off()
})

test_that("Error Handling & Input Validation work as expected", {

  # 1. Invalid method
  expect_error(apca(data_matrix, method = "invalid_method"), "not supported")

  # 2. Input data is not numeric
  expect_error(apca("i_am_not_a_matrix"), "matrix or a data frame")

  # 3. Requested components exceed variables
  expect_error(apca(data_matrix, n_components = 100), "cannot exceed")

  # 4. Plotting requests unavailable axes
  res_2pc <- apca(data_matrix, n_components = 2, method = "svd")
  expect_error(plot(res_2pc, pc_x = 1, pc_y = 3), "Cannot plot PC3")
})
