library(rearrr)
context("Pipeline")

# TODO I have only tested a few very narrow use cases
# I should find some cases and build out some more examples

# Test print methods

test_that("testing printing Pipeline", {
  xpectr::set_test_seed(42)

  pipe <- Pipeline$new()
  pipe$add_transformation(
    fn = rotate_2d,
    args = list(
      x_col = "Index",
      y_col = "A",
      origin = c(0, 0),
      degrees = 45,
      suffix = "",
      overwrite = TRUE
    ),
    name = "rotate",
    group_cols = "G"
  )
  pipe$add_transformation(
    fn = cluster_groups,
    args = list(
      cols = c("Index", "A"),
      suffix = "",
      overwrite = TRUE,
      multiplier = 0.05,
      scale_min_fn = function(x){ # For testing shortening in print()
        min(x)+min(x)^2
      }
    ),
    name = "cluster"
  )


  ## Testing 'testthat::capture_output(print(pipe))'                        ####
  ## Initially generated by xpectr
  xpectr::set_test_seed(42)
  # Assigning output
  output_11346 <- testthat::capture_output(print(pipe))
  # Testing class
  expect_equal(
    class(output_11346),
    "character",
    fixed = TRUE)
  # Testing type
  expect_type(
    output_11346,
    type = "character")
  # Testing values
  expect_equal(
    output_11346,
    paste0("Pipeline: \n  rotate\n    Arguments:  x_col=\"Index\", y_co",
           "l=\"A\", origin=c(0, 0), degrees=45, suffix=\"\", overwrite=",
           "TRUE\n    Grouping columns:  \"G\"\n  cluster\n    Arguments",
           ":  cols=c(\"Index\", \"A\"), suffix=\"\", overwrite=TRUE, mu",
           "ltiplier=0.05, scale_min_fn=function(x){min(x) +..."),
    fixed = TRUE)
  # Testing names
  expect_equal(
    names(output_11346),
    NULL,
    fixed = TRUE)
  # Testing length
  expect_equal(
    length(output_11346),
    1L)
  # Testing sum of element lengths
  expect_equal(
    sum(xpectr::element_lengths(output_11346)),
    1L)
  ## Finished testing 'testthat::capture_output(print(pipe))'               ####


})

test_that("testing printing FixedGroupsPipeline", {
  xpectr::set_test_seed(42)

  # Idea for example:
  agp <- FixedGroupsPipeline$new(3)
  agp$add_transformation(
    fn = rotate_2d,
    args = list(
      x_col = "Index",
      y_col = "A",
      origin = c(0, 0),
      suffix = "",
      overwrite = TRUE
    ),
    var_args = list(degrees = list(45, 90, 120),
                    .apply = list(TRUE, FALSE, FALSE)),
    name = "rotate"
  )
  agp$add_transformation(
    fn = cluster_groups,
    args = list(
      cols = c("Index", "A"),
      suffix = "",
      overwrite = TRUE,
      scale_min_fn = function(x){ # For testing shortening in print()
        min(x)+min(x)^2
      }
    ),
    var_args = list(multiplier = list(0.05, 0.1, 0.2)),
    name = "cluster"
  )


  ## Testing 'testthat::capture_output(print(agp))'                         ####
  ## Initially generated by xpectr
  xpectr::set_test_seed(42)
  # Assigning output
  output_10429 <- testthat::capture_output(print(agp))
  # Testing class
  expect_equal(
    class(output_10429),
    "character",
    fixed = TRUE)
  # Testing type
  expect_type(
    output_10429,
    type = "character")
  # Testing values
  expect_equal(
    output_10429,
    paste0("FixedGroupsPipeline: \n  No. expected groups: 3\n  rotate\n",
           "    Constant arguments:  x_col=\"Index\", y_col=\"A\", origi",
           "n=c(0, 0), suffix=\"\", overwrite=TRUE\n    Varying argument",
           "s:   degrees=list(45, 90, 120)\n    Don't apply to these gro",
           "ups:  2, 3\n  cluster\n    Constant arguments:  cols=c(\"Ind",
           "ex\", \"A\"), suffix=\"\", overwrite=TRUE, scale_min_fn=func",
           "tion(x){min(x) +...\n    Varying arguments:   multiplier=lis",
           "t(0.05, 0.1, 0.2)"),
    fixed = TRUE)
  # Testing names
  expect_equal(
    names(output_10429),
    NULL,
    fixed = TRUE)
  # Testing length
  expect_equal(
    length(output_10429),
    1L)
  # Testing sum of element lengths
  expect_equal(
    sum(xpectr::element_lengths(output_10429)),
    1L)
  ## Finished testing 'testthat::capture_output(print(agp))'                ####


})

