// Copyright 2025 Emiliano Spinella (eminwux)
//
// 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.
//
// SPDX-License-Identifier: Apache-2.0

package e2e_test

import (
	"os"
	"path/filepath"
	"strings"
	"testing"
)

// TestKuke_CreateSpace_RejectsInvalidNames covers the AC for #180: invalid
// space names ("_" or "/") must be rejected end-to-end with a non-zero exit
// code and a clear error message that names the offending input. Routed
// through the per-test daemon so the workload-command path matches the
// suite's default mode post-#565; the validator is wired at both
// controller and runner boundaries, so the same rejection fires either way.
func TestKuke_CreateSpace_RejectsInvalidNames(t *testing.T) {
	t.Parallel()

	runPath := getRandomRunPath(t)
	host := startKukeondDaemon(t, runPath)

	tests := []struct {
		name      string
		spaceName string
	}{
		{name: "underscore in space name", spaceName: "bad_space"},
		{name: "slash in space name", spaceName: "bad/space"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			args := append(buildKukeDaemonArgs(host),
				"create", "space", tt.spaceName, "--realm", "default")
			exitCode, _, stderr := runBinary(t, nil, kuke, args...)
			if exitCode == 0 {
				t.Fatalf("expected non-zero exit rejecting space name %q", tt.spaceName)
			}
			combined := string(stderr)
			if !strings.Contains(combined, "space") || !strings.Contains(combined, tt.spaceName) {
				t.Errorf("expected error mentioning kind=space and name %q, got:\n%s", tt.spaceName, combined)
			}
		})
	}
}

// TestKuke_CreateStack_RejectsInvalidNames covers the AC for #180: invalid
// stack names must be rejected end-to-end.
func TestKuke_CreateStack_RejectsInvalidNames(t *testing.T) {
	t.Parallel()

	runPath := getRandomRunPath(t)
	host := startKukeondDaemon(t, runPath)

	tests := []struct {
		name      string
		stackName string
	}{
		{name: "underscore in stack name", stackName: "bad_stack"},
		{name: "slash in stack name", stackName: "bad/stack"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			args := append(buildKukeDaemonArgs(host),
				"create", "stack", tt.stackName,
				"--realm", "default", "--space", "default")
			exitCode, _, stderr := runBinary(t, nil, kuke, args...)
			if exitCode == 0 {
				t.Fatalf("expected non-zero exit rejecting stack name %q", tt.stackName)
			}
			combined := string(stderr)
			if !strings.Contains(combined, "stack") || !strings.Contains(combined, tt.stackName) {
				t.Errorf("expected error mentioning kind=stack and name %q, got:\n%s", tt.stackName, combined)
			}
		})
	}
}

// TestKuke_CreateCell_RejectsInvalidNames covers the AC for #180: invalid
// cell names must be rejected end-to-end.
func TestKuke_CreateCell_RejectsInvalidNames(t *testing.T) {
	t.Parallel()

	runPath := getRandomRunPath(t)
	host := startKukeondDaemon(t, runPath)

	tests := []struct {
		name     string
		cellName string
	}{
		{name: "underscore in cell name", cellName: "bad_cell"},
		{name: "slash in cell name", cellName: "bad/cell"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			// The no-source-flag `kuke create cell` path was retired in
			// epic:bye-container step 3 (#996); exercise the cell-name
			// validation via `kuke apply -f` against a substituted cell.yaml
			// so the daemon's normalizeCellInputs check still fires.
			yamlContent := readTestdataYAML(t, "cell.yaml")
			yamlContent = strings.ReplaceAll(yamlContent, "test-cell", tt.cellName)
			yamlContent = strings.ReplaceAll(yamlContent, "test-realm", "default")
			yamlContent = strings.ReplaceAll(yamlContent, "test-space", "default")
			yamlContent = strings.ReplaceAll(yamlContent, "test-stack", "default")

			tmpDir := t.TempDir()
			tmpFile := filepath.Join(tmpDir, "cell.yaml")
			if err := os.WriteFile(tmpFile, []byte(yamlContent), 0o644); err != nil {
				t.Fatalf("failed to write temporary YAML file: %v", err)
			}

			args := append(buildKukeDaemonArgs(host), "apply", "-f", tmpFile)
			exitCode, stdout, stderr := runBinary(t, nil, kuke, args...)
			if exitCode == 0 {
				t.Fatalf("expected non-zero exit rejecting cell name %q", tt.cellName)
			}
			// `kuke apply` writes the daemon's per-resource error to stdout
			// (printApplyResult's "  Error: ..." line) and returns a generic
			// "config error: some resources failed to apply" on stderr; check
			// both streams so the assertion sees the name-validation message.
			combined := string(stdout) + string(stderr)
			if !strings.Contains(combined, "cell") || !strings.Contains(combined, tt.cellName) {
				t.Errorf("expected error mentioning kind=cell and name %q, got:\n%s", tt.cellName, combined)
			}
		})
	}
}

