package authoring

import (
	"encoding/json"
	"strings"
	"testing"

	"github.com/nomadcode/nomadcode-core/internal/storage"
)

func checkoutMeta(slotPath, sourceBranch string) json.RawMessage {
	raw, _ := json.Marshal(map[string]any{
		"checkout": map[string]any{
			"slot_path":     slotPath,
			"source_branch": sourceBranch,
			"slot_index":    0,
			"slot_id":       1,
		},
	})
	return raw
}

func TestBuildAuthoringGenerateInputPlaneOrigin(t *testing.T) {
	task := storage.Task{
		ID:       "task-1",
		Title:    "Create milestone for NOMAD-42",
		Source:   "plane",
		Payload:  json.RawMessage(`{"prompt":"Author the Milestone for NOMAD-42","instructions":"Use roadmap skill"}`),
		Metadata: checkoutMeta("/home/user/workspace/nomadcode/slots/000", "develop"),
	}

	input, ok := BuildAuthoringGenerateInput(task)
	if !ok {
		t.Fatal("expected authoring task to be detected")
	}
	// prompt override wins over message/title
	if input.Input != "Author the Milestone for NOMAD-42" {
		t.Errorf("unexpected input: %q", input.Input)
	}
	if input.Instructions != "Use roadmap skill" {
		t.Errorf("unexpected instructions: %q", input.Instructions)
	}
	if input.WorkspaceMetadata == nil {
		t.Fatal("expected WorkspaceMetadata to be set")
	}
	if input.WorkspaceMetadata.Path != "/home/user/workspace/nomadcode/slots/000" {
		t.Errorf("unexpected workspace path: %q", input.WorkspaceMetadata.Path)
	}
	if input.WorkspaceMetadata.SourceBranch != "develop" {
		t.Errorf("unexpected source branch: %q", input.WorkspaceMetadata.SourceBranch)
	}
	if input.Metadata["task_id"] != "task-1" || input.Metadata["source"] != "plane" {
		t.Errorf("unexpected metadata: %#v", input.Metadata)
	}
}

func TestBuildAuthoringGenerateInputPipelinePayload(t *testing.T) {
	// pipeline payload shape produced by workitem.BuildCreateTaskInput:
	// message = Plane ticket body, work_item = provider identity map
	pipelineBody := `{"message":"Plane ticket body text","work_item":{"provider":"plane","tenant":"acme","project":"proj-1","id":"NOMAD-42","state_id":"backlog","external_url":"https://plane.example.com/NOMAD-42"}}`
	extID := "NOMAD-42"
	extProvider := "plane"
	task := storage.Task{
		ID:               "task-2",
		Title:            "fallback title",
		Source:           "plane",
		Payload:          json.RawMessage(pipelineBody),
		Metadata:         checkoutMeta("/home/user/workspace/nomadcode/slots/001", "develop"),
		ExternalProvider: &extProvider,
		ExternalID:       &extID,
	}

	input, ok := BuildAuthoringGenerateInput(task)
	if !ok {
		t.Fatal("expected authoring task to be detected")
	}
	// message field is used as input when no prompt override is present
	if input.Input != "Plane ticket body text" {
		t.Errorf("expected pipeline message as input, got %q", input.Input)
	}
	if input.WorkspaceMetadata == nil {
		t.Fatal("expected WorkspaceMetadata to be set")
	}
	// provider identity from ExternalProvider/ExternalID (highest priority)
	if input.WorkspaceMetadata.Provider != "plane" {
		t.Errorf("unexpected provider: %q", input.WorkspaceMetadata.Provider)
	}
	if input.WorkspaceMetadata.WorkItemID != "NOMAD-42" {
		t.Errorf("unexpected work_item_id: %q", input.WorkspaceMetadata.WorkItemID)
	}
}

func TestBuildAuthoringGenerateInputProviderIdentityFallsBackToWorkItemMap(t *testing.T) {
	// When ExternalProvider/ExternalID are nil, fall back to work_item map in payload.
	pipelineBody := `{"message":"body","work_item":{"provider":"plane","id":"NOMAD-99"}}`
	task := storage.Task{
		ID:       "task-3",
		Source:   "plane",
		Payload:  json.RawMessage(pipelineBody),
		Metadata: checkoutMeta("/home/user/workspace/nomadcode/slots/000", "develop"),
	}

	input, ok := BuildAuthoringGenerateInput(task)
	if !ok {
		t.Fatal("expected authoring task to be detected")
	}
	if input.WorkspaceMetadata.Provider != "plane" {
		t.Errorf("unexpected provider: %q", input.WorkspaceMetadata.Provider)
	}
	if input.WorkspaceMetadata.WorkItemID != "NOMAD-99" {
		t.Errorf("unexpected work_item_id: %q", input.WorkspaceMetadata.WorkItemID)
	}
}

func TestBuildAuthoringGenerateInputFallbackInstructions(t *testing.T) {
	task := storage.Task{
		ID:       "task-2",
		Title:    "My work item title",
		Source:   "plane",
		Payload:  json.RawMessage(`{}`),
		Metadata: checkoutMeta("/home/user/workspace/nomadcode/slots/001", "develop"),
	}

	input, ok := BuildAuthoringGenerateInput(task)
	if !ok {
		t.Fatal("expected authoring task to be detected")
	}
	if input.Input != "My work item title" {
		t.Errorf("unexpected input fallback: %q", input.Input)
	}
	if input.Instructions == "" {
		t.Error("expected fallback instructions to be set")
	}
	if input.WorkspaceMetadata.Path != "/home/user/workspace/nomadcode/slots/001" {
		t.Errorf("unexpected workspace path: %q", input.WorkspaceMetadata.Path)
	}
}