test_that("testing printing GeneratedPipeline", {
  xpectr::set_test_seed(42)

  agp <- GeneratedPipeline$new()
  agp$add_transformation(
    fn = rotate_2d,
    args = list(
      x_col = "Index",
      y_col = "A",
      suffix = "",
      overwrite = TRUE
    ),
    generators = list(degrees = function(){sample.int(360, 1)},
                      origin = function(){rnorm(2)}),
    name = "rotate"
  )
  agp$add_transformation(
    fn = cluster_groups,
    args = list(
      cols = c("Index", "A"),
      suffix = "",
      overwrite = TRUE,
      scale_min_fn = function(x){ # For testing shortening in print()
        min(x)+min(x)^2
      }
    ),
    generators = list(
      multiplier = function() {
        runif(1) * 3 ^ sample.int(5, 1)
      },
      .apply=function(){sample(c(TRUE, FALSE), 1)}
    ),
    name = "cluster",
    group_cols = "G"
  )


  ## Testing 'testthat::capture_output(print(agp))'                         ####
  ## Initially generated by xpectr
  xpectr::set_test_seed(42)
  # Assigning output
  output_11364 <- testthat::capture_output(print(agp))
  # Testing class
  expect_equal(
    class(output_11364),
    "character",
    fixed = TRUE)
  # Testing type
  expect_type(
    output_11364,
    type = "character")
  # Testing values
  expect_equal(
    output_11364,
    paste0("GeneratedPipeline: \n  rotate\n    Constant arguments:  x_c",
           "ol=\"Index\", y_col=\"A\", suffix=\"\", overwrite=TRUE\n    ",
           "Generators:  degrees={sample.int(360, 1)}, origin={rnorm(2)}",
           "\n  cluster\n    Constant arguments:  cols=c(\"Index\", \"A",
           "\"), suffix=\"\", overwrite=TRUE, scale_min_fn=function(x){mi",
           "n(x) +...\n    Generators:  multiplier={runif(1) * 3^sample.",
           "int(5, 1)}\n    Apply generator:  {sample(c(TRUE, FALSE), 1)",
           "}\n    Grouping columns:  \"G\""),
    fixed = TRUE)
  # Testing names
  expect_equal(
    names(output_11364),
    NULL,
    fixed = TRUE)
  # Testing length
  expect_equal(
    length(output_11364),
    1L)
  # Testing sum of element lengths
  expect_equal(
    sum(xpectr::element_lengths(output_11364)),
    1L)
  ## Finished testing 'testthat::capture_output(print(agp))'                ####

})

# Test applying the pipelines

