package cli

import (
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"testing"
	"time"

	"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
)

func TestDefaultRunIDGeneratorProducesUniqueSafeIDs(t *testing.T) {
	startedAt := time.Unix(0, 123456789).UTC()
	pattern := regexp.MustCompile(`^run-123456789-[0-9a-f]{32}$`)
	seen := make(map[string]struct{}, 256)
	for i := 0; i < 256; i++ {
		runID, err := defaultRunIDGenerator(startedAt)
		if err != nil {
			t.Fatal(err)
		}
		if !pattern.MatchString(runID) {
			t.Fatalf("run ID %q does not match production format", runID)
		}
		if err := validateRunID(runID); err != nil {
			t.Fatalf("run ID %q is not path-safe: %v", runID, err)
		}
		if _, exists := seen[runID]; exists {
			t.Fatalf("duplicate run ID %q", runID)
		}
		seen[runID] = struct{}{}
	}
}

func TestWriteOutputFilesSupportsNestedLogicalPaths(t *testing.T) {
	runPath := filepath.Join(t.TempDir(), "output", "run-safe")
	if err := writeOutputFiles(runPath, []contracts.OutputFile{{Name: "nested/result.json", Bytes: []byte("result")}}); err != nil {
		t.Fatal(err)
	}
	resultPath := filepath.Join(runPath, "nested", "result.json")
	data, err := os.ReadFile(resultPath)
	if err != nil || string(data) != "result" {
		t.Fatalf("nested output = %q, %v", data, err)
	}
	for path, want := range map[string]os.FileMode{runPath: 0o755, filepath.Join(runPath, "nested"): 0o755, resultPath: 0o644} {
		info, err := os.Stat(path)
		if err != nil {
			t.Fatal(err)
		}
		mode := info.Mode().Perm()
		if mode&^want != 0 {
			t.Fatalf("%s mode = %#o, must not be broader than %#o", path, mode, want)
		}
		if info.IsDir() && mode&0o700 != 0o700 {
			t.Fatalf("%s mode = %#o, want owner access", path, mode)
		}
		if !info.IsDir() && mode != want {
			t.Fatalf("%s mode = %#o, want %#o", path, mode, want)
		}
	}
	entries, err := os.ReadDir(filepath.Join(runPath, "nested"))
	if err != nil {
		t.Fatal(err)
	}
	for _, entry := range entries {
		if strings.Contains(entry.Name(), ".tmp-") {
			t.Fatalf("temporary file remains: %s", entry.Name())
		}
	}
}

func TestWriteOutputFilesRejectsUnsafeNamesBeforeAllocatingRunDirectory(t *testing.T) {
	outputRoot := filepath.Join(t.TempDir(), "output")
	runPath := filepath.Join(outputRoot, "run-safe")
	for _, name := range []string{"", "../outside", "/absolute", `nested\\outside`, "nested/../outside"} {
		t.Run(name, func(t *testing.T) {
			if err := writeOutputFiles(runPath, []contracts.OutputFile{{Name: "safe.json"}, {Name: name}}); err == nil {
				t.Fatalf("writeOutputFiles accepted %q", name)
			}
			if _, err := os.Stat(outputRoot); !os.IsNotExist(err) {
				t.Fatalf("output root exists or stat failed after %q: %v", name, err)
			}
		})
	}
}

func TestWriteOutputFilesRetainsNewPartialDirectoryAndPreservesSibling(t *testing.T) {
	outputRoot := filepath.Join(t.TempDir(), "output")
	siblingPath := filepath.Join(outputRoot, "sibling")
	if err := os.MkdirAll(siblingPath, 0o755); err != nil {
		t.Fatal(err)
	}
	sentinelPath := filepath.Join(siblingPath, "sentinel")
	if err := os.WriteFile(sentinelPath, []byte("preserve sibling"), 0o644); err != nil {
		t.Fatal(err)
	}

	runPath := filepath.Join(outputRoot, "run-safe")
	err := writeOutputFiles(runPath, []contracts.OutputFile{
		{Name: "blocked", Bytes: []byte("partial output")},
		{Name: "blocked/nested.json", Bytes: []byte("unreachable")},
	})
	if err == nil || !strings.Contains(err.Error(), `write output file "blocked/nested.json"`) {
		t.Fatalf("writeOutputFiles() error = %v, want later directory failure", err)
	}
	if got, err := os.ReadFile(filepath.Join(runPath, "blocked")); err != nil || string(got) != "partial output" {
		t.Fatalf("partial output = %q, %v", got, err)
	}
	if got, err := os.ReadFile(sentinelPath); err != nil || string(got) != "preserve sibling" {
		t.Fatalf("sibling sentinel = %q, %v", got, err)
	}
}