func TestBuildAuthoringGenerateInputNonPlaneSource(t *testing.T) {
	task := storage.Task{
		ID:       "task-3",
		Title:    "Generic task",
		Source:   "manual",
		Payload:  json.RawMessage(`{"prompt":"do something"}`),
		Metadata: checkoutMeta("/home/user/workspace/nomadcode/slots/000", "develop"),
	}

	_, ok := BuildAuthoringGenerateInput(task)
	if ok {
		t.Error("expected non-plane task to not be detected as authoring task")
	}
}

func TestBuildAuthoringGenerateInputMissingCheckout(t *testing.T) {
	task := storage.Task{
		ID:     "task-4",
		Title:  "Plane task without checkout",
		Source: "plane",
		Metadata: json.RawMessage(`{"agent_run_state":"running"}`),
	}

	_, ok := BuildAuthoringGenerateInput(task)
	if ok {
		t.Error("expected task without checkout metadata to not be detected as authoring task")
	}
}

func TestBuildAuthoringGenerateInputEmptySlotPath(t *testing.T) {
	raw, _ := json.Marshal(map[string]any{
		"checkout": map[string]any{
			"slot_path":     "",
			"source_branch": "develop",
		},
	})
	task := storage.Task{
		ID:       "task-5",
		Title:    "Plane task with empty slot path",
		Source:   "plane",
		Metadata: raw,
	}

	_, ok := BuildAuthoringGenerateInput(task)
	if ok {
		t.Error("expected task with empty slot path to not be detected as authoring task")
	}
}

func TestBuildAuthoringGenerateInputCarriesProviderIdentityContext(t *testing.T) {
	pipelineBody := `{"message":"Plane ticket body text","work_item":{"provider":"plane","tenant":"acme","project":"proj-1","id":"NOMAD-42","state_id":"backlog","external_url":"https://plane.example.com/NOMAD-42"}}`
	extID := "NOMAD-42"
	extProvider := "plane"
	extURL := "https://plane.example.com/NOMAD-42"
	extMeta := json.RawMessage(`{"tenant":"acme-fallback","project":"proj-fallback","state_id":"state-fallback","external_url":"https://fallback.com"}`)

	task := storage.Task{
		ID:               "task-2",
		Title:            "fallback title",
		Source:           "plane",
		Payload:          json.RawMessage(pipelineBody),
		Metadata:         checkoutMeta("/home/user/workspace/nomadcode/slots/001", "develop"),
		ExternalProvider: &extProvider,
		ExternalID:       &extID,
		ExternalUrl:      &extURL,
		ExternalMetadata: extMeta,
	}

	input, ok := BuildAuthoringGenerateInput(task)
	if !ok {
		t.Fatal("expected authoring task to be detected")
	}

	if input.WorkspaceMetadata == nil {
		t.Fatal("expected WorkspaceMetadata to be set")
	}

	// Payload values should take precedence or fallback to task fields
	if input.WorkspaceMetadata.Provider != "plane" {
		t.Errorf("unexpected provider: %q", input.WorkspaceMetadata.Provider)
	}
	if input.WorkspaceMetadata.WorkItemID != "NOMAD-42" {
		t.Errorf("unexpected work_item_id: %q", input.WorkspaceMetadata.WorkItemID)
	}
	if input.WorkspaceMetadata.Tenant != "acme" { // payload value
		t.Errorf("unexpected tenant: %q", input.WorkspaceMetadata.Tenant)
	}
	if input.WorkspaceMetadata.Project != "proj-1" { // payload value
		t.Errorf("unexpected project: %q", input.WorkspaceMetadata.Project)
	}
	if input.WorkspaceMetadata.ExternalURL != "https://plane.example.com/NOMAD-42" { // task/payload value
		t.Errorf("unexpected external URL: %q", input.WorkspaceMetadata.ExternalURL)
	}
	if input.WorkspaceMetadata.StateID != "backlog" { // payload value
		t.Errorf("unexpected state id: %q", input.WorkspaceMetadata.StateID)
	}
}

func TestBuildAuthoringGenerateInputFallbackInstructionsRequireIdentityBlockAndNoDuplicate(t *testing.T) {
	task := storage.Task{
		ID:       "task-2",
		Title:    "My work item title",
		Source:   "plane",
		Payload:  json.RawMessage(`{}`),
		Metadata: checkoutMeta("/home/user/workspace/nomadcode/slots/001", "develop"),
	}

	input, ok := BuildAuthoringGenerateInput(task)
	if !ok {
		t.Fatal("expected authoring task to be detected")
	}

	assertions := []string{
		"Provider identity block",
		"duplicate",
		"active Milestone",
		"develop",
	}

	for _, ast := range assertions {
		if !containsIgnoreCase(input.Instructions, ast) {
			t.Errorf("expected instructions to contain %q, but got:\n%s", ast, input.Instructions)
		}
	}
}

func containsIgnoreCase(s, substr string) bool {
	return strings.Contains(strings.ToLower(s), strings.ToLower(substr))
}
