package cli

import (
	"encoding/json"

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

// --- claude-json emitter ---

type claudeJSONEmitter struct{}

func (claudeJSONEmitter) Name() string { return "claude-json" }

func (e claudeJSONEmitter) Emit(line string) ([]runtime.RuntimeEvent, error) {
	var ev struct {
		Type  string `json:"type"`
		Event struct {
			Type  string `json:"type"`
			Delta struct {
				Type string `json:"type"`
				Text string `json:"text"`
			} `json:"delta"`
		} `json:"event"`
		IsError bool   `json:"is_error"`
		Result  string `json:"result"`
	}
	if err := json.Unmarshal([]byte(line), &ev); err != nil {
		return nil, nil
	}
	switch ev.Type {
	case "stream_event":
		if ev.Event.Type == "content_block_delta" &&
			ev.Event.Delta.Type == "text_delta" &&
			ev.Event.Delta.Text != "" {
			return []runtime.RuntimeEvent{{
				Type:  runtime.EventTypeDelta,
				Delta: ev.Event.Delta.Text,
			}}, nil
		}
		return nil, nil
	case "result":
		if ev.IsError && ev.Result != "" {
			return []runtime.RuntimeEvent{{
				Type:  runtime.EventTypeError,
				Error: ev.Result,
			}}, nil
		}
		return nil, nil
	}
	return nil, nil
}

// --- codex-json emitter ---

type codexJSONEmitter struct{}

func (codexJSONEmitter) Name() string { return "codex-json" }

func (e codexJSONEmitter) Emit(line string) ([]runtime.RuntimeEvent, error) {
	var ev struct {
		Type string `json:"type"`
		Item struct {
			Type    string `json:"type"`
			Text    string `json:"text"`
			Delta   string `json:"delta"`
			Message struct {
				Content []struct {
					Type string `json:"type"`
					Text string `json:"text"`
				} `json:"content"`
			} `json:"message"`
		} `json:"item"`
		Delta   string `json:"delta"`
		Text    string `json:"text"`
		Message string `json:"message"`
		Error   struct {
			Message string `json:"message"`
		} `json:"error"`
		Content []struct {
			Type string `json:"type"`
			Text string `json:"text"`
		} `json:"content"`
	}
	if err := json.Unmarshal([]byte(line), &ev); err != nil {
		return nil, nil
	}
	switch ev.Type {
	case "item.delta", "item.updated", "response.output_text.delta", "output_text.delta":
		if delta := codexDeltaText(ev); delta != "" {
			return []runtime.RuntimeEvent{{
				Type:  runtime.EventTypeDelta,
				Delta: delta,
			}}, nil
		}
		return nil, nil
	case "item.completed":
		if ev.Item.Type == "agent_message" && ev.Item.Text != "" {
			return []runtime.RuntimeEvent{{
				Type:  runtime.EventTypeDelta,
				Delta: ev.Item.Text,
			}}, nil
		}
		if delta := codexDeltaText(ev); delta != "" {
			return []runtime.RuntimeEvent{{
				Type:  runtime.EventTypeDelta,
				Delta: delta,
			}}, nil
		}
		return nil, nil
	case "error":
		return []runtime.RuntimeEvent{{
			Type:  runtime.EventTypeError,
			Error: ev.Message,
		}}, nil
	case "turn.failed":
		return []runtime.RuntimeEvent{{
			Type:  runtime.EventTypeError,
			Error: ev.Error.Message,
		}}, nil
	}
	return nil, nil
}

func codexDeltaText(ev struct {
	Type string `json:"type"`
	Item struct {
		Type    string `json:"type"`
		Text    string `json:"text"`
		Delta   string `json:"delta"`
		Message struct {
			Content []struct {
				Type string `json:"type"`
				Text string `json:"text"`
			} `json:"content"`
		} `json:"message"`
	} `json:"item"`
	Delta   string `json:"delta"`
	Text    string `json:"text"`
	Message string `json:"message"`
	Error   struct {
		Message string `json:"message"`
	} `json:"error"`
	Content []struct {
		Type string `json:"type"`
		Text string `json:"text"`
	} `json:"content"`
}) string {
	if ev.Item.Delta != "" {
		return ev.Item.Delta
	}
	if ev.Delta != "" {
		return ev.Delta
	}
	if ev.Text != "" {
		return ev.Text
	}
	for _, part := range ev.Item.Message.Content {
		if part.Type == "output_text" && part.Text != "" {
			return part.Text
		}
	}
	for _, part := range ev.Content {
		if part.Type == "output_text" && part.Text != "" {
			return part.Text
		}
	}
	return ""
}

// --- opencode-json emitter ---

type opencodeJSONEmitter struct{}

func (opencodeJSONEmitter) Name() string { return "opencode-json" }

func (e opencodeJSONEmitter) Emit(line string) ([]runtime.RuntimeEvent, error) {
	var ev struct {
		Type  string `json:"type"`
		Error struct {
			Name string `json:"name"`
			Data struct {
				Message string `json:"message"`
			} `json:"data"`
		} `json:"error"`
		Part struct {
			Type string `json:"type"`
			Text string `json:"text"`
		} `json:"part"`
	}
	if err := json.Unmarshal([]byte(line), &ev); err != nil {
		return nil, nil
	}
	switch ev.Type {
	case "text":
		if ev.Part.Type == "text" && ev.Part.Text != "" {
			return []runtime.RuntimeEvent{{
				Type:  runtime.EventTypeDelta,
				Delta: ev.Part.Text,
			}}, nil
		}
		return nil, nil
	case "error":
		msg := ev.Error.Data.Message
		if msg == "" {
			msg = ev.Error.Name
		}
		if msg != "" {
			return []runtime.RuntimeEvent{{
				Type:  runtime.EventTypeError,
				Error: msg,
			}}, nil
		}
		return nil, nil
	}
	return nil, nil
}

// --- cline-json emitter ---

type clineJSONEmitter struct{}

func (clineJSONEmitter) Name() string { return "cline-json" }

func (e clineJSONEmitter) Emit(line string) ([]runtime.RuntimeEvent, error) {
	var ev struct {
		Type    string `json:"type"`
		Say     string `json:"say"`
		Ask     string `json:"ask"`
		Text    string `json:"text"`
		Message string `json:"message"`
		Status  string `json:"status"`
		Error   string `json:"error"`
	}
	if err := json.Unmarshal([]byte(line), &ev); err != nil {
		return nil, nil
	}
	switch {
	case ev.Type == "say" && ev.Say == "text" && ev.Text != "":
		return []runtime.RuntimeEvent{{
			Type:  runtime.EventTypeDelta,
			Delta: ev.Text,
		}}, nil
	case ev.Type == "say" && ev.Say == "error":
		msg := ev.Text
		if msg == "" {
			msg = ev.Message
		}
		if msg != "" {
			return []runtime.RuntimeEvent{{
				Type:  runtime.EventTypeError,
				Error: msg,
			}}, nil
		}
		return nil, nil
	case ev.Type == "completion" && ev.Status == "error":
		msg := ev.Error
		if msg == "" {
			msg = ev.Message
		}
		if msg == "" {
			msg = "cline task failed"
		}
		return []runtime.RuntimeEvent{{
			Type:  runtime.EventTypeError,
			Error: msg,
		}}, nil
	case ev.Type == "ask" && ev.Ask == "api_req_failed":
		msg := ev.Text
		if msg == "" {
			msg = ev.Message
		}
		if msg != "" {
			return []runtime.RuntimeEvent{{
				Type:  runtime.EventTypeError,
				Error: msg,
			}}, nil
		}
		return nil, nil
	}
	return nil, nil
}
