package test

import (
	"errors"
	"strings"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestResourceNested_basic(t *testing.T) {
	resource.UnitTest(t, resource.TestCase{
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckResourceDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: strings.TrimSpace(`
resource "test_resource_nested" "foo" {
	nested {
		string = "val"
	}
}
				`),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(
						"test_resource_nested.foo", "nested.#", "1",
					),
					resource.TestCheckResourceAttr(
						"test_resource_nested.foo", "nested.1877647874.string", "val",
					),
					resource.TestCheckResourceAttr(
						"test_resource_nested.foo", "list_block.0.sub_list_block.0.bool", "false",
					),
				),
			},
		},
	})
}

func TestResourceNested_addRemove(t *testing.T) {
	var id string
	idCheck := func(s *terraform.State) error {
		root := s.ModuleByPath(addrs.RootModuleInstance)
		res := root.Resources["test_resource_nested.foo"]
		if res.Primary.ID == id {
			return errors.New("expected new resource")
		}
		id = res.Primary.ID
		return nil
	}
	resource.UnitTest(t, resource.TestCase{
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckResourceDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: strings.TrimSpace(`
resource "test_resource_nested" "foo" {
}
				`),
				Check: resource.ComposeTestCheckFunc(
					idCheck,
					resource.TestCheckResourceAttr(
						"test_resource_nested.foo", "nested.#", "0",
					),
					// Checking for a count of 0 and a nonexistent count should
					// now be the same operation.
					resource.TestCheckNoResourceAttr(
						"test_resource_nested.foo", "nested.#",
					),
				),
			},
			resource.TestStep{
				Config: strings.TrimSpace(`
resource "test_resource_nested" "foo" {
	nested {
		string = "val"
	}
}
				`),
				Check: resource.ComposeTestCheckFunc(
					idCheck,
					resource.TestCheckResourceAttr(
						"test_resource_nested.foo", "nested.1877647874.string", "val",
					),
				),
			},
			resource.TestStep{
				Config: strings.TrimSpace(`
resource "test_resource_nested" "foo" {
	optional = true
	nested {
		string = "val"
	}
}
				`),
				Check: resource.ComposeTestCheckFunc(
					idCheck,
					resource.TestCheckResourceAttr(
						"test_resource_nested.foo", "nested.1877647874.string", "val",
					),
					resource.TestCheckResourceAttr(
						"test_resource_nested.foo", "optional", "true",
					),
				),
			},
			resource.TestStep{
				Config: strings.TrimSpace(`
resource "test_resource_nested" "foo" {
	nested {
		string = "val"
	}
}
				`),
				Check: resource.ComposeTestCheckFunc(
					idCheck,
					resource.TestCheckResourceAttr(
						"test_resource_nested.foo", "nested.1877647874.string", "val",
					),
					resource.TestCheckNoResourceAttr(
						"test_resource_nested.foo", "optional",
					),
				),
			},
			resource.TestStep{
				Config: strings.TrimSpace(`
resource "test_resource_nested" "foo" {
	nested {
		string = "val"
		optional = true
	}
}
				`),
				Check: resource.ComposeTestCheckFunc(
					idCheck,
					resource.TestCheckResourceAttr(
						"test_resource_nested.foo", "nested.2994502535.string", "val",
					),
					resource.TestCheckResourceAttr(
						"test_resource_nested.foo", "nested.2994502535.optional", "true",
					),
				),
			},
			resource.TestStep{
				Config: strings.TrimSpace(`
resource "test_resource_nested" "foo" {
}
				`),
				Check: resource.ComposeTestCheckFunc(
					idCheck,
					resource.TestCheckNoResourceAttr(
						"test_resource_nested.foo", "nested.#",
					),
				),
			},
		},
	})
}

func TestResourceNested_dynamic(t *testing.T) {
	resource.UnitTest(t, resource.TestCase{
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckResourceDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: strings.TrimSpace(`
resource "test_resource_nested" "foo" {
	dynamic "nested" {
		for_each = [["a"], []]
		content {
			string   = join(",", nested.value)
			optional = false
			dynamic "nested_again" {
				for_each = nested.value
				content {
					string = nested_again.value
				}
			}
		}
	}
}
				`),
				Check: func(s *terraform.State) error {
					rs, ok := s.RootModule().Resources["test_resource_nested.foo"]
					if !ok {
						return errors.New("missing resource in state")
					}

					got := rs.Primary.Attributes
					want := map[string]string{
						"nested.#":                                      "2",
						"nested.33842314.string":                        "a",
						"nested.33842314.optional":                      "false",
						"nested.33842314.nested_again.#":                "1",
						"nested.33842314.nested_again.936590934.string": "a",
						"nested.140280279.string":                       "",
						"nested.140280279.optional":                     "false",
						"nested.140280279.nested_again.#":               "0",
						"list_block.#":                                  "1",
						"list_block.0.sub_list_block.#":                 "1",
						"list_block.0.sub_list_block.0.bool":            "false",
						"list_block.0.sub_list_block.0.set.#":           "0",
					}
					delete(got, "id") // it's random, so not useful for testing

					if !cmp.Equal(got, want) {
						return errors.New("wrong result\n" + cmp.Diff(want, got))
					}

					return nil
				},
			},
		},
	})
}
