package eino

import (
	"encoding/json"
	"sync/atomic"
	"time"

	"github.com/sundynix/sundynix-shared/contract"
)

// ExecSink 是执行可视化事件的回流出口（由 NATS bus 实现）：
// 把节点/阶段生命周期事件发到 sundynix.exec.<id>，供"运行·观测"实时点亮轨迹。
type ExecSink interface {
	PublishExec(taskID string, data []byte) error
	CompleteExec(taskID string) error
}

// execTracer 为一个任务发结构化执行事件，自增 Seq 保序、span 自动计耗时。
type execTracer struct {
	sink      ExecSink
	task      string
	seq       int32
	suspended bool // HITL 中断挂起：done() 不关 exec 流，留给 resume 续录（对齐 token 流的中断处理）
}

// tracer 为某任务建一个事件发射器（sink 为空时所有方法变空操作）。
func (o *Orchestrator) tracer(taskID string) *execTracer {
	return &execTracer{sink: o.exec, task: taskID}
}

func (e *execTracer) emit(node, kind, phase, label, detail string, ms int64) {
	if e == nil || e.sink == nil {
		return
	}
	ev := contract.ExecEvent{
		Seq: int(atomic.AddInt32(&e.seq, 1)), TS: time.Now().UnixMilli(),
		Node: node, Kind: kind, Phase: phase, Label: label, Detail: detail, MS: ms,
	}
	if data, err := json.Marshal(&ev); err == nil {
		_ = e.sink.PublishExec(e.task, data)
	}
}

// info 发一条瞬时事件（无耗时）。
func (e *execTracer) info(node, kind, label, detail string) {
	e.emit(node, kind, "info", label, detail, 0)
}

// span 发 start，并返回一个结束函数：调用时按 err 发 end / error，附带耗时。
func (e *execTracer) span(node, kind, label string) func(detail string, err error) {
	e.emit(node, kind, "start", label, "", 0)
	t0 := time.Now()
	return func(detail string, err error) {
		ms := time.Since(t0).Milliseconds()
		if err != nil {
			e.emit(node, kind, "error", label, err.Error(), ms)
			return
		}
		e.emit(node, kind, "end", label, detail, ms)
	}
}

// done 关闭该任务的执行事件流（让 SSE 客户端收尾）。
// 中断挂起时不关：任务停在审批、resume 是另一次调用，过早关流会让录制器错过 resume 的轨迹事件
// （审批通过/拒绝、续跑的节点），导致复盘里审批节点永远停在「等待中」。
func (e *execTracer) done() {
	if e == nil || e.sink == nil || e.suspended {
		return
	}
	_ = e.sink.CompleteExec(e.task)
}
