package emitter

import (
	"strings"
	"testing"
)

func TestDriftPathUnit_RenderUnits(t *testing.T) {
	specs := []DriftPathSpec{
		{Pattern: "/etc/traefik/dynamic/orca.yml", SystemdPathUnit: true},
		{Pattern: "/etc/nftables.d/orca.nft", SystemdPathUnit: true},
	}
	files, err := (DriftPathUnit{}).RenderDriftUnits(specs)
	if err != nil {
		t.Fatalf("RenderDriftUnits: %v", err)
	}
	if len(files) != 4 {
		t.Fatalf("got %d files, want 4 (2 .path + 2 .service)", len(files))
	}
	var pathUnits, svcUnits int
	for _, f := range files {
		if strings.HasSuffix(f.Path, ".path") {
			pathUnits++
		}
		if strings.HasSuffix(f.Path, ".service") {
			svcUnits++
		}
	}
	if pathUnits != 2 || svcUnits != 2 {
		t.Errorf("path=%d service=%d, want 2 and 2", pathUnits, svcUnits)
	}
}

func TestDriftPathUnit_PathUnitContent(t *testing.T) {
	files, err := (DriftPathUnit{}).RenderDriftUnits([]DriftPathSpec{
		{Pattern: "/etc/traefik/dynamic/orca.yml", SystemdPathUnit: true},
	})
	if err != nil {
		t.Fatalf("Render: %v", err)
	}
	var pathFile *File
	for i := range files {
		if strings.HasSuffix(files[i].Path, ".path") {
			pathFile = &files[i]
		}
	}
	if pathFile == nil {
		t.Fatal("no .path unit emitted")
	}
	c := pathFile.Content
	for _, want := range []string{
		"PathChanged=/etc/traefik/dynamic/orca.yml",
		"RateLimitIntervalSec=1s",
		"RateLimitBurst=5",
		"WantedBy=multi-user.target",
	} {
		if !strings.Contains(c, want) {
			t.Errorf("path unit missing %q:\n%s", want, c)
		}
	}
}

func TestDriftPathUnit_ServiceUnitSecurity(t *testing.T) {
	files, err := (DriftPathUnit{}).RenderDriftUnits([]DriftPathSpec{
		{Pattern: "/etc/nftables.d/orca.nft", SystemdPathUnit: true},
	})
	if err != nil {
		t.Fatalf("Render: %v", err)
	}
	var svcFile *File
	for i := range files {
		if strings.HasSuffix(files[i].Path, ".service") {
			svcFile = &files[i]
		}
	}
	if svcFile == nil {
		t.Fatal("no .service unit emitted")
	}
	c := svcFile.Content
	for _, want := range []string{
		"Type=oneshot",
		"ExecStart=/usr/local/bin/orca-drift-notify.sh %f",
		"User=orca",
		"Group=orca",
		"NoNewPrivileges=yes",
		"ProtectSystem=strict",
		"ReadWritePaths=/etc/orca/state/drift-events",
		"ProtectHome=yes",
	} {
		if !strings.Contains(c, want) {
			t.Errorf("service unit missing %q:\n%s", want, c)
		}
	}
}

func TestDriftPathUnit_UnitNameSlug(t *testing.T) {
	cases := []struct {
		pattern string
		want    string
	}{
		{"/etc/traefik/dynamic/orca.yml", "orca-drift-etc-traefik-dynamic-orca-yml"},
		{"/etc/systemd/system/orca-alloc-*.service", "orca-drift-etc-systemd-system-orca-alloc-service"},
		{"/etc/orca/actual/*/etc/sudoers.d/orca-*", "orca-drift-etc-orca-actual-etc-sudoers-d-orca"},
	}
	for _, c := range cases {
		got := driftUnitName(c.pattern)
		if got != c.want {
			t.Errorf("driftUnitName(%q) = %q, want %q", c.pattern, got, c.want)
		}
	}
}

func TestDriftPathUnit_EmptyPatternErrors(t *testing.T) {
	_, err := (DriftPathUnit{}).RenderDriftUnits([]DriftPathSpec{{Pattern: ""}})
	if err == nil {
		t.Fatal("expected error for empty pattern")
	}
}

func TestDriftPathUnit_NoSpecsNoFiles(t *testing.T) {
	files, err := (DriftPathUnit{}).RenderDriftUnits(nil)
	if err != nil {
		t.Fatalf("RenderDriftUnits nil: %v", err)
	}
	if files != nil {
		t.Errorf("got %d files, want nil", len(files))
	}
}

func TestDriftPathUnit_DedupesSameSlug(t *testing.T) {
	// Two patterns that produce the same slug (after non-alnum collapse)
	// should dedupe to a single .path + .service pair.
	files, err := (DriftPathUnit{}).RenderDriftUnits([]DriftPathSpec{
		{Pattern: "/etc/x/a.yml", SystemdPathUnit: true},
		{Pattern: "/etc/x/a.yml", SystemdPathUnit: true},
	})
	if err != nil {
		t.Fatalf("Render: %v", err)
	}
	if len(files) != 2 {
		t.Errorf("got %d files, want 2 (deduped identical patterns)", len(files))
	}
}

func TestDriftPathUnit_ServiceAfterPath(t *testing.T) {
	files, _ := (DriftPathUnit{}).RenderDriftUnits([]DriftPathSpec{
		{Pattern: "/etc/traefik/dynamic/orca.yml", SystemdPathUnit: true},
	})
	for _, f := range files {
		if strings.HasSuffix(f.Path, ".service") {
			if !strings.Contains(f.Content, "After=orca-drift-etc-traefik-dynamic-orca-yml.path") {
				t.Errorf("service missing After=<name>.path:\n%s", f.Content)
			}
			return
		}
	}
	t.Fatal("no service file found")
}
