package workitempipeline

import (
	"context"
	"encoding/json"
	"errors"
	"testing"

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

type fakeReader struct {
	item workitem.WorkItem
	err  error
}

func (f *fakeReader) FetchWorkItem(_ context.Context, _ workitem.Ref) (workitem.WorkItem, error) {
	return f.item, f.err
}

type fakeTaskCreator struct {
	task       storage.Task
	err        error
	calledWith *workflow.CreateTaskInput
}

func (f *fakeTaskCreator) CreateTask(_ context.Context, input workflow.CreateTaskInput) (storage.Task, error) {
	f.calledWith = &input
	return f.task, f.err
}

func TestCreateTaskFromWorkItemFetchesMapsAndCreatesTask(t *testing.T) {
	ref := workitem.Ref{Provider: "plane", Tenant: "acme", Project: "proj-1", ID: "work-1", URL: "https://example.com/work-1"}
	reader := &fakeReader{item: workitem.WorkItem{
		Ref:   ref,
		Title: "Fix issue",
	}}
	creator := &fakeTaskCreator{task: storage.Task{ID: "task-abc", Status: "pending"}}
	svc := New(reader, creator)

	result, err := svc.CreateTaskFromWorkItem(context.Background(), CreateTaskInput{
		Ref:     ref,
		StateID: "state-1",
		Comment: "my comment",
	})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if result.ID != "task-abc" {
		t.Errorf("id: got %q", result.ID)
	}
	if creator.calledWith == nil {
		t.Fatal("CreateTask was not called")
	}
	if creator.calledWith.Source != "plane" {
		t.Errorf("source: got %q", creator.calledWith.Source)
	}

	var payload map[string]any
	if err := json.Unmarshal(creator.calledWith.Payload, &payload); err != nil {
		t.Fatalf("payload unmarshal: %v", err)
	}
	if payload["message"] != "my comment" {
		t.Errorf("message: got %v", payload["message"])
	}
	wi, ok := payload["work_item"].(map[string]any)
	if !ok {
		t.Fatalf("work_item missing: %v", payload)
	}
	checks := map[string]string{
		"provider": "plane",
		"tenant":   "acme",
		"project":  "proj-1",
		"id":       "work-1",
	}
	for k, want := range checks {
		if wi[k] != want {
			t.Errorf("work_item[%q]: got %v, want %q", k, wi[k], want)
		}
	}

	if creator.calledWith.External == nil ||
		creator.calledWith.External.Provider != "plane" ||
		creator.calledWith.External.ID != "work-1" {
		t.Errorf("external ref: %v", creator.calledWith.External)
	}
}

func TestCreateTaskFromWorkItemPropagatesReaderError(t *testing.T) {
	readerErr := errors.New("not found")
	reader := &fakeReader{err: readerErr}
	creator := &fakeTaskCreator{}
	svc := New(reader, creator)

	_, err := svc.CreateTaskFromWorkItem(context.Background(), CreateTaskInput{
		Ref: workitem.Ref{Provider: "plane", ID: "work-1"},
	})
	if !errors.Is(err, readerErr) {
		t.Errorf("expected reader error, got %v", err)
	}
	if creator.calledWith != nil {
		t.Error("CreateTask should not have been called")
	}
}

func TestCreateTaskFromWorkItemPropagatesCreateTaskError(t *testing.T) {
	ref := workitem.Ref{Provider: "plane", Tenant: "acme", Project: "proj-1", ID: "work-1"}
	reader := &fakeReader{item: workitem.WorkItem{Ref: ref, Title: "T"}}
	createErr := errors.New("db error")
	creator := &fakeTaskCreator{err: createErr}
	svc := New(reader, creator)

	_, err := svc.CreateTaskFromWorkItem(context.Background(), CreateTaskInput{Ref: ref})
	if !errors.Is(err, createErr) {
		t.Errorf("expected create error, got %v", err)
	}
}
