package briefkit

import (
	"testing"
	"time"

	"github.com/google/uuid"
	"github.com/orbiqd/orbiqd-briefkit/internal/pkg/utils"
	"github.com/stretchr/testify/require"
)

func TestNewExecutionID(t *testing.T) {
	id := NewExecutionID()
	require.NotEmpty(t, id)
	_, err := uuid.Parse(string(id))
	require.NoError(t, err)

	other := NewExecutionID()
	require.NotEqual(t, id, other)
}

func TestExecutionIDValidate(t *testing.T) {
	t.Run("empty", func(t *testing.T) {
		err := ExecutionID("").Validate()
		require.ErrorIs(t, err, ErrExecutionIDInvalid)
	})

	t.Run("invalid", func(t *testing.T) {
		err := ExecutionID("not-a-uuid").Validate()
		require.ErrorIs(t, err, ErrExecutionIDInvalid)
	})

	t.Run("valid", func(t *testing.T) {
		err := ExecutionID(uuid.NewString()).Validate()
		require.NoError(t, err)
	})
}

func TestExecutionInputAttachmentValidate(t *testing.T) {
	t.Run("missing mime type", func(t *testing.T) {
		err := ExecutionInputAttachment{Path: "/tmp/file.png"}.Validate()
		require.ErrorIs(t, err, ErrExecutionAttachmentMimeTypeRequired)
	})

	t.Run("missing path", func(t *testing.T) {
		err := ExecutionInputAttachment{MimeType: "image/png"}.Validate()
		require.ErrorIs(t, err, ErrExecutionAttachmentPathRequired)
	})

	t.Run("valid", func(t *testing.T) {
		err := ExecutionInputAttachment{MimeType: "image/png", Path: "/tmp/file.png"}.Validate()
		require.NoError(t, err)
	})
}

func TestExecutionState_IsFinished(t *testing.T) {
	tests := []struct {
		name     string
		state    ExecutionState
		expected bool
	}{
		{
			name:     "created is not finished",
			state:    ExecutionCreated,
			expected: false,
		},
		{
			name:     "started is not finished",
			state:    ExecutionStarted,
			expected: false,
		},
		{
			name:     "running is not finished",
			state:    ExecutionRunning,
			expected: false,
		},
		{
			name:     "provisioning is not finished",
			state:    ExecutionProvisioning,
			expected: false,
		},
		{
			name:     "succeeded is finished",
			state:    ExecutionSucceeded,
			expected: true,
		},
		{
			name:     "failed is finished",
			state:    ExecutionFailed,
			expected: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := tt.state.IsFinished()
			require.Equal(t, tt.expected, result)
		})
	}
}

func TestExecutionInputValidate(t *testing.T) {
	workingDir := t.TempDir()
	valid := ExecutionInput{
		Prompt:           "Hello",
		Timeout:          utils.Duration(5 * time.Second),
		WorkingDirectory: &workingDir,
	}

	t.Run("missing prompt", func(t *testing.T) {
		input := valid
		input.Prompt = "  "
		err := input.Validate()
		require.ErrorIs(t, err, ErrExecutionPromptRequired)
	})

	t.Run("missing timeout", func(t *testing.T) {
		input := valid
		input.Timeout = 0
		err := input.Validate()
		require.ErrorIs(t, err, ErrExecutionTimeoutRequired)
	})

	t.Run("missing working directory", func(t *testing.T) {
		input := valid
		input.WorkingDirectory = nil
		err := input.Validate()
		require.NoError(t, err)
	})

	t.Run("empty working directory", func(t *testing.T) {
		input := valid
		empty := " "
		input.WorkingDirectory = &empty
		err := input.Validate()
		require.ErrorIs(t, err, ErrExecutionWorkingDirectoryRequired)
	})

	t.Run("relative working directory", func(t *testing.T) {
		input := valid
		relative := "relative/path"
		input.WorkingDirectory = &relative
		err := input.Validate()
		require.ErrorIs(t, err, ErrExecutionWorkingDirectoryNotAbsolute)
	})

	t.Run("invalid attachment", func(t *testing.T) {
		input := valid
		input.Attachments = []ExecutionInputAttachment{{Path: "/tmp/file.png"}}
		err := input.Validate()
		require.ErrorIs(t, err, ErrExecutionAttachmentMimeTypeRequired)
	})

	t.Run("workspace and working directory both set", func(t *testing.T) {
		input := valid
		workspaceURI := "dir:///tmp/project"
		input.Workspace = &workspaceURI
		err := input.Validate()
		require.ErrorIs(t, err, ErrExecutionWorkspaceMutuallyExclusive)
	})

	t.Run("empty workspace", func(t *testing.T) {
		input := valid
		input.WorkingDirectory = nil
		empty := "  "
		input.Workspace = &empty
		err := input.Validate()
		require.ErrorIs(t, err, ErrExecutionWorkspaceInvalid)
	})

	t.Run("workspace without scheme", func(t *testing.T) {
		input := valid
		input.WorkingDirectory = nil
		noScheme := "/tmp/project"
		input.Workspace = &noScheme
		err := input.Validate()
		require.ErrorIs(t, err, ErrExecutionWorkspaceInvalid)
	})

	t.Run("valid workspace", func(t *testing.T) {
		input := valid
		input.WorkingDirectory = nil
		workspaceURI := "dir:///tmp/project"
		input.Workspace = &workspaceURI
		err := input.Validate()
		require.NoError(t, err)
	})

	t.Run("workspace with nil working directory", func(t *testing.T) {
		input := valid
		input.WorkingDirectory = nil
		workspaceURI := "dir:///tmp/project"
		input.Workspace = &workspaceURI
		err := input.Validate()
		require.NoError(t, err)
	})

	t.Run("tilde working directory", func(t *testing.T) {
		home := t.TempDir()
		t.Setenv("HOME", home)
		input := valid
		tilde := "~"
		input.WorkingDirectory = &tilde
		err := input.Validate()
		require.NoError(t, err)
	})

	t.Run("valid", func(t *testing.T) {
		err := valid.Validate()
		require.NoError(t, err)
	})
}
