package core

import (
	"context"
	"encoding/json"
	"io"

	"ragflow/internal/harness/core/schema"
)

// typedStateModelWrapper unifies message deep copy, ID injection, cancel checking,
// and event sending into a single wrapper layer for the model call.
//
// This is the central wrapper (typedStateModelWrapper) that sits between
// middlewares and the retry/failover chain, adding:
//   - Message deep copy (prevent pointer-sharing in middleware chain)
//   - Message ID auto-assignment
//   - Cancel context checking before model call
//   - Model output event emission
//   - BeforeModelRewrite / AfterModelRewrite orchestration (via chatmodel.go loop)
type typedStateModelWrapper[M MessageType] struct {
	inner     Model[M]
	cancelCtx *cancelContext
}

func newTypedStateModelWrapper[M MessageType](inner Model[M], cc *cancelContext) Model[M] {
	return &typedStateModelWrapper[M]{inner: inner, cancelCtx: cc}
}

// copyMessage performs a deep copy of a Message or AgenticMessage to prevent
// pointer-sharing bugs when the same message flows through multiple wrappers.
//
// The Extra map uses JSON marshal/unmarshal for deep copy (same approach as
// checkpoint.deepCopy) so that nested maps/slices are fully independent.
// If JSON round-trip fails for a value, the original reference is kept as
// a fallback to avoid data loss.
func copyMessage[M MessageType](msg M) M {
	switch v := any(msg).(type) {
	case *schema.Message:
		cp := &schema.Message{
			Role:    v.Role,
			Content: v.Content,
			Name:    v.Name,
		}
		if len(v.ToolCalls) > 0 {
			cp.ToolCalls = make([]schema.ToolCall, len(v.ToolCalls))
			copy(cp.ToolCalls, v.ToolCalls)
		}
		if v.Extra != nil {
			cp.Extra = make(map[string]any, len(v.Extra))
			for k, val := range v.Extra {
				cp.Extra[k] = deepCopyAny(val)
			}
		}
		return any(cp).(M)
	case *schema.AgenticMessage:
		cp := &schema.AgenticMessage{
			Role:    v.Role,
			Content: v.Content,
		}
		if len(v.ContentBlocks) > 0 {
			cp.ContentBlocks = make([]schema.ContentBlock, len(v.ContentBlocks))
			copy(cp.ContentBlocks, v.ContentBlocks)
		}
		return any(cp).(M)
	}
	return msg
}

// deepCopyAny performs a deep copy of an arbitrary value via JSON round-trip.
// Falls back to the original value if JSON marshal/unmarshal fails.
func deepCopyAny(v any) any {
	if v == nil {
		return nil
	}
	data, err := json.Marshal(v)
	if err != nil {
		return v // fallback: keep original reference
	}
	var result any
	if err := json.Unmarshal(data, &result); err != nil {
		return v // fallback: keep original reference
	}
	return result
}

// preprocessInput performs cancel check, deep copy, and message ID injection.
// Returns nil if cancelled (caller should return ErrStreamCanceled immediately).
func (w *typedStateModelWrapper[M]) preprocessInput(msgs []M) []M {
	if w.cancelCtx != nil && w.cancelCtx.isImmediate() {
		return nil
	}
	copied := make([]M, len(msgs))
	for i, m := range msgs {
		copied[i] = copyMessage(m)
	}
	for _, m := range copied {
		switch v := any(m).(type) {
		case *schema.Message:
			if v.Extra == nil {
				v.Extra = make(map[string]any)
			}
			v.Extra = EnsureMessageID(v.Extra)
		}
	}
	return copied
}

func (w *typedStateModelWrapper[M]) Generate(ctx context.Context, msgs []M, opts ...ModelOption) (M, error) {
	copied := w.preprocessInput(msgs)
	if copied == nil {
		var zero M
		return zero, ErrStreamCanceled
	}
	resp, err := w.inner.Generate(ctx, copied, opts...)
	if err != nil {
		return resp, err
	}
	return copyMessage(resp), nil
}

func (w *typedStateModelWrapper[M]) Stream(ctx context.Context, msgs []M, opts ...ModelOption) (*schema.StreamReader[M], error) {
	// Cancel check before allocating any resources (returns error-embedded StreamReader)
	if w.cancelCtx != nil && w.cancelCtx.isImmediate() {
		r := schema.NewStreamReader[M]()
		var zero M
		r.Send(zero, ErrStreamCanceled)
		r.Close()
		return r, nil
	}

	copied := w.preprocessInput(msgs)
	if copied == nil {
		return nil, ErrStreamCanceled
	}

	s, err := w.inner.Stream(ctx, copied, opts...)
	if err != nil {
		return nil, err
	}

	r := schema.NewStreamReader[M]()
	go func() {
		defer r.Close()
		defer s.Close()
		for {
			if w.cancelCtx != nil && w.cancelCtx.isImmediate() {
				var zero M
				r.Send(zero, ErrStreamCanceled)
				return
			}
			c, e := s.Recv()
			if e == io.EOF {
				break
			}
			if e != nil {
				r.Send(c, e)
				return
			}
			r.Send(copyMessage(c), nil)
		}
	}()
	return r, nil
}

func (w *typedStateModelWrapper[M]) BindTools(tools []*schema.ToolInfo) error {
	return w.inner.BindTools(tools)
}
