package service

import (
	"fmt"
	"sync/atomic"
	"time"

	"google.golang.org/protobuf/types/known/structpb"

	eventpkg "iop/packages/go/events"
	"iop/packages/go/execution"
	iop "iop/proto/gen/iop"
)

var lastRunIDNanos atomic.Int64

func dispatchResponseStallTimeout(ms int64) int64 {
	if ms == 0 {
		return execution.DefaultResponseStallTimeoutMS
	}
	return ms
}

func NewRunID() string {
	return newRunIDAt(time.Now().UnixNano())
}

func newRunIDAt(now int64) string {
	for {
		previous := lastRunIDNanos.Load()
		next := now
		if next <= previous {
			next = previous + 1
		}
		if lastRunIDNanos.CompareAndSwap(previous, next) {
			return fmt.Sprintf("manual-%d", next)
		}
	}
}

func IsNodeDisconnected(event *iop.EdgeNodeEvent) bool {
	return event.GetType() == eventpkg.TypeNodeDisconnected
}

func BuildRunRequest(req SubmitRunRequest) (*iop.RunRequest, string, error) {
	inputMap := make(map[string]any, len(req.Input)+1)
	for k, v := range req.Input {
		inputMap[k] = v
	}
	if req.Prompt != "" {
		if _, ok := inputMap["prompt"]; !ok {
			inputMap["prompt"] = req.Prompt
		}
	}
	input, err := structpb.NewStruct(inputMap)
	if err != nil {
		return nil, "", err
	}
	runID := req.RunID
	if runID == "" {
		runID = NewRunID()
	}
	metadata := make(map[string]string, len(req.Metadata))
	for k, v := range req.Metadata {
		metadata[k] = v
	}
	return &iop.RunRequest{
		RunId:                  runID,
		Adapter:                req.Adapter,
		Target:                 req.Target,
		SessionId:              NormalizeSessionID(req.SessionID),
		Background:             req.Background,
		Input:                  input,
		TimeoutSec:             int32(normalizeTimeoutSec(req.TimeoutSec)),
		Metadata:               metadata,
		ResponseStallTimeoutMs: req.ResponseStallTimeoutMS,
	}, runID, nil
}