test_that("testing Pipeline", {
  xpectr::set_test_seed(42)

  # Create a data frame
  df <- data.frame(
    "Index" = 1:12,
    "A" = c(1:4, 9:12, 15:18),
    "G" = rep(1:3, each = 4)
  )

  pipe <- Pipeline$new()
  pipe$add_transformation(
    fn = rotate_2d,
    args = list(
      x_col = "Index",
      y_col = "A",
      origin_fn = centroid,
      degrees = 45,
      suffix = "",
      overwrite = TRUE
    ),
    name = "rotate",
    group_cols = "G"
  )
  pipe$add_transformation(
    fn = cluster_groups,
    args = list(
      cols = c("Index", "A"),
      group_cols = "G",
      suffix = "",
      overwrite = TRUE,
      multiplier = 0.05
    ),
    name = "cluster",
    group_cols = NULL # cluster_groups need entire dataset as context
  )

  # Because groups were defined in the transformations
  # The initial grouping is ignored
  expect_equal(pipe$apply(df),
               xpectr::suppress_mw(pipe$apply(dplyr::group_by(df, G))))


  ## Testing 'pipe$apply(df)'                                               ####
  ## Initially generated by xpectr
  xpectr::set_test_seed(42)
  # Assigning output
  output_10374 <- pipe$apply(df)
  # Testing class
  expect_equal(
    class(output_10374),
    c("tbl_df", "tbl", "data.frame"),
    fixed = TRUE)
  # Testing column values
  expect_equal(
    output_10374[["Index"]],
    c(2.5, 2.5, 2.5, 2.5, 6.5, 6.5, 6.5, 6.5, 10.5, 10.5, 10.5, 10.5),
    tolerance = 1e-4)
  expect_equal(
    output_10374[["A"]],
    c(0.76759, 0.85448, 0.94138, 1.02827, 10.59853, 10.68542, 10.77231,
      10.85921, 17.97173, 18.05862, 18.14552, 18.23241),
    tolerance = 1e-4)
  expect_equal(
    output_10374[["G"]],
    c(1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3),
    tolerance = 1e-4)
  expect_equal(
    output_10374[[".degrees"]],
    c(45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45),
    tolerance = 1e-4)
  # Testing column names
  expect_equal(
    names(output_10374),
    c("Index", "A", "G", ".origin", ".degrees"),
    fixed = TRUE)
  # Testing column classes
  expect_equal(
    xpectr::element_classes(output_10374),
    c("numeric", "numeric", "integer", "list", "numeric"),
    fixed = TRUE)
  # Testing column types
  expect_equal(
    xpectr::element_types(output_10374),
    c("double", "double", "integer", "list", "double"),
    fixed = TRUE)
  # Testing dimensions
  expect_equal(
    dim(output_10374),
    c(12L, 5L))
  # Testing group keys
  expect_equal(
    colnames(dplyr::group_keys(output_10374)),
    character(0),
    fixed = TRUE)
  ## Finished testing 'pipe$apply(df)'                                      ####


  # Manually applying transformations

  step_1 <- rotate_2d(
    data = dplyr::group_by(df, G),
    x_col = "Index",
    y_col = "A",
    origin_fn = centroid,
    degrees = 45,
    suffix = "",
    overwrite = TRUE
  )
  expect_true(!dplyr::is_grouped_df(step_1))
  step_2 <- cluster_groups(
    data = step_1,
    cols = c("Index", "A"),
    group_cols = "G",
    suffix = "",
    overwrite = TRUE,
    multiplier = 0.05
  )

  expect_equal(step_2, pipe$apply(df))


  ## Not grouped

  pipe <- Pipeline$new()
  pipe$add_transformation(
    fn = rotate_2d,
    args = list(
      x_col = "Index",
      y_col = "A",
      origin_fn = centroid,
      degrees = 45,
      suffix = "",
      overwrite = TRUE
    ),
    name = "rotate"
  )
  pipe$add_transformation(
    fn = cluster_groups,
    args = list(
      cols = c("Index", "A"),
      group_cols = "G",
      suffix = "",
      overwrite = TRUE,
      multiplier = 0.05
    ),
    name = "cluster"
  )


  ## Testing 'pipe$apply(dplyr::group_by(df, G))'                           ####
  ## Initially generated by xpectr
  xpectr::set_test_seed(42)
  # Testing side effects
  # Assigning side effects
  side_effects_11387 <- xpectr::capture_side_effects(pipe$apply(dplyr::group_by(df, G)), reset_seed = TRUE)
  expect_equal(
    xpectr::strip(side_effects_11387[['warnings']]),
    xpectr::strip("Ignoring groups in `data`. Only the `group_cols` grouping specifications are used."),
    fixed = TRUE)
  expect_equal(
    xpectr::strip(side_effects_11387[['messages']]),
    xpectr::strip(character(0)),
    fixed = TRUE)

  step_1 <- rotate_2d(
    data = df,
    x_col = "Index",
    y_col = "A",
    origin_fn = centroid,
    degrees = 45,
    suffix = "",
    overwrite = TRUE
  )
  step_2 <- cluster_groups(
    data = step_1,
    cols = c("Index", "A"),
    group_cols = "G",
    suffix = "",
    overwrite = TRUE,
    multiplier = 0.05
  )

  expect_equal(step_2, pipe$apply(df))

})

