package scheduler

import (
	"context"
	"encoding/json"
	"log/slog"
	"time"

	"github.com/riverqueue/river"

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

// RoadmapCreationSyncJobArgs is the River job that drives one Plane-origin
// Milestone creation sync cycle. It carries the develop scan result and the
// expected work item identity (the develop match inputs) plus the projection
// inputs, so the worker can build the orchestrator input without reaching into
// provider DTOs. It is enqueued by the internal develop-scan handoff via
// Client.EnqueueRoadmapCreationSync, not by a public API.
type RoadmapCreationSyncJobArgs struct {
	Scan     roadmapsync.ScanResult `json:"scan"`
	Expected roadmapsync.Identity   `json:"expected"`

	Ref               workitem.Ref `json:"ref"`
	OriginalBody      string       `json:"original_body"`
	TodoStateID       string       `json:"todo_state_id"`
	MilestoneMarkdown string       `json:"milestone_markdown"`

	Actor            string `json:"actor,omitempty"`
	ProviderRevision string `json:"provider_revision,omitempty"`
	RoadmapRevision  string `json:"roadmap_revision,omitempty"`

	ExternalSource string `json:"external_source,omitempty"`
	ExternalID     string `json:"external_id,omitempty"`
}

func (RoadmapCreationSyncJobArgs) Kind() string {
	return "roadmap_creation_sync"
}

// CreationSyncRunner is the orchestrator seam the worker drives. It is satisfied
// by *roadmapsyncpipeline.Service and by test fakes, so the worker stays
// testable without a Plane client or database.
type CreationSyncRunner interface {
	SyncCreation(ctx context.Context, in roadmapsyncpipeline.SyncCreationInput) (roadmapsyncpipeline.SyncCreationResult, error)
}

type TaskFinalizer interface {
	CompleteTaskByExternalRef(ctx context.Context, provider string, id string, result json.RawMessage) (storage.Task, bool, error)
}

// RoadmapCreationSyncWorker runs a creation sync job by delegating to the
// orchestrator. Not-ready/conflict/self-mutation/complete are normal terminal
// outcomes the orchestrator already guards with no provider side effect, so the
// worker logs them and returns nil rather than failing and triggering a River
// retry. Only an actual error (provider/storage failure) is returned so River
// retries it.
type RoadmapCreationSyncWorker struct {
	river.WorkerDefaults[RoadmapCreationSyncJobArgs]

	Sync          CreationSyncRunner
	TaskFinalizer TaskFinalizer
	SlotUpdater   WorkspaceSlotStateUpdater
	Logger        *slog.Logger
}

func (w *RoadmapCreationSyncWorker) Work(ctx context.Context, job *river.Job[RoadmapCreationSyncJobArgs]) error {
	args := job.Args
	res, err := w.Sync.SyncCreation(ctx, roadmapsyncpipeline.SyncCreationInput{
		Scan:              args.Scan,
		Expected:          args.Expected,
		Ref:               args.Ref,
		OriginalBody:      args.OriginalBody,
		TodoStateID:       args.TodoStateID,
		MilestoneMarkdown: args.MilestoneMarkdown,
		Actor:             args.Actor,
		ProviderRevision:  args.ProviderRevision,
		RoadmapRevision:   args.RoadmapRevision,
		ExternalSource:    args.ExternalSource,
		ExternalID:        args.ExternalID,
	})
	if err != nil {
		return err
	}

	if (res.Action == roadmapsyncpipeline.SyncActionProjected || res.Action == roadmapsyncpipeline.SyncActionComplete) && w.TaskFinalizer != nil {
		resultMap := map[string]any{
			"action":                              string(res.Action),
			"reason":                              res.Reason,
			"summary":                             res.Reason,
			workflow.MetadataKeyAuthoringRunState: "succeeded",
			workflow.MetadataKeyAuthoringRunUpdatedAt: time.Now().UTC().Format(time.RFC3339),
		}
		if res.NextStep != "" {
			resultMap["next_step"] = string(res.NextStep)
		}
		resultBytes, err := json.Marshal(resultMap)
		if err != nil {
			return err
		}
		task, transitioned, err := w.TaskFinalizer.CompleteTaskByExternalRef(ctx, string(args.Ref.Provider), args.Ref.ID, resultBytes)
		if err != nil {
			return err
		}
		if transitioned {
			applyTaskSlotTerminalState(ctx, w.SlotUpdater, task, projectsync.SlotStateAvailable, w.Logger)
		}
	}

	if w.Logger != nil {
		w.Logger.Info("roadmap creation sync completed",
			"work_item_id", args.Expected.WorkItemID,
			"milestone_path", args.Expected.RoadmapMilestonePath,
			"action", string(res.Action),
			"reason", res.Reason,
		)
	}
	return nil
}
