// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package looker

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"regexp"
	"strings"
	"testing"
	"time"

	"github.com/googleapis/genai-toolbox/internal/log"
	"github.com/googleapis/genai-toolbox/internal/testutils"
	"github.com/googleapis/genai-toolbox/internal/util"
	"github.com/googleapis/genai-toolbox/tests"

	"github.com/looker-open-source/sdk-codegen/go/rtl"
	v4 "github.com/looker-open-source/sdk-codegen/go/sdk/v4"
)

var (
	LookerSourceKind   = "looker"
	LookerBaseUrl      = os.Getenv("LOOKER_BASE_URL")
	LookerVerifySsl    = os.Getenv("LOOKER_VERIFY_SSL")
	LookerClientId     = os.Getenv("LOOKER_CLIENT_ID")
	LookerClientSecret = os.Getenv("LOOKER_CLIENT_SECRET")
	LookerProject      = os.Getenv("LOOKER_PROJECT")
	LookerLocation     = os.Getenv("LOOKER_LOCATION")
)

func getLookerVars(t *testing.T) map[string]any {
	switch "" {
	case LookerBaseUrl:
		t.Fatal("'LOOKER_BASE_URL' not set")
	case LookerVerifySsl:
		t.Fatal("'LOOKER_VERIFY_SSL' not set")
	case LookerClientId:
		t.Fatal("'LOOKER_CLIENT_ID' not set")
	case LookerClientSecret:
		t.Fatal("'LOOKER_CLIENT_SECRET' not set")
	case LookerProject:
		t.Fatal("'LOOKER_PROJECT' not set")
	case LookerLocation:
		t.Fatal("'LOOKER_LOCATION' not set")
	}

	return map[string]any{
		"kind":          LookerSourceKind,
		"base_url":      LookerBaseUrl,
		"verify_ssl":    (LookerVerifySsl == "true"),
		"client_id":     LookerClientId,
		"client_secret": LookerClientSecret,
		"project":       LookerProject,
		"location":      LookerLocation,
	}
}