test_that("testing FixedGroupsPipeline()", {
  xpectr::set_test_seed(42)

  # Create a data frame
  df <- data.frame(
    "Index" = 1:12,
    "A" = c(1:4, 9:12, 15:18),
    "G" = rep(1:3, each = 4)
  )

  agp <- FixedGroupsPipeline$new(3)
  agp$add_transformation(
    fn = rotate_2d,
    args = list(
      x_col = "Index",
      y_col = "A",
      origin = c(0, 0),
      suffix = "",
      overwrite = TRUE
    ),
    var_args = list(degrees = list(45, 90, 120),
                    .apply = list(TRUE, FALSE, FALSE)),
    name = "rotate"
  )
  agp$add_transformation(
    fn = cluster_groups,
    args = list(
      cols = c("Index", "A"),
      group_cols = "G", # Required as called separately per group with ungrouped subset
      suffix = "",
      overwrite = TRUE
    ),
    var_args = list(multiplier = list(0.05, 0.1, 0.2)),
    name = "cluster"
  )


  ## Testing 'agp$apply(df)'                                                ####
  ## Initially generated by xpectr
  xpectr::set_test_seed(42)
  # Testing side effects
  # Assigning side effects
  side_effects_11346 <- xpectr::capture_side_effects(agp$apply(df), reset_seed = TRUE)
  expect_equal(
    xpectr::strip(side_effects_11346[['error']]),
    xpectr::strip("`data` did not have exactly 3 groups as expected."),
    fixed = TRUE)
  expect_equal(
    xpectr::strip(side_effects_11346[['error_class']]),
    xpectr::strip(c("simpleError", "error", "condition")),
    fixed = TRUE)
  ## Finished testing 'agp$apply(df)'                                       ####

  # Note that we don't apply rotation to 2 and 3

  ## Testing 'agp$apply(dplyr::group_by(df, G))'                            ####
  ## Initially generated by xpectr
  xpectr::set_test_seed(42)
  # Assigning output
  output_11387 <- agp$apply(dplyr::group_by(df, G))
  # Testing class
  expect_equal(
    class(output_11387),
    c("grouped_df", "tbl_df", "tbl", "data.frame"),
    fixed = TRUE)
  # Testing column values
  expect_equal(
    output_11387[["Index"]],
    c(0, 0, 0, 0, 5.075, 6.025, 6.975, 7.925, 9.075, 10.025, 10.975,
      11.925),
    tolerance = 1e-4)
  expect_equal(
    output_11387[["A"]],
    c(1.52028, 2.86378, 4.20729, 5.55079, 9.075, 10.025, 10.975, 11.925,
      15.075, 16.025, 16.975, 17.925),
    tolerance = 1e-4)
  expect_equal(
    output_11387[["G"]],
    c(1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3),
    tolerance = 1e-4)
  expect_equal(
    output_11387[[".degrees"]],
    c(45, 45, 45, 45, NA, NA, NA, NA, NA, NA, NA, NA),
    tolerance = 1e-4)
  # Testing column names
  expect_equal(
    names(output_11387),
    c("Index", "A", "G", ".origin", ".degrees"),
    fixed = TRUE)
  # Testing column classes
  expect_equal(
    xpectr::element_classes(output_11387),
    c("numeric", "numeric", "integer", "list", "numeric"),
    fixed = TRUE)
  # Testing column types
  expect_equal(
    xpectr::element_types(output_11387),
    c("double", "double", "integer", "list", "double"),
    fixed = TRUE)
  # Testing dimensions
  expect_equal(
    dim(output_11387),
    c(12L, 5L))
  # Testing group keys
  expect_equal(
    colnames(dplyr::group_keys(output_11387)),
    "G",
    fixed = TRUE)
  ## Finished testing 'agp$apply(dplyr::group_by(df, G))'                   ####


})

