package domain

import (
	"errors"
	"testing"
)

func TestBuildSpec_Validate(t *testing.T) {
	tests := []struct {
		name    string
		spec    BuildSpec
		wantErr error
	}{
		{
			name:    "valid spec with prompt",
			spec:    BuildSpec{Prompt: "Build a landing page"},
			wantErr: nil,
		},
		{
			name:    "valid spec with all fields",
			spec:    BuildSpec{Prompt: "Build it", Template: "nextjs", AutoCommit: true, AutoPush: true},
			wantErr: nil,
		},
		{
			name:    "empty prompt",
			spec:    BuildSpec{},
			wantErr: ErrPromptRequired,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := tt.spec.Validate()
			if tt.wantErr != nil {
				if !errors.Is(err, tt.wantErr) {
					t.Errorf("Validate() error = %v, want %v", err, tt.wantErr)
				}
			} else if err != nil {
				t.Errorf("Validate() unexpected error = %v", err)
			}
		})
	}
}

func TestBuildStatus_IsValid(t *testing.T) {
	tests := []struct {
		status BuildStatus
		want   bool
	}{
		{BuildStatusPending, true},
		{BuildStatusRunning, true},
		{BuildStatusCompleted, true},
		{BuildStatusFailed, true},
		{BuildStatusCancelled, true},
		{"unknown", false},
		{"", false},
	}

	for _, tt := range tests {
		t.Run(string(tt.status), func(t *testing.T) {
			if got := tt.status.IsValid(); got != tt.want {
				t.Errorf("IsValid() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestBuildResult_ToWorkResult(t *testing.T) {
	t.Run("success with all fields", func(t *testing.T) {
		result := &BuildResult{
			Success:      true,
			Output:       "Build completed",
			CommitSHA:    "abc123",
			FilesChanged: []string{"main.go", "go.mod"},
			DurationMs:   1500,
			Artifacts:    map[string]string{"deploy_url": "https://example.com"},
		}

		wr := result.ToWorkResult()

		if wr.Output != "Build completed" {
			t.Errorf("Output = %q, want %q", wr.Output, "Build completed")
		}
		if wr.Artifacts["commit_sha"] != "abc123" {
			t.Errorf("commit_sha = %q, want %q", wr.Artifacts["commit_sha"], "abc123")
		}
		if wr.Artifacts["duration_ms"] != "1500" {
			t.Errorf("duration_ms = %q, want %q", wr.Artifacts["duration_ms"], "1500")
		}
		if wr.Artifacts["files_changed_count"] != "2" {
			t.Errorf("files_changed_count = %q, want %q", wr.Artifacts["files_changed_count"], "2")
		}
		if wr.Artifacts["deploy_url"] != "https://example.com" {
			t.Errorf("deploy_url = %q, want %q", wr.Artifacts["deploy_url"], "https://example.com")
		}
	})

	t.Run("failure uses error as output", func(t *testing.T) {
		result := &BuildResult{
			Success: false,
			Output:  "partial output",
			Error:   "build failed: missing deps",
		}

		wr := result.ToWorkResult()

		if wr.Output != "build failed: missing deps" {
			t.Errorf("Output = %q, want error message", wr.Output)
		}
	})

	t.Run("nil artifacts map is safe", func(t *testing.T) {
		result := &BuildResult{
			Success: true,
			Output:  "done",
		}

		wr := result.ToWorkResult()

		if wr.Output != "done" {
			t.Errorf("Output = %q, want %q", wr.Output, "done")
		}
		if len(wr.Artifacts) != 0 {
			t.Errorf("Artifacts = %v, want empty", wr.Artifacts)
		}
	})

	t.Run("zero duration not included", func(t *testing.T) {
		result := &BuildResult{
			Success:    true,
			DurationMs: 0,
		}

		wr := result.ToWorkResult()

		if _, ok := wr.Artifacts["duration_ms"]; ok {
			t.Error("duration_ms should not be in artifacts when zero")
		}
	})

	t.Run("nil receiver returns empty result", func(t *testing.T) {
		var result *BuildResult
		wr := result.ToWorkResult()

		if wr.Output != "" {
			t.Errorf("Output = %q, want empty", wr.Output)
		}
		if wr.Artifacts != nil {
			t.Errorf("Artifacts = %v, want nil", wr.Artifacts)
		}
	})

	t.Run("promoted fields overwrite existing artifacts", func(t *testing.T) {
		result := &BuildResult{
			Success:   true,
			CommitSHA: "new-sha",
			Artifacts: map[string]string{
				"commit_sha": "old-sha",
				"custom_key": "kept",
			},
		}

		wr := result.ToWorkResult()

		if wr.Artifacts["commit_sha"] != "new-sha" {
			t.Errorf("commit_sha = %q, want %q (promoted field should overwrite)", wr.Artifacts["commit_sha"], "new-sha")
		}
		if wr.Artifacts["custom_key"] != "kept" {
			t.Errorf("custom_key = %q, want %q", wr.Artifacts["custom_key"], "kept")
		}
	})

	t.Run("failed with empty error keeps output", func(t *testing.T) {
		result := &BuildResult{
			Success: false,
			Output:  "partial output before crash",
			Error:   "",
		}

		wr := result.ToWorkResult()

		if wr.Output != "partial output before crash" {
			t.Errorf("Output = %q, want original output when error is empty", wr.Output)
		}
	})
}

func TestBuildStatus_IsTerminal(t *testing.T) {
	tests := []struct {
		status BuildStatus
		want   bool
	}{
		{BuildStatusPending, false},
		{BuildStatusRunning, false},
		{BuildStatusCompleted, true},
		{BuildStatusFailed, true},
		{BuildStatusCancelled, true},
	}

	for _, tt := range tests {
		t.Run(string(tt.status), func(t *testing.T) {
			if got := tt.status.IsTerminal(); got != tt.want {
				t.Errorf("IsTerminal() = %v, want %v", got, tt.want)
			}
		})
	}
}
