package cli

import (
	"encoding/json"
	"fmt"
	"strings"

	"iop/apps/node/internal/runtime"
)

type streamJSONEmitter struct{}

func (streamJSONEmitter) Name() string { return "stream-json" }

func (e streamJSONEmitter) Emit(line string) ([]runtime.RuntimeEvent, error) {
	var ev struct {
		Type       string         `json:"type"`
		Role       string         `json:"role"`
		Content    string         `json:"content"`
		Error      any            `json:"error"`
		Message    string         `json:"message"`
		Status     string         `json:"status"`
		ToolName   string         `json:"tool_name"`
		ToolID     string         `json:"tool_id"`
		Parameters map[string]any `json:"parameters"`
		Output     string         `json:"output"`
	}
	if err := json.Unmarshal([]byte(line), &ev); err != nil {
		return nil, nil
	}
	switch ev.Type {
	case "message":
		if ev.Role != "assistant" || ev.Content == "" {
			return nil, nil
		}
		return []runtime.RuntimeEvent{{
			Type:  runtime.EventTypeDelta,
			Delta: ev.Content,
		}}, nil
	case "tool_use":
		if delta := streamToolUseDelta(ev.ToolName, ev.Parameters); delta != "" {
			return []runtime.RuntimeEvent{{
				Type:  runtime.EventTypeDelta,
				Delta: delta,
				Metadata: map[string]string{
					"kind":      "tool_use",
					"tool_name": ev.ToolName,
					"tool_id":   ev.ToolID,
				},
			}}, nil
		}
		return nil, nil
	case "tool_result":
		if delta := streamToolResultDelta(ev.Status, ev.Output); delta != "" {
			return []runtime.RuntimeEvent{{
				Type:  runtime.EventTypeDelta,
				Delta: delta,
				Metadata: map[string]string{
					"kind":    "tool_result",
					"tool_id": ev.ToolID,
				},
			}}, nil
		}
		return nil, nil
	case "result":
		if ev.Status != "" && ev.Status != "success" {
			msg := ev.Message
			if msg == "" {
				msg = streamErrorString(ev.Error)
			}
			if msg == "" {
				msg = ev.Status
			}
			return []runtime.RuntimeEvent{{
				Type:  runtime.EventTypeError,
				Error: msg,
			}}, nil
		}
		return nil, nil
	case "error":
		return []runtime.RuntimeEvent{{
			Type:  runtime.EventTypeError,
			Error: streamErrorString(ev.Error),
		}}, nil
	}
	return nil, nil
}

func streamToolUseDelta(toolName string, params map[string]any) string {
	if toolName == "" {
		return ""
	}
	summary := streamToolParamSummary(params)
	if summary == "" {
		return fmt.Sprintf("[tool] %s\n", toolName)
	}
	return fmt.Sprintf("[tool] %s: %s\n", toolName, summary)
}

func streamToolParamSummary(params map[string]any) string {
	if params == nil {
		return ""
	}
	for _, key := range []string{"command", "description", "title", "summary"} {
		if s, ok := params[key].(string); ok && strings.TrimSpace(s) != "" {
			return truncateStreamToolText(strings.TrimSpace(s), 180)
		}
	}
	return ""
}

func streamToolResultDelta(status, output string) string {
	status = strings.TrimSpace(status)
	output = strings.TrimSpace(output)
	if status == "" && output == "" {
		return ""
	}
	if status == "" {
		status = "done"
	}
	if output == "" {
		return fmt.Sprintf("[tool-result] %s\n", status)
	}
	return fmt.Sprintf("[tool-result] %s: %s\n", status, truncateStreamToolText(output, 180))
}

func streamErrorString(v any) string {
	switch err := v.(type) {
	case string:
		return err
	case map[string]any:
		if msg, ok := err["message"].(string); ok {
			return msg
		}
		if data, ok := err["data"].(map[string]any); ok {
			if msg, ok := data["message"].(string); ok {
				return msg
			}
		}
		if name, ok := err["name"].(string); ok {
			return name
		}
	}
	return ""
}

func truncateStreamToolText(s string, max int) string {
	s = strings.Join(strings.Fields(s), " ")
	if len(s) <= max {
		return s
	}
	if max <= 3 {
		return s[:max]
	}
	return s[:max-3] + "..."
}
