package prediction

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"math/big"
)

func BuildPrompt(workflowJSON string, binding InputBinding, input map[string]any) (map[string]any, error) {
	var workflow map[string]any
	if err := json.Unmarshal([]byte(workflowJSON), &workflow); err != nil {
		return nil, fmt.Errorf("invalid workflow: %w", err)
	}
	prompt := deepCopyMap(workflow)
	for name, target := range binding {
		if target == nil || target.Node == "" || target.Field == "" {
			continue
		}
		val, ok := input[name]
		if !ok {
			continue
		}
		if name == "seed" {
			val = resolveSeed(val)
		}
		node, ok := prompt[target.Node].(map[string]any)
		if !ok {
			return nil, fmt.Errorf("node %s not found", target.Node)
		}
		inputs, ok := node["inputs"].(map[string]any)
		if !ok {
			return nil, fmt.Errorf("node %s inputs missing", target.Node)
		}
		inputs[target.Field] = val
	}
	sanitizeSamplerSeeds(prompt)
	return prompt, nil
}

// resolveSeed maps seed < 0 (random) to a non-negative value ComfyUI accepts.
func resolveSeed(val any) any {
	n, ok := seedAsInt64(val)
	if !ok || n >= 0 {
		return val
	}
	r, err := rand.Int(rand.Reader, big.NewInt(1<<31-1))
	if err != nil {
		return int(0)
	}
	return int(r.Int64())
}

func seedAsInt64(v any) (int64, bool) {
	switch n := v.(type) {
	case int:
		return int64(n), true
	case int64:
		return n, true
	case float64:
		return int64(n), true
	case json.Number:
		i, err := n.Int64()
		return i, err == nil
	default:
		return 0, false
	}
}

func sanitizeSamplerSeeds(prompt map[string]any) {
	seedFields := []string{"seed", "noise_seed"}
	for _, raw := range prompt {
		node, ok := raw.(map[string]any)
		if !ok {
			continue
		}
		if class, _ := node["class_type"].(string); class != "KSampler" {
			continue
		}
		inputs, ok := node["inputs"].(map[string]any)
		if !ok {
			continue
		}
		for _, field := range seedFields {
			if v, ok := inputs[field]; ok {
				inputs[field] = resolveSeed(v)
			}
		}
	}
}

func deepCopyMap(src map[string]any) map[string]any {
	b, _ := json.Marshal(src)
	var dst map[string]any
	_ = json.Unmarshal(b, &dst)
	return dst
}