func TestLooker(t *testing.T) {
	sourceConfig := getLookerVars(t)
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
	defer cancel()

	testLogger, err := log.NewStdLogger(os.Stdout, os.Stderr, "info")
	if err != nil {
		t.Fatalf("unexpected error: %s", err)
	}
	ctx = util.WithLogger(ctx, testLogger)

	var args []string

	// Write config into a file and pass it to command

	toolsFile := map[string]any{
		"sources": map[string]any{
			"my-instance": sourceConfig,
		},
		"tools": map[string]any{
			"get_models": map[string]any{
				"kind":        "looker-get-models",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"get_explores": map[string]any{
				"kind":        "looker-get-explores",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"get_dimensions": map[string]any{
				"kind":        "looker-get-dimensions",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"get_measures": map[string]any{
				"kind":        "looker-get-measures",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"get_filters": map[string]any{
				"kind":        "looker-get-filters",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"get_parameters": map[string]any{
				"kind":        "looker-get-parameters",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"query": map[string]any{
				"kind":        "looker-query",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"query_sql": map[string]any{
				"kind":        "looker-query-sql",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"query_url": map[string]any{
				"kind":        "looker-query-url",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"get_looks": map[string]any{
				"kind":        "looker-get-looks",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"make_look": map[string]any{
				"kind":        "looker-make-look",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"get_dashboards": map[string]any{
				"kind":        "looker-get-dashboards",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"make_dashboard": map[string]any{
				"kind":        "looker-make-dashboard",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"add_dashboard_filter": map[string]any{
				"kind":        "looker-add-dashboard-filter",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"add_dashboard_element": map[string]any{
				"kind":        "looker-add-dashboard-element",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"conversational_analytics": map[string]any{
				"kind":        "looker-conversational-analytics",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"health_pulse": map[string]any{
				"kind":        "looker-health-pulse",
				"source":      "my-instance",
				"description": "Checks the health of a Looker instance by running a series of checks on the system.",
			},
			"health_analyze": map[string]any{
				"kind":        "looker-health-analyze",
				"source":      "my-instance",
				"description": "Provides analysis of a Looker instance's projects, models, or explores.",
			},
			"health_vacuum": map[string]any{
				"kind":        "looker-health-vacuum",
				"source":      "my-instance",
				"description": "Vacuums unused content from a Looker instance.",
			},
			"dev_mode": map[string]any{
				"kind":        "looker-dev-mode",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"get_projects": map[string]any{
				"kind":        "looker-get-projects",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"get_project_files": map[string]any{
				"kind":        "looker-get-project-files",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"get_project_file": map[string]any{
				"kind":        "looker-get-project-file",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"create_project_file": map[string]any{
				"kind":        "looker-create-project-file",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"update_project_file": map[string]any{
				"kind":        "looker-update-project-file",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"delete_project_file": map[string]any{
				"kind":        "looker-delete-project-file",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"generate_embed_url": map[string]any{
				"kind":        "looker-generate-embed-url",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"get_connections": map[string]any{
				"kind":        "looker-get-connections",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"get_connection_schemas": map[string]any{
				"kind":        "looker-get-connection-schemas",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"get_connection_databases": map[string]any{
				"kind":        "looker-get-connection-databases",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"get_connection_tables": map[string]any{
				"kind":        "looker-get-connection-tables",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
			"get_connection_table_columns": map[string]any{
				"kind":        "looker-get-connection-table-columns",
				"source":      "my-instance",
				"description": "Simple tool to test end to end functionality.",
			},
		},
	}

	cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
	if err != nil {
		t.Fatalf("command initialization returned an error: %s", err)
	}
	defer cleanup()

	waitCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
	defer cancel()
	out, err := testutils.WaitForString(waitCtx, regexp.MustCompile(`Server ready to serve`), cmd.Out)
	if err != nil {
		t.Logf("toolbox command logs: \n%s", out)
		t.Fatalf("toolbox didn't start successfully: %s", err)
	}

	tests.RunToolGetTestByName(t, "get_models",
		map[string]any{
			"get_models": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters":   []any{},
			},
		},
	)
	tests.RunToolGetTestByName(t, "get_explores",
		map[string]any{
			"get_explores": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The model containing the explores.",
						"name":        "model",
						"required":    true,
						"type":        "string",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "get_dimensions",
		map[string]any{
			"get_dimensions": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The model containing the explore.",
						"name":        "model",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The explore containing the fields.",
						"name":        "explore",
						"required":    true,
						"type":        "string",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "get_measures",
		map[string]any{
			"get_measures": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The model containing the explore.",
						"name":        "model",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The explore containing the fields.",
						"name":        "explore",
						"required":    true,
						"type":        "string",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "get_parameters",
		map[string]any{
			"get_parameters": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The model containing the explore.",
						"name":        "model",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The explore containing the fields.",
						"name":        "explore",
						"required":    true,
						"type":        "string",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "get_filters",
		map[string]any{
			"get_filters": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The model containing the explore.",
						"name":        "model",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The explore containing the fields.",
						"name":        "explore",
						"required":    true,
						"type":        "string",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "query",
		map[string]any{
			"query": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The model containing the explore.",
						"name":        "model",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The explore to be queried.",
						"name":        "explore",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The fields to be retrieved.",
						"items": map[string]any{
							"authSources": []any{},
							"description": "A field to be returned in the query",
							"name":        "field",
							"required":    true,
							"type":        "string",
						},
						"name":     "fields",
						"required": true,
						"type":     "array",
					},
					map[string]any{
						"additionalProperties": true,
						"authSources":          []any{},
						"description":          "The filters for the query",
						"name":                 "filters",
						"required":             false,
						"type":                 "object",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The query pivots (must be included in fields as well).",
						"items": map[string]any{
							"authSources": []any{},
							"description": "A field to be used as a pivot in the query",
							"name":        "pivot_field",
							"required":    false,
							"type":        "string",
						},
						"name":     "pivots",
						"required": false,
						"type":     "array",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The sorts like \"field.id desc 0\".",
						"items": map[string]any{
							"authSources": []any{},
							"description": "A field to be used as a sort in the query",
							"name":        "sort_field",
							"required":    false,
							"type":        "string",
						},
						"name":     "sorts",
						"required": false,
						"type":     "array",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The row limit.",
						"name":        "limit",
						"required":    false,
						"type":        "integer",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The query timezone.",
						"name":        "tz",
						"required":    false,
						"type":        "string",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "query_sql",
		map[string]any{
			"query_sql": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The model containing the explore.",
						"name":        "model",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The explore to be queried.",
						"name":        "explore",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The fields to be retrieved.",
						"items": map[string]any{
							"authSources": []any{},
							"description": "A field to be returned in the query",
							"name":        "field",
							"required":    true,
							"type":        "string",
						},
						"name":     "fields",
						"required": true,
						"type":     "array",
					},
					map[string]any{
						"additionalProperties": true,
						"authSources":          []any{},
						"description":          "The filters for the query",
						"name":                 "filters",
						"required":             false,
						"type":                 "object",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The query pivots (must be included in fields as well).",
						"items": map[string]any{
							"authSources": []any{},
							"description": "A field to be used as a pivot in the query",
							"name":        "pivot_field",
							"required":    false,
							"type":        "string",
						},
						"name":     "pivots",
						"required": false,
						"type":     "array",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The sorts like \"field.id desc 0\".",
						"items": map[string]any{
							"authSources": []any{},
							"description": "A field to be used as a sort in the query",
							"name":        "sort_field",
							"required":    false,
							"type":        "string",
						},
						"name":     "sorts",
						"required": false,
						"type":     "array",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The row limit.",
						"name":        "limit",
						"required":    false,
						"type":        "integer",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The query timezone.",
						"name":        "tz",
						"required":    false,
						"type":        "string",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "query_url",
		map[string]any{
			"query_url": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The model containing the explore.",
						"name":        "model",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The explore to be queried.",
						"name":        "explore",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The fields to be retrieved.",
						"items": map[string]any{
							"authSources": []any{},
							"description": "A field to be returned in the query",
							"name":        "field",
							"required":    true,
							"type":        "string",
						},
						"name":     "fields",
						"required": true,
						"type":     "array",
					},
					map[string]any{
						"additionalProperties": true,
						"authSources":          []any{},
						"description":          "The filters for the query",
						"name":                 "filters",
						"required":             false,
						"type":                 "object",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The query pivots (must be included in fields as well).",
						"items": map[string]any{
							"authSources": []any{},
							"description": "A field to be used as a pivot in the query",
							"name":        "pivot_field",
							"required":    false,
							"type":        "string",
						},
						"name":     "pivots",
						"required": false,
						"type":     "array",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The sorts like \"field.id desc 0\".",
						"items": map[string]any{
							"authSources": []any{},
							"description": "A field to be used as a sort in the query",
							"name":        "sort_field",
							"required":    false,
							"type":        "string",
						},
						"name":     "sorts",
						"required": false,
						"type":     "array",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The row limit.",
						"name":        "limit",
						"required":    false,
						"type":        "integer",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The query timezone.",
						"name":        "tz",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"additionalProperties": true,
						"authSources":          []any{},
						"description":          "The visualization config for the query",
						"name":                 "vis_config",
						"required":             false,
						"type":                 "object",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "get_looks",
		map[string]any{
			"get_looks": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The title of the look.",
						"name":        "title",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The description of the look.",
						"name":        "desc",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The number of looks to fetch. Default 100",
						"name":        "limit",
						"required":    false,
						"type":        "integer",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The number of looks to skip before fetching. Default 0",
						"name":        "offset",
						"required":    false,
						"type":        "integer",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "make_look",
		map[string]any{
			"make_look": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The model containing the explore.",
						"name":        "model",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The explore to be queried.",
						"name":        "explore",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The fields to be retrieved.",
						"items": map[string]any{
							"authSources": []any{},
							"description": "A field to be returned in the query",
							"name":        "field",
							"required":    true,
							"type":        "string",
						},
						"name":     "fields",
						"required": true,
						"type":     "array",
					},
					map[string]any{
						"additionalProperties": true,
						"authSources":          []any{},
						"description":          "The filters for the query",
						"name":                 "filters",
						"required":             false,
						"type":                 "object",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The query pivots (must be included in fields as well).",
						"items": map[string]any{
							"authSources": []any{},
							"description": "A field to be used as a pivot in the query",
							"name":        "pivot_field",
							"required":    false,
							"type":        "string",
						},
						"name":     "pivots",
						"required": false,
						"type":     "array",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The sorts like \"field.id desc 0\".",
						"items": map[string]any{
							"authSources": []any{},
							"description": "A field to be used as a sort in the query",
							"name":        "sort_field",
							"required":    false,
							"type":        "string",
						},
						"name":     "sorts",
						"required": false,
						"type":     "array",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The row limit.",
						"name":        "limit",
						"required":    false,
						"type":        "integer",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The query timezone.",
						"name":        "tz",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The title of the Look",
						"name":        "title",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The description of the Look",
						"name":        "description",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The folder id where the Look will be created. Leave blank to use the user's personal folder",
						"name":        "folder",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"additionalProperties": true,
						"authSources":          []any{},
						"description":          "The visualization config for the query",
						"name":                 "vis_config",
						"required":             false,
						"type":                 "object",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "get_dashboards",
		map[string]any{
			"get_dashboards": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The title of the dashboard.",
						"name":        "title",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The description of the dashboard.",
						"name":        "desc",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The number of dashboards to fetch. Default 100",
						"name":        "limit",
						"required":    false,
						"type":        "integer",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The number of dashboards to skip before fetching. Default 0",
						"name":        "offset",
						"required":    false,
						"type":        "integer",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "make_dashboard",
		map[string]any{
			"make_dashboard": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The title of the Dashboard",
						"name":        "title",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The description of the Dashboard",
						"name":        "description",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The folder id where the Dashboard will be created. Leave blank to use the user's personal folder",
						"name":        "folder",
						"required":    false,
						"type":        "string",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "add_dashboard_filter",
		map[string]any{
			"add_dashboard_filter": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The id of the dashboard where this filter will exist",
						"name":        "dashboard_id",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The name of the Dashboard Filter",
						"name":        "name",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The title of the Dashboard Filter",
						"name":        "title",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The filter_type of the Dashboard Filter: date_filter, number_filter, string_filter, field_filter (default field_filter)",
						"name":        "filter_type",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The default_value of the Dashboard Filter (optional)",
						"name":        "default_value",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The model of a field type Dashboard Filter (required if type field)",
						"name":        "model",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The explore of a field type Dashboard Filter (required if type field)",
						"name":        "explore",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The dimension of a field type Dashboard Filter (required if type field)",
						"name":        "dimension",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The Dashboard Filter should allow multiple values (default true)",
						"name":        "allow_multiple_values",
						"required":    false,
						"type":        "boolean",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The Dashboard Filter is required to run dashboard (default false)",
						"name":        "required",
						"required":    false,
						"type":        "boolean",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "add_dashboard_element",
		map[string]any{
			"add_dashboard_element": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The model containing the explore.",
						"name":        "model",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The explore to be queried.",
						"name":        "explore",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The fields to be retrieved.",
						"items": map[string]any{
							"authSources": []any{},
							"description": "A field to be returned in the query",
							"name":        "field",
							"required":    true,
							"type":        "string",
						},
						"name":     "fields",
						"required": true,
						"type":     "array",
					},
					map[string]any{
						"additionalProperties": true,
						"authSources":          []any{},
						"description":          "The filters for the query",
						"name":                 "filters",
						"required":             false,
						"type":                 "object",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The query pivots (must be included in fields as well).",
						"items": map[string]any{
							"authSources": []any{},
							"description": "A field to be used as a pivot in the query",
							"name":        "pivot_field",
							"required":    false,
							"type":        "string",
						},
						"name":     "pivots",
						"required": false,
						"type":     "array",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The sorts like \"field.id desc 0\".",
						"items": map[string]any{
							"authSources": []any{},
							"description": "A field to be used as a sort in the query",
							"name":        "sort_field",
							"required":    false,
							"type":        "string",
						},
						"name":     "sorts",
						"required": false,
						"type":     "array",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The row limit.",
						"name":        "limit",
						"required":    false,
						"type":        "integer",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The query timezone.",
						"name":        "tz",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The id of the dashboard where this tile will exist",
						"name":        "dashboard_id",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The title of the Dashboard Element",
						"name":        "title",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"additionalProperties": true,
						"authSources":          []any{},
						"description":          "The visualization config for the query",
						"name":                 "vis_config",
						"required":             false,
						"type":                 "object",
					},
					map[string]any{
						"authSources": []any{},
						"description": `An array of dashboard filters like [{"dashboard_filter_name": "name", "field": "view_name.field_name"}, ...]`,
						"items": map[string]any{
							"additionalProperties": true,
							"authSources":          []any{},
							"description":          `A dashboard filter like {"dashboard_filter_name": "name", "field": "view_name.field_name"}`,
							"name":                 "dashboard_filter",
							"required":             false,
							"type":                 "object",
						},
						"name":     "dashboard_filters",
						"required": false,
						"type":     "array",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "conversational_analytics",
		map[string]any{
			"conversational_analytics": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The user's question, potentially including conversation history and system instructions for context.",
						"name":        "user_query_with_context",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "An Array of at least one and up to 5 explore references like [{'model': 'MODEL_NAME', 'explore': 'EXPLORE_NAME'}]",
						"items": map[string]any{
							"additionalProperties": true,
							"authSources":          []any{},
							"name":                 "explore_reference",
							"description":          "An explore reference like {'model': 'MODEL_NAME', 'explore': 'EXPLORE_NAME'}",
							"required":             true,
							"type":                 "object",
						},
						"name":     "explore_references",
						"required": true,
						"type":     "array",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "health_pulse",
		map[string]any{
			"health_pulse": map[string]any{
				"description":  "Checks the health of a Looker instance by running a series of checks on the system.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The health check to run. Can be either: `check_db_connections`, `check_dashboard_performance`,`check_dashboard_errors`,`check_explore_performance`,`check_schedule_failures`, or `check_legacy_features`",
						"name":        "action",
						"required":    true,
						"type":        "string",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "health_analyze",
		map[string]any{
			"health_analyze": map[string]any{
				"description":  "Provides analysis of a Looker instance's projects, models, or explores.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The analysis to run. Can be 'projects', 'models', or 'explores'.",
						"name":        "action",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The Looker project to analyze (optional).",
						"name":        "project",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The Looker model to analyze (optional).",
						"name":        "model",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The Looker explore to analyze (optional).",
						"name":        "explore",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The timeframe in days to analyze.",
						"name":        "timeframe",
						"required":    false,
						"type":        "integer",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The minimum number of queries for a model or explore to be considered used.",
						"name":        "min_queries",
						"required":    false,
						"type":        "integer",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "health_vacuum",
		map[string]any{
			"health_vacuum": map[string]any{
				"description":  "Vacuums unused content from a Looker instance.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The vacuum action to run. Can be 'models', or 'explores'.",
						"name":        "action",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The Looker project to vacuum (optional).",
						"name":        "project",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The Looker model to vacuum (optional).",
						"name":        "model",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The Looker explore to vacuum (optional).",
						"name":        "explore",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The timeframe in days to analyze.",
						"name":        "timeframe",
						"required":    false,
						"type":        "integer",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The minimum number of queries for a model or explore to be considered used.",
						"name":        "min_queries",
						"required":    false,
						"type":        "integer",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "dev_mode",
		map[string]any{
			"dev_mode": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "Whether to set Dev Mode.",
						"name":        "devMode",
						"required":    false,
						"type":        "boolean",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "get_projects",
		map[string]any{
			"get_projects": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters":   []any{},
			},
		},
	)
	tests.RunToolGetTestByName(t, "get_project_files",
		map[string]any{
			"get_project_files": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The id of the project containing the files",
						"name":        "project_id",
						"required":    true,
						"type":        "string",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "get_project_file",
		map[string]any{
			"get_project_file": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The id of the project containing the files",
						"name":        "project_id",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The path of the file within the project",
						"name":        "file_path",
						"required":    true,
						"type":        "string",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "create_project_file",
		map[string]any{
			"create_project_file": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The id of the project containing the files",
						"name":        "project_id",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The path of the file within the project",
						"name":        "file_path",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The content of the file",
						"name":        "file_content",
						"required":    true,
						"type":        "string",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "update_project_file",
		map[string]any{
			"update_project_file": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The id of the project containing the files",
						"name":        "project_id",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The path of the file within the project",
						"name":        "file_path",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The content of the file",
						"name":        "file_content",
						"required":    true,
						"type":        "string",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "delete_project_file",
		map[string]any{
			"delete_project_file": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The id of the project containing the files",
						"name":        "project_id",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The path of the file within the project",
						"name":        "file_path",
						"required":    true,
						"type":        "string",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "generate_embed_url",
		map[string]any{
			"generate_embed_url": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "Type of Looker content to embed (ie. dashboards, looks, query-visualization)",
						"name":        "type",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The ID of the content to embed.",
						"name":        "id",
						"required":    false,
						"type":        "string",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "get_connections",
		map[string]any{
			"get_connections": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters":   []any{},
			},
		},
	)
	tests.RunToolGetTestByName(t, "get_connection_schemas",
		map[string]any{
			"get_connection_schemas": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The connection containing the schemas.",
						"name":        "conn",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The optional database to search",
						"name":        "db",
						"required":    false,
						"type":        "string",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "get_connection_databases",
		map[string]any{
			"get_connection_databases": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The connection containing the databases.",
						"name":        "conn",
						"required":    true,
						"type":        "string",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "get_connection_tables",
		map[string]any{
			"get_connection_tables": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The connection containing the tables.",
						"name":        "conn",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The optional database to search",
						"name":        "db",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The schema containing the tables.",
						"name":        "schema",
						"required":    true,
						"type":        "string",
					},
				},
			},
		},
	)
	tests.RunToolGetTestByName(t, "get_connection_table_columns",
		map[string]any{
			"get_connection_table_columns": map[string]any{
				"description":  "Simple tool to test end to end functionality.",
				"authRequired": []any{},
				"parameters": []any{
					map[string]any{
						"authSources": []any{},
						"description": "The connection containing the tables.",
						"name":        "conn",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The optional database to search",
						"name":        "db",
						"required":    false,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "The schema containing the tables.",
						"name":        "schema",
						"required":    true,
						"type":        "string",
					},
					map[string]any{
						"authSources": []any{},
						"description": "A comma separated list of tables containing the columns.",
						"name":        "tables",
						"required":    true,
						"type":        "string",
					},
				},
			},
		},
	)

	wantResult := "{\"connections\":[],\"label\":\"System Activity\",\"name\":\"system__activity\",\"project_name\":\"system__activity\"}"
	tests.RunToolInvokeSimpleTest(t, "get_models", wantResult)

	wantResult = "{\"description\":\"Data about Look and dashboard usage, including frequency of views, favoriting, scheduling, embedding, and access via the API. Also includes details about individual Looks and dashboards.\",\"group_label\":\"System Activity\",\"label\":\"Content Usage\",\"name\":\"content_usage\"}"
	tests.RunToolInvokeParametersTest(t, "get_explores", []byte(`{"model": "system__activity"}`), wantResult)

	wantResult = "{\"description\":\"Number of times this content has been viewed via the Looker API\",\"label\":\"Content Usage API Count\",\"label_short\":\"API Count\",\"name\":\"content_usage.api_count\",\"type\":\"number\"}"
	tests.RunToolInvokeParametersTest(t, "get_dimensions", []byte(`{"model": "system__activity", "explore": "content_usage"}`), wantResult)

	wantResult = "{\"description\":\"The total number of views via the Looker API\",\"label\":\"Content Usage API Total\",\"label_short\":\"API Total\",\"name\":\"content_usage.api_total\",\"type\":\"sum\"}"
	tests.RunToolInvokeParametersTest(t, "get_measures", []byte(`{"model": "system__activity", "explore": "content_usage"}`), wantResult)

	wantResult = "[]"
	tests.RunToolInvokeParametersTest(t, "get_filters", []byte(`{"model": "system__activity", "explore": "content_usage"}`), wantResult)

	wantResult = "[]"
	tests.RunToolInvokeParametersTest(t, "get_parameters", []byte(`{"model": "system__activity", "explore": "content_usage"}`), wantResult)

	wantResult = "{\"look.count\":"
	tests.RunToolInvokeParametersTest(t, "query", []byte(`{"model": "system__activity", "explore": "look", "fields": ["look.count"]}`), wantResult)

	wantResult = "SELECT"
	tests.RunToolInvokeParametersTest(t, "query_sql", []byte(`{"model": "system__activity", "explore": "look", "fields": ["look.count"]}`), wantResult)

	wantResult = "system__activity"
	tests.RunToolInvokeParametersTest(t, "query_url", []byte(`{"model": "system__activity", "explore": "look", "fields": ["look.count"]}`), wantResult)

	// A system that is just being used for testing has no looks or dashboards
	wantResult = "null"
	tests.RunToolInvokeParametersTest(t, "get_looks", []byte(`{"title": "FOO", "desc": "BAR"}`), wantResult)

	wantResult = "null"
	tests.RunToolInvokeParametersTest(t, "get_dashboards", []byte(`{"title": "FOO", "desc": "BAR"}`), wantResult)

	wantResult = "\"Connection\":\"thelook\""
	tests.RunToolInvokeParametersTest(t, "health_pulse", []byte(`{"action": "check_db_connections"}`), wantResult)

	wantResult = "[]"
	tests.RunToolInvokeParametersTest(t, "health_pulse", []byte(`{"action": "check_schedule_failures"}`), wantResult)

	wantResult = "[{\"Feature\":\"Unsupported in Looker (Google Cloud core)\"}]"
	tests.RunToolInvokeParametersTest(t, "health_pulse", []byte(`{"action": "check_legacy_features"}`), wantResult)

	wantResult = "\"Project\":\"the_look\""
	tests.RunToolInvokeParametersTest(t, "health_analyze", []byte(`{"action": "projects"}`), wantResult)

	wantResult = "\"Model\":\"the_look\""
	tests.RunToolInvokeParametersTest(t, "health_analyze", []byte(`{"action": "explores", "project": "the_look", "model": "the_look", "explore": "inventory_items"}`), wantResult)

	wantResult = "\"Model\":\"the_look\""
	tests.RunToolInvokeParametersTest(t, "health_vacuum", []byte(`{"action": "models"}`), wantResult)

	wantResult = "the_look"
	tests.RunToolInvokeSimpleTest(t, "get_projects", wantResult)

	wantResult = "order_items.view"
	tests.RunToolInvokeParametersTest(t, "get_project_files", []byte(`{"project_id": "the_look"}`), wantResult)

	wantResult = "view"
	tests.RunToolInvokeParametersTest(t, "get_project_file", []byte(`{"project_id": "the_look", "file_path": "order_items.view.lkml"}`), wantResult)

	wantResult = "dev"
	tests.RunToolInvokeParametersTest(t, "dev_mode", []byte(`{"devMode": true}`), wantResult)

	wantResult = "created"
	tests.RunToolInvokeParametersTest(t, "create_project_file", []byte(`{"project_id": "the_look", "file_path": "foo.view.lkml", "file_content": "view"}`), wantResult)

	wantResult = "updated"
	tests.RunToolInvokeParametersTest(t, "update_project_file", []byte(`{"project_id": "the_look", "file_path": "foo.view.lkml", "file_content": "model"}`), wantResult)

	wantResult = "deleted"
	tests.RunToolInvokeParametersTest(t, "delete_project_file", []byte(`{"project_id": "the_look", "file_path": "foo.view.lkml"}`), wantResult)

	wantResult = "production"
	tests.RunToolInvokeParametersTest(t, "dev_mode", []byte(`{"devMode": false}`), wantResult)

	wantResult = "thelook"
	tests.RunToolInvokeSimpleTest(t, "get_connections", wantResult)

	wantResult = "{\"name\":\"demo_db\",\"is_default\":true}"
	tests.RunToolInvokeParametersTest(t, "get_connection_schemas", []byte(`{"conn": "thelook"}`), wantResult)

	wantResult = "[]"
	tests.RunToolInvokeParametersTest(t, "get_connection_databases", []byte(`{"conn": "thelook"}`), wantResult)

	wantResult = "Employees"
	tests.RunToolInvokeParametersTest(t, "get_connection_tables", []byte(`{"conn": "thelook", "schema": "demo_db"}`), wantResult)

	wantResult = "{\"column_name\":\"EmpID\",\"data_type_database\":\"int\",\"data_type_looker\":\"number\",\"sql_escaped_column_name\":\"EmpID\"}"
	tests.RunToolInvokeParametersTest(t, "get_connection_table_columns", []byte(`{"conn": "thelook", "schema": "demo_db", "tables": "Employees"}`), wantResult)

	wantResult = "/login/embed?t=" // testing for specific substring, since url is dynamic
	tests.RunToolInvokeParametersTest(t, "generate_embed_url", []byte(`{"type": "dashboards", "id": "1"}`), wantResult)

	runConversationalAnalytics(t, "system__activity", "content_usage")

	deleteLook := testMakeLook(t)
	defer deleteLook()

	dashboardId, deleteDashboard := testMakeDashboard(t)
	defer deleteDashboard()
	testAddDashboardFilter(t, dashboardId)
	testAddDashboardElement(t, dashboardId)
}

func runConversationalAnalytics(t *testing.T, modelName, exploreName string) {
	exploreRefsJSON := fmt.Sprintf(`[{"model":"%s","explore":"%s"}]`, modelName, exploreName)

	var refs []map[string]any
	if err := json.Unmarshal([]byte(exploreRefsJSON), &refs); err != nil {
		t.Fatalf("failed to unmarshal explore refs: %v", err)
	}

	testCases := []struct {
		name           string
		exploreRefs    []map[string]any
		wantStatusCode int
		wantInResult   string
		wantInError    string
	}{
		{
			name:           "invoke conversational analytics with explore",
			exploreRefs:    refs,
			wantStatusCode: http.StatusOK,
			wantInResult:   `Answer`,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			requestBodyMap := map[string]any{
				"user_query_with_context": "What is in the explore?",
				"explore_references":      tc.exploreRefs,
			}
			bodyBytes, err := json.Marshal(requestBodyMap)
			if err != nil {
				t.Fatalf("failed to marshal request body: %v", err)
			}
			url := "http://127.0.0.1:5000/api/tool/conversational_analytics/invoke"
			resp, bodyBytes := tests.RunRequest(t, http.MethodPost, url, bytes.NewBuffer(bodyBytes), nil)

			if resp.StatusCode != tc.wantStatusCode {
				t.Fatalf("unexpected status code: got %d, want %d. Body: %s", resp.StatusCode, tc.wantStatusCode, string(bodyBytes))
			}

			if tc.wantInResult != "" {
				var respBody map[string]interface{}
				if err := json.Unmarshal(bodyBytes, &respBody); err != nil {
					t.Fatalf("error parsing response body: %v", err)
				}
				got, ok := respBody["result"].(string)
				if !ok {
					t.Fatalf("unable to find result in response body")
				}
				if !strings.Contains(got, tc.wantInResult) {
					t.Errorf("unexpected result: got %q, want to contain %q", got, tc.wantInResult)
				}
			}

			if tc.wantInError != "" {
				if !strings.Contains(string(bodyBytes), tc.wantInError) {
					t.Errorf("unexpected error message: got %q, want to contain %q", string(bodyBytes), tc.wantInError)
				}
			}
		})
	}
}

func newLookerTestSDK(t *testing.T) *v4.LookerSDK {
	t.Helper()
	cfg := rtl.ApiSettings{
		BaseUrl:      LookerBaseUrl,
		ApiVersion:   "4.0",
		VerifySsl:    LookerVerifySsl == "true",
		Timeout:      120,
		ClientId:     LookerClientId,
		ClientSecret: LookerClientSecret,
	}
	return v4.NewLookerSDK(rtl.NewAuthSession(cfg))
}

func testMakeLook(t *testing.T) func() {
	var id string
	t.Run("TestMakeLook", func(t *testing.T) {
		reqBody := []byte(`{"model": "system__activity", "explore": "look", "fields": ["look.count"], "title": "TestLook"}`)

		url := "http://127.0.0.1:5000/api/tool/make_look/invoke"
		resp, bodyBytes := tests.RunRequest(t, http.MethodPost, url, bytes.NewBuffer(reqBody), nil)

		if resp.StatusCode != 200 {
			t.Fatalf("unexpected status code: got %d, want %d. Body: %s", resp.StatusCode, 200, string(bodyBytes))
		}

		var respBody map[string]interface{}
		if err := json.Unmarshal(bodyBytes, &respBody); err != nil {
			t.Fatalf("error parsing response body: %v", err)
		}

		result := respBody["result"].(string)
		if err := json.Unmarshal([]byte(result), &respBody); err != nil {
			t.Fatalf("error parsing result body: %v", err)
		}

		var ok bool
		if id, ok = respBody["id"].(string); !ok || id == "" {
			t.Fatalf("didn't get TestLook id, got %s", string(bodyBytes))
		}
	})

	return func() {
		sdk := newLookerTestSDK(t)

		if _, err := sdk.DeleteLook(id, nil); err != nil {
			t.Fatalf("error deleting look: %v", err)
		}
		t.Logf("deleted Look %s", id)
	}
}

func testAddDashboardFilter(t *testing.T, dashboardId string) {
	t.Run("TestAddDashboardFilter", func(t *testing.T) {
		reqBody := []byte(fmt.Sprintf(`{"dashboard_id": "%s", "model": "system__activity", "explore": "look", "dimension": "look.created_year", "name": "test_filter", "title": "TestDashboardFilter"}`, dashboardId))

		url := "http://127.0.0.1:5000/api/tool/add_dashboard_filter/invoke"
		resp, bodyBytes := tests.RunRequest(t, http.MethodPost, url, bytes.NewBuffer(reqBody), nil)

		if resp.StatusCode != 200 {
			t.Fatalf("unexpected status code: got %d, want %d. Body: %s", resp.StatusCode, 200, string(bodyBytes))
		}

		t.Logf("got %s", string(bodyBytes))
	})
}

func testAddDashboardElement(t *testing.T, dashboardId string) {
	t.Run("TestAddDashboardElement", func(t *testing.T) {
		reqBody := []byte(fmt.Sprintf(`{"dashboard_id": "%s", "model": "system__activity", "explore": "look", "fields": ["look.count"], "title": "TestDashboardElement"}`, dashboardId))

		url := "http://127.0.0.1:5000/api/tool/add_dashboard_element/invoke"
		resp, bodyBytes := tests.RunRequest(t, http.MethodPost, url, bytes.NewBuffer(reqBody), nil)

		if resp.StatusCode != 200 {
			t.Fatalf("unexpected status code: got %d, want %d. Body: %s", resp.StatusCode, 200, string(bodyBytes))
		}

		t.Logf("got %s", string(bodyBytes))
	})
}

func testMakeDashboard(t *testing.T) (string, func()) {
	var id string
	t.Run("TestMakeDashboard", func(t *testing.T) {
		reqBody := []byte(`{"title": "TestDashboard"}`)

		url := "http://127.0.0.1:5000/api/tool/make_dashboard/invoke"
		resp, bodyBytes := tests.RunRequest(t, http.MethodPost, url, bytes.NewBuffer(reqBody), nil)

		if resp.StatusCode != 200 {
			t.Fatalf("unexpected status code: got %d, want %d. Body: %s", resp.StatusCode, 200, string(bodyBytes))
		}

		var respBody map[string]interface{}
		if err := json.Unmarshal(bodyBytes, &respBody); err != nil {
			t.Fatalf("error parsing response body: %v", err)
		}

		result := respBody["result"].(string)
		if err := json.Unmarshal([]byte(result), &respBody); err != nil {
			t.Fatalf("error parsing result body: %v", err)
		}

		var ok bool
		if id, ok = respBody["id"].(string); !ok || id == "" {
			t.Fatalf("didn't get TestDashboard id, got %s", string(bodyBytes))
		}
	})

	return id, func() {
		sdk := newLookerTestSDK(t)

		if _, err := sdk.DeleteDashboard(id, nil); err != nil {
			t.Fatalf("error deleting dashboard: %v", err)
		}
		t.Logf("deleted Dashboard %s", id)
	}
}
