// Package mock provides a mock Adapter that echoes input with streaming deltas.
// Useful for testing transport and pipeline correctness without real models.
package mock

import (
	"context"
	"fmt"
	"strings"
	"time"

	"go.uber.org/zap"

	runtime "iop/packages/go/execution"
)

const Name = "mock"

type Mock struct {
	logger *zap.Logger
}

func New(logger *zap.Logger) *Mock {
	return &Mock{logger: logger}
}

func (m *Mock) Name() string { return Name }

func (m *Mock) Capabilities(_ context.Context) (runtime.Capabilities, error) {
	return runtime.Capabilities{
		AdapterName:    Name,
		Targets:        []string{"mock-echo", "mock-stream"},
		MaxConcurrency: 16,
		ProviderStatus: runtime.ProviderStatusAvailable,
	}, nil
}

func (m *Mock) ProbeProvider(ctx context.Context, target string) (runtime.ProviderProbeResult, error) {
	targets := []string{"mock-echo", "mock-stream"}
	return runtime.ProviderProbeResult{
		AdapterName: Name,
		Target:      target,
		Targets:     targets,
		Status:      runtime.ProviderStatusAvailable,
	}, nil
}

func (m *Mock) Execute(ctx context.Context, spec runtime.ExecutionSpec, sink runtime.EventSink) error {
	m.logger.Info("mock adapter executing", zap.String("run_id", spec.RunID))

	if err := sink.Emit(ctx, runtime.RuntimeEvent{
		RunID:     spec.RunID,
		Type:      runtime.EventTypeStart,
		Timestamp: time.Now(),
	}); err != nil {
		return err
	}

	prompt := extractPrompt(spec.Input)
	words := strings.Fields(fmt.Sprintf("echo: %s", prompt))
	if len(words) == 0 {
		words = []string{"(empty", "input)"}
	}

	for _, word := range words {
		select {
		case <-ctx.Done():
			return ctx.Err()
		default:
		}
		if err := sink.Emit(ctx, runtime.RuntimeEvent{
			RunID:     spec.RunID,
			Type:      runtime.EventTypeDelta,
			Delta:     word + " ",
			Timestamp: time.Now(),
		}); err != nil {
			return err
		}
		time.Sleep(30 * time.Millisecond)
	}

	return sink.Emit(ctx, runtime.RuntimeEvent{
		RunID:   spec.RunID,
		Type:    runtime.EventTypeComplete,
		Message: "mock execution complete",
		Usage: &runtime.UsageStats{
			InputTokens:  len(strings.Fields(prompt)),
			OutputTokens: len(words),
		},
		Timestamp: time.Now(),
	})
}

func extractPrompt(input map[string]any) string {
	if input == nil {
		return ""
	}
	if v, ok := input["prompt"]; ok {
		if s, ok := v.(string); ok {
			return s
		}
	}
	if v, ok := input["messages"]; ok {
		return fmt.Sprintf("%v", v)
	}
	return fmt.Sprintf("%v", input)
}
