context("dict")

test_that("Python dictionaries can be created", {
  skip_if_no_python()
  expect_is(dict(), "python.builtin.dict")
})

test_that("Python dictionaries can be created with py_dict", {
  skip_if_no_python()
  expect_is(py_dict(list("a", "b", "c"), list(1,2,3)), "python.builtin.dict")
})

test_that("Python dictionaries can use python objects as keys", {
  skip_if_no_python()
  py <- import_builtins(convert = FALSE)
  key <- py$int(42)
  expect_error(dict(key = "foo"), NA)
  expect_is(py_dict(list(key), list("foo")), "python.builtin.dict")
})

test_that("Python dictionaries have numeric keys", {
  skip_if_no_python()
  expect_error(dict(`42` = "foo"), NA)
})

test_that("Python dictionaries can include numbers in their keys", {
  skip_if_no_python()
  expect_error(dict(foo42 = "foo"), NA)
})

test_that("Dictionary items can be get / set / removed with py_item APIs", {
  skip_if_no_python()

  d <- dict()
  one <- r_to_py(1)

  py_set_item(d, "apple", one)
  expect_equal(py_id(py_get_item(d, "apple")), py_id(one))

  py_del_item(d, "apple")
  expect_error(py_get_item(d, "apple"))
  expect_identical(py_get_item(d, "apple", silent = TRUE), NULL)
})

test_that("$, [ operators behave as expected", {
  skip_if_no_python()

  d <- dict(items = 1, apple = 42)

  expect_true(is.function(d$items))
  expect_true(py_bool(d['items'] == 1))

  expect_true(py_bool(d$apple == 42))
  expect_true(py_bool(d['apple'] == 42))

})

test_that("ordered dictionaries with non-string keys can be converted", {
  skip_if_no_python()

  builtins <- import_builtins(convert = FALSE)
  collections <- import("collections", convert = FALSE)

  t <- builtins$tuple(list(42))
  od <- collections$OrderedDict(list())
  od[[t]] <- 42

  result <- py_to_r(od)
  expect_identical(result, list("(42.0,)" = 42))

})

test_that("ordered dictionaries can be converted", {
  skip_if_no_python()

  collections <- import("collections", convert = FALSE)
  od <- collections$OrderedDict(list(tuple("a", 1),
                                     tuple("b", 2),
                                     tuple("c", 3)))

  result <- py_eval("lambda x: x")(od) # implicit conversion to R
  expect_identical(result, list(a = 1, b = 2, c = 3))

  result <- py_eval("lambda x: x", convert = FALSE)(od) # no conversion
  expect_identical(py_id(result), py_id(od))

})

test_that("py_to_r(dict) converts recursively, #1221", {
  skip_if_no_python()
  skip_if_no_numpy()
  skip_if_no_pandas()

  py <- py_run_string('
import numpy as np
import pandas as pd

np.random.seed(6012022)
tools = ["sas", "stata", "spss", "python", "r", "julia"]

random_df = pd.DataFrame({
"tool": np.random.choice(tools, 500),
"int": np.random.randint(1, 15, 500),
"num": np.random.randn(500),
"bool": np.random.choice([True, False], 500),
"date": np.random.choice(pd.date_range("2020-01-01", "2022-06-01"), 500)
})

# LIST OF DATA FRAMES
df_list = [df for i, df in random_df.groupby(["tool"])]

# DICT OF DATA FRAMES
# beginning in Pandas 2.0, .groupby() returns the key as tuple(str,), previously, as a str.
df_dict = {i[0] if isinstance(i, tuple) else i: df for i, df in random_df.groupby(["tool"])}
', local = TRUE)

  rdf_list <- py$df_list
  lapply(rdf_list, expect_s3_class, "data.frame")

  rdf_dict <- py$df_dict
  lapply(rdf_list, expect_s3_class, "data.frame")

  for (i in seq_along(rdf_dict)) {
    attr(rdf_dict[[i]], "pandas.index") <- NULL
    attr(rdf_list[[i]], "pandas.index") <- NULL
  }

  expect_identical(rdf_list, unname(rdf_dict))
  expect_identical(sort(names(rdf_dict)),
                   sort(c("sas", "stata", "spss", "python", "r", "julia")))

})



test_that("py_to_r(list) converts recursively", {
  skip_if_no_python()

  expect_identical(py_eval("[1, [2, [3, 4]]]"),
                   list(1L, list(2L, c(3L, 4L))))

  str <- "[1, {'b': 3, 'c': ['d', 4]}, [3, 5]]"
  exp <- list(1L, list(b = 3L, c = list("d", 4L)), c(3L, 5L))

  expect_identical(py_eval(str), exp)

  x <- py_eval(str, convert = FALSE)
  expect_true(is_py_object(x))
  expect_s3_class(x, "python.builtin.list")
  expect_s3_class(x, "python.builtin.object")
  expect_identical(py_to_r(x), exp)

  x1 <- x[1]
  expect_true(is_py_object(x1))
  expect_s3_class(x1, "python.builtin.dict")
  expect_s3_class(x1, "python.builtin.object")
  expect_identical(py_to_r(x1), exp[[2]])


})
