package jobspec

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

func TestDispatch_Markdown(t *testing.T) {
	input := "---\nkind: Job\nname: md-job\n---\nbody content\n"
	ws, err := Dispatch([]byte(input), "spec.md")
	if err != nil {
		t.Fatalf("Dispatch .md: %v", err)
	}
	if ws.Kind != "Job" {
		t.Errorf("Kind = %q, want Job", ws.Kind)
	}
	if ws.Name != "md-job" {
		t.Errorf("Name = %q, want md-job", ws.Name)
	}
	if ws.Body != "body content\n" {
		t.Errorf("Body = %q, want %q (R-015)", ws.Body, "body content\n")
	}
}

func TestDispatch_YAML(t *testing.T) {
	input := "kind: Service\nname: yaml-svc\nports:\n  - name: http\n    port: 80\n"
	ws, err := Dispatch([]byte(input), "spec.yaml")
	if err != nil {
		t.Fatalf("Dispatch .yaml: %v", err)
	}
	if ws.Kind != "Service" {
		t.Errorf("Kind = %q, want Service", ws.Kind)
	}
	if ws.Name != "yaml-svc" {
		t.Errorf("Name = %q, want yaml-svc", ws.Name)
	}
	if ws.Body != "" {
		t.Errorf("Body = %q, want empty (YAML has no body)", ws.Body)
	}
	if len(ws.Ports) != 1 || ws.Ports[0].Name != "http" || ws.Ports[0].Port != 80 {
		t.Errorf("Ports = %+v, want one http:80", ws.Ports)
	}
}

func TestDispatch_YML(t *testing.T) {
	input := "kind: DaemonSet\nname: yml-ds\n"
	ws, err := Dispatch([]byte(input), "spec.yml")
	if err != nil {
		t.Fatalf("Dispatch .yml: %v", err)
	}
	if ws.Kind != "DaemonSet" {
		t.Errorf("Kind = %q, want DaemonSet", ws.Kind)
	}
	if ws.Body != "" {
		t.Errorf("Body = %q, want empty", ws.Body)
	}
}

func TestDispatch_HCLAdapter(t *testing.T) {
	hcl := `job "demo" {}
task "build" {
  command = "/bin/echo"
  args    = ["hello"]
}
`
	ws, err := Dispatch([]byte(hcl), "spec.hcl")
	if err != nil {
		t.Fatalf("Dispatch .hcl: %v", err)
	}
	if ws.Kind != "Job" {
		t.Errorf("Kind = %q, want Job (adapter always sets Job)", ws.Kind)
	}
	if ws.Name != "demo" {
		t.Errorf("Name = %q, want demo (from spec.Job.Name)", ws.Name)
	}
	if ws.Runtime == nil {
		t.Fatal("Runtime is nil; adapter should populate from tasks[0]")
	}
	if ws.Runtime.OneOf != "process" {
		t.Errorf("Runtime.OneOf = %q, want process", ws.Runtime.OneOf)
	}
	if ws.Runtime.Command != "/bin/echo" {
		t.Errorf("Runtime.Command = %q, want /bin/echo (from tasks[0].Command)", ws.Runtime.Command)
	}
	if ws.Body != "" {
		t.Errorf("Body = %q, want empty (HCL has no body)", ws.Body)
	}
}

func TestDispatch_HCLAdapterNoTasks(t *testing.T) {
	hcl := `job "x" {}`
	_, err := Dispatch([]byte(hcl), "spec.hcl")
	if err == nil {
		t.Fatal("expected error for HCL with no tasks")
	}
	if !strings.Contains(err.Error(), "at least one task") {
		t.Errorf("error = %q, want it to contain 'at least one task'", err.Error())
	}
}

