package zddc

import (
	"archive/zip"
	"bytes"
	"os"
	"path/filepath"
	"strings"
	"testing"
)

// TestEmbeddedDefaultsParse — the embedded per-depth default tree must assemble
// + parse cleanly into a ZddcFile. Regression guard against a broken member.
func TestEmbeddedDefaultsParse(t *testing.T) {
	zf, err := EmbeddedDefaults()
	if err != nil {
		t.Fatalf("EmbeddedDefaults: %v", err)
	}
	if zf.Title == "" {
		t.Errorf("embedded defaults have no title")
	}
}

// TestEmbeddedDefaultsZipDumpable — the .zddc.zip emitted by show-defaults must
// be a valid archive carrying the per-depth policy members with "*" wildcard
// segments (no leftover _any_ placeholder).
func TestEmbeddedDefaultsZipDumpable(t *testing.T) {
	b, err := EmbeddedDefaultsZip()
	if err != nil {
		t.Fatalf("EmbeddedDefaultsZip: %v", err)
	}
	zr, err := zip.NewReader(bytes.NewReader(b), int64(len(b)))
	if err != nil {
		t.Fatalf("not a valid zip: %v", err)
	}
	var hasRoot, hasWildcard bool
	for _, f := range zr.File {
		if strings.Contains(f.Name, AnyPlaceholder) {
			t.Errorf("member %q still has the _any_ placeholder; want * wildcard", f.Name)
		}
		switch f.Name {
		case ".zddc":
			hasRoot = true
		case "*/working/.zddc":
			hasWildcard = true
		}
	}
	if !hasRoot {
		t.Error("zip missing root .zddc member")
	}
	if !hasWildcard {
		t.Error(`zip missing "*/working/.zddc" member`)
	}
}

// TestCascadeIncludesEmbeddedByDefault — a fresh deployment with no
// on-disk .zddc still gets the embedded defaults reachable via
// chain.Embedded.
func TestCascadeIncludesEmbeddedByDefault(t *testing.T) {
	resetCache()
	root := t.TempDir()
	leaf := filepath.Join(root, "Proj")
	if err := mkdirAll(leaf); err != nil {
		t.Fatal(err)
	}
	chain, err := EffectivePolicy(root, leaf)
	if err != nil {
		t.Fatal(err)
	}
	if chain.Embedded.Title == "" {
		t.Errorf("chain.Embedded.Title empty, want defaults title to populate")
	}
}

// TestCascadeInheritFalseDropsEmbedded — when an on-disk .zddc sets
// top-level `inherit: false`, the embedded layer is zeroed out.
func TestCascadeInheritFalseDropsEmbedded(t *testing.T) {
	resetCache()
	root := t.TempDir()
	writeZddc(t, root, "title: 'op-managed'\ninherit: false\n")
	leaf := filepath.Join(root, "Proj")
	if err := mkdirAll(leaf); err != nil {
		t.Fatal(err)
	}
	chain, err := EffectivePolicy(root, leaf)
	if err != nil {
		t.Fatal(err)
	}
	if chain.Embedded.Title != "" {
		t.Errorf("chain.Embedded.Title = %q, want empty (inherit:false should drop embedded)",
			chain.Embedded.Title)
	}
	// On-disk level still present.
	if got := chain.Levels[0].Title; got != "op-managed" {
		t.Errorf("Levels[0].Title = %q, want %q", got, "op-managed")
	}
}

// TestCascadeInheritTrueExplicitKeepsEmbedded — `inherit: true`
// explicitly is the same as omitting it (default behaviour).
func TestCascadeInheritTrueExplicitKeepsEmbedded(t *testing.T) {
	resetCache()
	root := t.TempDir()
	writeZddc(t, root, "title: 'op-managed'\ninherit: true\n")
	leaf := filepath.Join(root, "Proj")
	if err := mkdirAll(leaf); err != nil {
		t.Fatal(err)
	}
	chain, err := EffectivePolicy(root, leaf)
	if err != nil {
		t.Fatal(err)
	}
	if chain.Embedded.Title == "" {
		t.Errorf("chain.Embedded.Title empty, want defaults to remain since inherit: true is the default")
	}
}

func mkdirAll(p string) error {
	return os.MkdirAll(p, 0o755)
}
