package workspaceapps_test

import (
	"testing"

	"github.com/google/uuid"
	"github.com/stretchr/testify/require"

	"github.com/coder/coder/v2/coderd/workspaceapps"
)

func Test_RequestValidate(t *testing.T) {
	t.Parallel()

	cases := []struct {
		name        string
		req         workspaceapps.Request
		noNormalize bool
		errContains string
	}{
		{
			name: "OK1",
			req: workspaceapps.Request{
				AccessMethod:      workspaceapps.AccessMethodPath,
				BasePath:          "/",
				UsernameOrID:      "foo",
				WorkspaceNameOrID: "bar",
				AgentNameOrID:     "baz",
				AppSlugOrPort:     "qux",
			},
		},
		{
			name: "OK2",
			req: workspaceapps.Request{
				AccessMethod:      workspaceapps.AccessMethodSubdomain,
				BasePath:          "/",
				UsernameOrID:      "foo",
				WorkspaceAndAgent: "bar.baz",
				AppSlugOrPort:     "qux",
			},
		},
		{
			name: "OK3",
			req: workspaceapps.Request{
				AccessMethod:  workspaceapps.AccessMethodTerminal,
				BasePath:      "/",
				AgentNameOrID: uuid.New().String(),
			},
		},
		{
			name: "OK4",
			req: workspaceapps.Request{
				AccessMethod:      workspaceapps.AccessMethodPath,
				BasePath:          "/",
				UsernameOrID:      "foo",
				WorkspaceNameOrID: "bar",
				AppSlugOrPort:     "baz",
			},
		},
		{
			name: "OK5",
			req: workspaceapps.Request{
				AccessMethod:      workspaceapps.AccessMethodSubdomain,
				BasePath:          "/",
				UsernameOrID:      "foo",
				WorkspaceNameOrID: "bar",
				AppSlugOrPort:     "8080",
			},
		},
		{
			name: "OK6",
			req: workspaceapps.Request{
				AccessMethod:      workspaceapps.AccessMethodSubdomain,
				BasePath:          "/",
				UsernameOrID:      "foo",
				WorkspaceNameOrID: "bar",
				AppSlugOrPort:     "8080s",
			},
		},
		{
			name: "NoAccessMethod",
			req: workspaceapps.Request{
				AccessMethod:      "",
				BasePath:          "/",
				UsernameOrID:      "foo",
				WorkspaceNameOrID: "bar",
				AgentNameOrID:     "baz",
				AppSlugOrPort:     "qux",
			},
			errContains: "invalid access method",
		},
		{
			name: "UnknownAccessMethod",
			req: workspaceapps.Request{
				AccessMethod:      "dean was here",
				BasePath:          "/",
				UsernameOrID:      "foo",
				WorkspaceNameOrID: "bar",
				AgentNameOrID:     "baz",
				AppSlugOrPort:     "qux",
			},
			errContains: "invalid access method",
		},
		{
			name: "NoBasePath",
			req: workspaceapps.Request{
				AccessMethod:      workspaceapps.AccessMethodPath,
				BasePath:          "",
				UsernameOrID:      "foo",
				WorkspaceNameOrID: "bar",
				AgentNameOrID:     "baz",
				AppSlugOrPort:     "qux",
			},
			noNormalize: true,
			errContains: "base path is required",
		},
		{
			name: "NoUsernameOrID",
			req: workspaceapps.Request{
				AccessMethod:      workspaceapps.AccessMethodPath,
				BasePath:          "/",
				UsernameOrID:      "",
				WorkspaceNameOrID: "bar",
				AgentNameOrID:     "baz",
				AppSlugOrPort:     "qux",
			},
			errContains: "username or ID is required",
		},
		{
			name: "NoMe",
			req: workspaceapps.Request{
				AccessMethod:      workspaceapps.AccessMethodPath,
				BasePath:          "/",
				UsernameOrID:      "me",
				WorkspaceNameOrID: "bar",
				AgentNameOrID:     "baz",
				AppSlugOrPort:     "qux",
			},
			errContains: `username cannot be "me"`,
		},
		{
			name: "InvalidWorkspaceAndAgent/EmptyWorkspace",
			req: workspaceapps.Request{
				AccessMethod:      workspaceapps.AccessMethodPath,
				BasePath:          "/",
				UsernameOrID:      "foo",
				WorkspaceAndAgent: ".bar",
				AppSlugOrPort:     "baz",
			},
			errContains: "workspace name or ID is required",
		},
		{
			name: "NoWorkspaceNameOrID",
			req: workspaceapps.Request{
				AccessMethod:      workspaceapps.AccessMethodPath,
				BasePath:          "/",
				UsernameOrID:      "foo",
				WorkspaceNameOrID: "",
				AgentNameOrID:     "baz",
				AppSlugOrPort:     "qux",
			},
			errContains: "workspace name or ID is required",
		},
		{
			name: "NoAppSlugOrPort",
			req: workspaceapps.Request{
				AccessMethod:      workspaceapps.AccessMethodPath,
				BasePath:          "/",
				UsernameOrID:      "foo",
				WorkspaceNameOrID: "bar",
				AgentNameOrID:     "baz",
				AppSlugOrPort:     "",
			},
			errContains: "app slug or port is required",
		},
		{
			name: "Prefix/OK",
			req: workspaceapps.Request{
				AccessMethod:      workspaceapps.AccessMethodSubdomain,
				Prefix:            "blah---",
				BasePath:          "/",
				UsernameOrID:      "foo",
				WorkspaceNameOrID: "bar",
				AgentNameOrID:     "baz",
				AppSlugOrPort:     "qux",
			},
		},
		{
			name: "Prefix/Invalid",
			req: workspaceapps.Request{
				AccessMethod:      workspaceapps.AccessMethodSubdomain,
				Prefix:            "blah", // no trailing ---
				BasePath:          "/",
				UsernameOrID:      "foo",
				WorkspaceNameOrID: "bar",
				AgentNameOrID:     "baz",
				AppSlugOrPort:     "qux",
			},
			errContains: "prefix must have a trailing '---'",
		},
		{
			name: "Prefix/NotAllowedPath",
			req: workspaceapps.Request{
				AccessMethod:      workspaceapps.AccessMethodPath,
				Prefix:            "blah---",
				BasePath:          "/",
				UsernameOrID:      "foo",
				WorkspaceNameOrID: "bar",
				AgentNameOrID:     "baz",
				AppSlugOrPort:     "qux",
			},
			errContains: "prefix is only valid for subdomain apps",
		},
		{
			name: "Terminal/OtherFields/UsernameOrID",
			req: workspaceapps.Request{
				AccessMethod:  workspaceapps.AccessMethodTerminal,
				BasePath:      "/",
				UsernameOrID:  "foo",
				AgentNameOrID: uuid.New().String(),
			},
			errContains: "cannot specify any fields other than",
		},
		{
			name: "Terminal/OtherFields/WorkspaceAndAgent",
			req: workspaceapps.Request{
				AccessMethod:      workspaceapps.AccessMethodTerminal,
				BasePath:          "/",
				WorkspaceAndAgent: "bar.baz",
				AgentNameOrID:     uuid.New().String(),
			},
			errContains: "cannot specify any fields other than",
		},
		{
			name: "Terminal/OtherFields/WorkspaceNameOrID",
			req: workspaceapps.Request{
				AccessMethod:      workspaceapps.AccessMethodTerminal,
				BasePath:          "/",
				WorkspaceNameOrID: "bar",
				AgentNameOrID:     uuid.New().String(),
			},
			errContains: "cannot specify any fields other than",
		},
		{
			name: "Terminal/OtherFields/AppSlugOrPort",
			req: workspaceapps.Request{
				AccessMethod:  workspaceapps.AccessMethodTerminal,
				BasePath:      "/",
				AgentNameOrID: uuid.New().String(),
				AppSlugOrPort: "baz",
			},
			errContains: "cannot specify any fields other than",
		},
		{
			name: "Terminal/AgentNameOrID/Empty",
			req: workspaceapps.Request{
				AccessMethod:  workspaceapps.AccessMethodTerminal,
				BasePath:      "/",
				AgentNameOrID: "",
			},
			errContains: "agent name or ID is required",
		},
		{
			name: "Terminal/AgentNameOrID/NotUUID",
			req: workspaceapps.Request{
				AccessMethod:  workspaceapps.AccessMethodTerminal,
				BasePath:      "/",
				AgentNameOrID: "baz",
			},
			errContains: `invalid agent name or ID "baz", must be a UUID`,
		},
	}

	for _, c := range cases {
		c := c
		t.Run(c.name, func(t *testing.T) {
			t.Parallel()
			req := c.req
			if !c.noNormalize {
				req = c.req.Normalize()
			}
			err := req.Validate()
			if c.errContains == "" {
				require.NoError(t, err)
			} else {
				require.Error(t, err)
				require.ErrorContains(t, err, c.errContains)
			}
		})
	}
}
