package emitter

import (
	"strings"
	"testing"

	"git.cloudinit.dev/coreci/orca/internal/jobspec"
)

func TestSystemdEmitter_LifecyclePostStart(t *testing.T) {
	// lifecycle.post_start → ExecStartPost (one line per command).
	spec := &jobspec.WorkloadSpec{
		Kind:    "Service",
		Name:    "web",
		Runtime: &jobspec.RuntimeBlock{OneOf: "process", Command: "/usr/local/bin/httpd -f"},
		Lifecycle: &jobspec.LifecycleBlock{
			PostStart: []string{"/usr/bin/sleep 1", "/usr/bin/curl localhost/healthz"},
		},
	}
	files, err := SystemdEmitter{}.Render(spec, &Node{Hostname: "n1"})
	if err != nil {
		t.Fatalf("Render: %v", err)
	}
	c := files[0].Content
	if !strings.Contains(c, "ExecStartPost=/usr/bin/sleep 1\n") {
		t.Errorf("missing ExecStartPost for sleep 1\n%s", c)
	}
	if !strings.Contains(c, "ExecStartPost=/usr/bin/curl localhost/healthz\n") {
		t.Errorf("missing ExecStartPost for curl\n%s", c)
	}
	// ExecStart must still be present.
	if !strings.Contains(c, "ExecStart=/usr/local/bin/httpd -f\n") {
		t.Errorf("missing ExecStart\n%s", c)
	}
}

func TestSystemdEmitter_LifecyclePreStop(t *testing.T) {
	// lifecycle.pre_stop → ExecStop (one line per command).
	spec := &jobspec.WorkloadSpec{
		Kind:    "Service",
		Name:    "web",
		Runtime: &jobspec.RuntimeBlock{OneOf: "process", Command: "/usr/local/bin/httpd -f"},
		Lifecycle: &jobspec.LifecycleBlock{
			PreStop: []string{"/usr/local/bin/httpd -graceful", "/usr/bin/sleep 5"},
		},
	}
	files, err := SystemdEmitter{}.Render(spec, &Node{Hostname: "n1"})
	if err != nil {
		t.Fatalf("Render: %v", err)
	}
	c := files[0].Content
	if !strings.Contains(c, "ExecStop=/usr/local/bin/httpd -graceful\n") {
		t.Errorf("missing ExecStop for graceful\n%s", c)
	}
	if !strings.Contains(c, "ExecStop=/usr/bin/sleep 5\n") {
		t.Errorf("missing ExecStop for sleep 5\n%s", c)
	}
}

func TestSystemdEmitter_LifecycleBoth(t *testing.T) {
	spec := &jobspec.WorkloadSpec{
		Kind:    "Service",
		Name:    "web",
		Runtime: &jobspec.RuntimeBlock{OneOf: "process", Command: "/bin/httpd"},
		Lifecycle: &jobspec.LifecycleBlock{
			PostStart: []string{"/bin/after-start"},
			PreStop:   []string{"/bin/before-stop"},
		},
	}
	files, err := SystemdEmitter{}.Render(spec, &Node{})
	if err != nil {
		t.Fatalf("Render: %v", err)
	}
	c := files[0].Content
	// ExecStartPost must appear before ExecStop (post_start runs after
	// start; pre_stop runs before stop — the order in the unit file
	// reflects the lifecycle order).
	startIdx := strings.Index(c, "ExecStart=")
	postIdx := strings.Index(c, "ExecStartPost=")
	stopIdx := strings.Index(c, "ExecStop=")
	if startIdx < 0 || postIdx < 0 || stopIdx < 0 {
		t.Fatalf("missing one of ExecStart/ExecStartPost/ExecStop\n%s", c)
	}
	if !(startIdx < postIdx && postIdx < stopIdx) {
		t.Errorf("expected order ExecStart < ExecStartPost < ExecStop\n%s", c)
	}
}

func TestSystemdEmitter_LifecycleNilOmitsDirectives(t *testing.T) {
	// No lifecycle block → no ExecStartPost / ExecStop lines.
	spec := &jobspec.WorkloadSpec{
		Kind:    "Job",
		Name:    "backup",
		Runtime: &jobspec.RuntimeBlock{OneOf: "process", Command: "/bin/rsync"},
	}
	files, err := SystemdEmitter{}.Render(spec, &Node{})
	if err != nil {
		t.Fatalf("Render: %v", err)
	}
	c := files[0].Content
	if strings.Contains(c, "ExecStartPost=") {
		t.Errorf("ExecStartPost should be omitted when no lifecycle\n%s", c)
	}
	if strings.Contains(c, "ExecStop=") {
		t.Errorf("ExecStop should be omitted when no lifecycle\n%s", c)
	}
}

func TestSystemdEmitter_LifecycleEmptyListsOmitted(t *testing.T) {
	// Lifecycle block present but empty lists → no ExecStartPost / ExecStop.
	spec := &jobspec.WorkloadSpec{
		Kind:      "Job",
		Name:      "x",
		Runtime:   &jobspec.RuntimeBlock{OneOf: "process", Command: "/bin/x"},
		Lifecycle: &jobspec.LifecycleBlock{},
	}
	files, err := SystemdEmitter{}.Render(spec, &Node{})
	if err != nil {
		t.Fatalf("Render: %v", err)
	}
	c := files[0].Content
	if strings.Contains(c, "ExecStartPost=") {
		t.Errorf("ExecStartPost should be omitted for empty PostStart\n%s", c)
	}
	if strings.Contains(c, "ExecStop=") {
		t.Errorf("ExecStop should be omitted for empty PreStop\n%s", c)
	}
}

func TestSystemdEmitter_LifecycleOnlyPostStart(t *testing.T) {
	spec := &jobspec.WorkloadSpec{
		Kind:    "Job",
		Name:    "x",
		Runtime: &jobspec.RuntimeBlock{OneOf: "process", Command: "/bin/x"},
		Lifecycle: &jobspec.LifecycleBlock{
			PostStart: []string{"/bin/notify-up"},
		},
	}
	files, err := SystemdEmitter{}.Render(spec, &Node{})
	if err != nil {
		t.Fatalf("Render: %v", err)
	}
	c := files[0].Content
	if !strings.Contains(c, "ExecStartPost=/bin/notify-up\n") {
		t.Errorf("missing ExecStartPost\n%s", c)
	}
	if strings.Contains(c, "ExecStop=") {
		t.Errorf("ExecStop should be omitted when only PostStart set\n%s", c)
	}
}

func TestSystemdEmitter_LifecycleOnlyPreStop(t *testing.T) {
	spec := &jobspec.WorkloadSpec{
		Kind:    "Job",
		Name:    "x",
		Runtime: &jobspec.RuntimeBlock{OneOf: "process", Command: "/bin/x"},
		Lifecycle: &jobspec.LifecycleBlock{
			PreStop: []string{"/bin/notify-down"},
		},
	}
	files, err := SystemdEmitter{}.Render(spec, &Node{})
	if err != nil {
		t.Fatalf("Render: %v", err)
	}
	c := files[0].Content
	if !strings.Contains(c, "ExecStop=/bin/notify-down\n") {
		t.Errorf("missing ExecStop\n%s", c)
	}
	if strings.Contains(c, "ExecStartPost=") {
		t.Errorf("ExecStartPost should be omitted when only PreStop set\n%s", c)
	}
}