func TestDispatch_UnknownExtension(t *testing.T) {
	_, err := Dispatch([]byte("kind: Job\nname: x\n"), "spec.json")
	if err == nil {
		t.Fatal("expected error for unknown extension, got nil")
	}
	if !strings.Contains(err.Error(), "unknown extension") {
		t.Errorf("error = %q, want it to contain 'unknown extension'", err.Error())
	}
}

func TestDispatch_NoExtension(t *testing.T) {
	_, err := Dispatch([]byte("kind: Job\nname: x\n"), "spec")
	if err == nil {
		t.Fatal("expected error for no extension, got nil")
	}
}

func TestDispatch_EmptyYAML(t *testing.T) {
	_, err := Dispatch([]byte(""), "spec.yaml")
	if err == nil {
		t.Fatal("expected error for empty YAML, got nil")
	}
}

func TestParseFile_Markdown(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "spec.md")
	content := "---\nkind: Job\nname: file-md\n---\nbody\n"
	if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
		t.Fatalf("write: %v", err)
	}
	ws, err := ParseFile(path)
	if err != nil {
		t.Fatalf("ParseFile .md: %v", err)
	}
	if ws.Kind != "Job" || ws.Name != "file-md" {
		t.Errorf("got Kind=%q Name=%q", ws.Kind, ws.Name)
	}
	if ws.Body != "body\n" {
		t.Errorf("Body = %q, want %q", ws.Body, "body\n")
	}
}

func TestParseFile_YAML(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "spec.yaml")
	content := "kind: Service\nname: file-yaml\n"
	if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
		t.Fatalf("write: %v", err)
	}
	ws, err := ParseFile(path)
	if err != nil {
		t.Fatalf("ParseFile .yaml: %v", err)
	}
	if ws.Kind != "Service" || ws.Name != "file-yaml" {
		t.Errorf("got Kind=%q Name=%q", ws.Kind, ws.Name)
	}
	if ws.Body != "" {
		t.Errorf("Body = %q, want empty", ws.Body)
	}
}

func TestParseFile_HCL(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "spec.hcl")
	content := `job "file-hcl" {}
task "t" { command = "/bin/true" }
`
	if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
		t.Fatalf("write: %v", err)
	}
	ws, err := ParseFile(path)
	if err != nil {
		t.Fatalf("ParseFile .hcl: %v", err)
	}
	if ws.Kind != "Job" || ws.Name != "file-hcl" {
		t.Errorf("got Kind=%q Name=%q", ws.Kind, ws.Name)
	}
	if ws.Runtime == nil || ws.Runtime.Command != "/bin/true" {
		t.Errorf("Runtime.Command = %v, want /bin/true", ws.Runtime)
	}
}

func TestParseFile_MissingFile(t *testing.T) {
	_, err := ParseFile(filepath.Join(t.TempDir(), "nope.md"))
	if err == nil {
		t.Fatal("expected error for missing file, got nil")
	}
	if !strings.Contains(err.Error(), "read spec file") {
		t.Errorf("error = %q, want it to contain 'read spec file'", err.Error())
	}
}

func TestParseFile_UnknownExtension(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "spec.txt")
	if err := os.WriteFile(path, []byte("kind: Job\nname: x\n"), 0o644); err != nil {
		t.Fatalf("write: %v", err)
	}
	_, err := ParseFile(path)
	if err == nil {
		t.Fatal("expected error for unknown extension, got nil")
	}
	if !strings.Contains(err.Error(), "unknown extension") {
		t.Errorf("error = %q, want 'unknown extension'", err.Error())
	}
}

func TestParseHCL_LegacySpec(t *testing.T) {
	hcl := `job "legacy" {}
task "t" { command = "/bin/echo" }
`
	ws, err := ParseHCL([]byte(hcl), "spec.hcl")
	if err != nil {
		t.Fatalf("ParseHCL: %v", err)
	}
	if ws.Kind != "Job" || ws.Name != "legacy" {
		t.Errorf("adapter got Kind=%q Name=%q", ws.Kind, ws.Name)
	}
}
