package agent

import (
	"github.com/sipeed/picoclaw/pkg/bus"
	"github.com/sipeed/picoclaw/pkg/routing"
	"github.com/sipeed/picoclaw/pkg/session"
)

// TurnContext carries normalized turn-scoped facts that can be shared across
// events, hooks, and other runtime observers without re-parsing legacy fields.
type TurnContext struct {
	Inbound *bus.InboundContext    `json:"inbound,omitempty"`
	Route   *routing.ResolvedRoute `json:"route,omitempty"`
	Scope   *session.SessionScope  `json:"scope,omitempty"`
}

func newTurnContext(
	inbound *bus.InboundContext,
	route *routing.ResolvedRoute,
	scope *session.SessionScope,
) *TurnContext {
	if inbound == nil && route == nil && scope == nil {
		return nil
	}
	return &TurnContext{
		Inbound: cloneInboundContext(inbound),
		Route:   cloneResolvedRoute(route),
		Scope:   session.CloneScope(scope),
	}
}

func cloneTurnContext(ctx *TurnContext) *TurnContext {
	if ctx == nil {
		return nil
	}
	cloned := *ctx
	cloned.Inbound = cloneInboundContext(ctx.Inbound)
	cloned.Route = cloneResolvedRoute(ctx.Route)
	cloned.Scope = session.CloneScope(ctx.Scope)
	return &cloned
}

func cloneInboundContext(ctx *bus.InboundContext) *bus.InboundContext {
	if ctx == nil {
		return nil
	}
	cloned := *ctx
	cloned.ReplyHandles = cloneStringMap(ctx.ReplyHandles)
	cloned.Raw = cloneStringMap(ctx.Raw)
	return &cloned
}

func cloneStringMap(src map[string]string) map[string]string {
	if len(src) == 0 {
		return nil
	}
	cloned := make(map[string]string, len(src))
	for k, v := range src {
		cloned[k] = v
	}
	return cloned
}

func cloneHookMeta(meta HookMeta) HookMeta {
	meta.turnContext = cloneTurnContext(meta.turnContext)
	return meta
}

func cloneResolvedRoute(route *routing.ResolvedRoute) *routing.ResolvedRoute {
	if route == nil {
		return nil
	}
	cloned := *route
	cloned.SessionPolicy = routing.SessionPolicy{
		Dimensions:    append([]string(nil), route.SessionPolicy.Dimensions...),
		IdentityLinks: cloneIdentityLinks(route.SessionPolicy.IdentityLinks),
	}
	return &cloned
}

func cloneIdentityLinks(src map[string][]string) map[string][]string {
	if len(src) == 0 {
		return nil
	}
	cloned := make(map[string][]string, len(src))
	for canonical, ids := range src {
		dup := make([]string, len(ids))
		copy(dup, ids)
		cloned[canonical] = dup
	}
	return cloned
}
