package render

import (
	"fmt"
	"strings"

	"github.com/charmbracelet/lipgloss"
)

// statusIcon ports BaseToolRenderer.status_icon.
func statusIcon(status string) (string, lipgloss.Style) {
	switch status {
	case "running":
		return "● In progress...", Col(AmberY)
	case "completed":
		return "✓ Done", Col(Green)
	case "failed":
		return "✗ Failed", Col(SevCrit)
	case "error":
		return "✗ Error", Col(SevCrit)
	}
	return "○ Unknown", Dim()
}

// renderGenericTool ports registry._render_default_tool_widget. It shows the
// tool name, its arguments, and a status line only. The raw result is
// deliberately not rendered: a generic result (e.g. a multi-kilobyte JSON
// payload from a database query tool) is noise on screen, and the agent narrates
// what it got in its next message. The full result still lives in the event
// data, the run log, and the `strix view` viewer.
func renderGenericTool(name string, args map[string]any, status string) string {
	var b strings.Builder
	b.WriteString(Dim().Render("→ Using tool ") + Bold(Blue).Render(name) + "\n")
	for _, k := range SortedKeys(args) {
		b.WriteString("  " + Dim().Render(k) + ": " + StringValue(args[k]) + "\n")
	}
	icon, style := statusIcon(status)
	b.WriteString(style.Render(icon))
	return b.String()
}

// ---------------------------------------------------------------------------
// Dispatch
// ---------------------------------------------------------------------------

func Tool(data map[string]any) string {
	name := StringValue(data["tool_name"])
	status := StringValue(data["status"])
	args, _ := data["args"].(map[string]any)
	if args == nil {
		args = map[string]any{}
	}
	result := data["result"]

	// A call to a tool from one of the user's MCP servers is tagged with the
	// connection it came from, because its name is the server's own and means
	// nothing here. The tag is only ever set from the connections the run made,
	// so it is the one thing that can tell such a call apart from a built-in.
	if connection := StringValue(data["mcp_connection"]); connection != "" {
		toolName := StringValue(data["mcp_tool"])
		if toolName == "" {
			toolName = name
		}
		return renderMcpTool(connection, toolName, args, status)
	}

	switch name {
	case "exec_command":
		return renderExecCommand(args, result, status)
	case "write_stdin":
		return renderWriteStdin(args, result, status)
	case "apply_patch":
		return renderApplyPatch(args, result, status)
	case "view_image":
		return renderViewImage(args, result)
	case "create_vulnerability_report":
		return renderVulnerabilityReport(args, result)
	case "create_dependency_report":
		return renderDependencyReport(args, result)
	case "list_reports":
		return renderListReports(result)
	case "get_report":
		return renderGetReport(result)
	case "respond_to_user":
		return renderRespondToUser(args)
	case "finish_scan":
		return renderFinishScan(args)
	case "think":
		return renderThink(args)
	case "web_search":
		return renderWebSearch(args)
	case "load_skill":
		return renderLoadSkill(args, result)
	case "create_note", "delete_note", "update_note", "list_notes", "get_note":
		return renderNote(name, args, result)
	case "create_todo", "list_todos", "update_todo", "mark_todo_done", "mark_todo_pending", "delete_todo":
		return renderTodo(name, result)
	case "record_coverage", "update_coverage", "list_coverage":
		return renderCoverage(name, args, result)
	case "get_threat_model", "save_threat_model", "amend_threat_model":
		return renderThreatModel(name, args, result)
	case "view_agent_graph", "create_agent", "send_message_to_agent", "agent_finish", "wait_for_agents", "stop_agent":
		return renderAgentGraphTool(name, args, result)
	case "list_requests", "view_request", "repeat_request", "list_sitemap", "view_sitemap_entry", "scope_rules":
		return renderProxyTool(name, args, result, status)
	}
	return renderGenericTool(name, args, status)
}

// ---------------------------------------------------------------------------
// Collapsing: output-heavy tools (terminal, proxy) render as short block
// previews; clicking a tool in the trace expands it to the full render.
// ---------------------------------------------------------------------------

const outputPreviewLines = 10

// ToolPreviewLines returns how many lines of a tool's render are shown before
// it is collapsed; 0 means the tool is never collapsed. Only tools whose
// output can grow unbounded (terminal, patches, proxy) collapse.
func ToolPreviewLines(name string) int {
	switch name {
	case "exec_command", "write_stdin", "apply_patch",
		"view_request", "repeat_request", "view_sitemap_entry",
		"list_coverage", "get_threat_model":
		return outputPreviewLines
	}
	return 0
}

// CollapseTool clips a full tool render to its preview size, appending a
// click-to-expand/collapse hint. It reports whether the tool has more content
// than the preview (i.e. whether it is expandable).
func CollapseTool(full, name string, expanded bool) (string, bool) {
	maxLines := ToolPreviewLines(name)
	if maxLines <= 0 {
		return full, false
	}
	lines := strings.Split(full, "\n")
	if len(lines) <= maxLines {
		return full, false
	}
	if expanded {
		return full + "\n" + Dim().Italic(true).Render("  ▲ click to collapse"), true
	}
	preview := strings.Join(lines[:maxLines], "\n")
	hidden := len(lines) - maxLines
	plural := "s"
	if hidden == 1 {
		plural = ""
	}
	hint := Dim().Italic(true).Render(fmt.Sprintf("  … +%d line%s — click to expand", hidden, plural))
	return preview + "\n" + hint, true
}