test_that("testing GeneratedPipeline()", {
  xpectr::set_test_seed(42)

  # Create a data frame
  df <- data.frame(
    "Index" = 1:12,
    "A" = c(1:4, 9:12, 15:18),
    "G" = rep(1:3, each = 4)
  )

  agp <- GeneratedPipeline$new()
  agp$add_transformation(
    fn = rotate_2d,
    args = list(
      x_col = "Index",
      y_col = "A",
      suffix = "",
      overwrite = TRUE
    ),
    generators = list(degrees = function(){sample.int(360, 1)},
                      origin = function(){rnorm(2)}),
    name = "rotate",
    group_cols = "G"
  )
  agp$add_transformation(
    fn = cluster_groups,
    args = list(
      cols = c("Index", "A"),
      suffix = "",
      overwrite = TRUE,
      scale_min_fn = function(x){ # For testing shortening in print()
        min(x)+min(x)^2
      },
      group_cols = "G"
    ),
    generators = list(
      multiplier = function() {
        runif(1) * 3 ^ sample.int(5, 1)
      },
      .apply=function(){sample(c(TRUE, FALSE), 1)}
    ),
    name = "cluster",
    group_cols = "G"
  )


  ## Testing 'agp$apply(df)'                                                ####
  ## Initially generated by xpectr
  xpectr::set_test_seed(42)
  # Assigning output
  output_16399 <- agp$apply(df)
  # Testing class
  expect_equal(
    class(output_16399),
    c("grouped_df", "tbl_df", "tbl", "data.frame"),
    fixed = TRUE)
  # Testing column values
  expect_equal(
    output_16399[["G"]],
    c(1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3),
    tolerance = 1e-4)
  expect_equal(
    output_16399[["Index"]],
    c(1.09313, 2.45915, 3.82518, 5.19121, -2.65417, -3.52484, -4.39552,
      -5.2662, -11.69701, 64.92873, 141.55447, 218.1802),
    tolerance = 1e-4)
  expect_equal(
    output_16399[["A"]],
    c(1.25944, 1.62546, 1.99149, 2.35751, -8.65961, -9.77403, -10.88844,
      -12.00286, -13.65532, 68.06585, 149.78703, 231.5082),
    tolerance = 1e-4)
  expect_equal(
    output_16399[[".degrees"]],
    c(330, 330, 330, 330, 187, 187, 187, 187, 165, 165, 165, 165),
    tolerance = 1e-4)
  expect_equal(
    unlist(output_16399$.origin),
    c(Index = 1.53068, A = 0.95594, Index = 1.53068, A = 0.95594, Index = 1.53068,
      A = 0.95594, Index = 1.53068, A = 0.95594, Index = 0.63286,
      A = 0.40427, Index = 0.63286, A = 0.40427, Index = 0.63286,
      A = 0.40427, Index = 0.63286, A = 0.40427, Index = 0.58021,
      A = -0.6575, Index = 0.58021, A = -0.6575, Index = 0.58021,
      A = -0.6575, Index = 0.58021, A = -0.6575),
    tolerance = 1e-4)
  # Testing column names
  expect_equal(
    names(output_16399),
    c("G", "Index", "A", ".origin", ".degrees"),
    fixed = TRUE)
  # Testing column classes
  expect_equal(
    xpectr::element_classes(output_16399),
    c("integer", "numeric", "numeric", "list", "integer"),
    fixed = TRUE)
  # Testing column types
  expect_equal(
    xpectr::element_types(output_16399),
    c("integer", "double", "double", "list", "integer"),
    fixed = TRUE)
  # Testing dimensions
  expect_equal(
    dim(output_16399),
    c(12L, 5L))
  # Testing group keys
  expect_equal(
    colnames(dplyr::group_keys(output_16399)),
    "G",
    fixed = TRUE)
  ## Finished testing 'agp$apply(df)'                                       ####


})

