package app

import (
	"encoding/json"
	"sort"
	"strings"
)

// artifactDisplayLines turns a structured (JSON) artifact preview into scannable plain-text lines,
// so the approval card and transcript show a readable summary instead of a raw JSON dump. Returns
// (nil, false) for non-JSON content, which falls back to the raw line view. The lines are plain;
// colorizePreviewLine adds the highlighting at render time. Kept in sync with previewRowCount via
// previewDisplayLines so band sizing/scroll bounds match what's drawn.
func artifactDisplayLines(content string) ([]string, bool) {
	trimmed := strings.TrimSpace(content)
	if !strings.HasPrefix(trimmed, "{") {
		return nil, false
	}
	var obj map[string]json.RawMessage
	if json.Unmarshal([]byte(trimmed), &obj) != nil {
		return nil, false
	}
	if lines, ok := planSummaryLines(obj); ok {
		return lines, true
	}
	return objectSummaryLines(obj), true
}

// planSummaryLines renders an execution_plan artifact ({goal, stages:[{id,role,prompt,produces,…}]})
// as a goal line plus a numbered, aligned stage list — the freestyle plan approval an operator sees.
func planSummaryLines(obj map[string]json.RawMessage) ([]string, bool) {
	raw, ok := obj["stages"]
	if !ok {
		return nil, false
	}
	var stages []struct {
		ID       string `json:"id"`
		Role     string `json:"role"`
		Prompt   string `json:"prompt"`
		Produces string `json:"produces"`
	}
	if json.Unmarshal(raw, &stages) != nil || len(stages) == 0 {
		return nil, false
	}

	var out []string
	if g := jsonString(obj["goal"]); g != "" {
		out = append(out, "Goal", "  "+g, "")
	}
	out = append(out, "Plan · "+plural(len(stages), "stage"))
	idW := 0
	for _, s := range stages {
		if len(s.ID) > idW {
			idW = len(s.ID)
		}
	}
	for i, s := range stages {
		head := "  " + itoa(i+1) + ". " + padRaw(s.ID, idW) + "  " + padRaw(s.Role, 12)
		if s.Produces != "" {
			head += " → " + s.Produces
		}
		out = append(out, head)
		if p := firstLine(s.Prompt); p != "" {
			out = append(out, "      "+p)
		}
	}
	return out, true
}

// objectSummaryLines pretty-prints any other JSON object as indented key/value lines — a readable
// fallback (e.g. the analyst's `analysis` artifact) instead of a single minified blob.
func objectSummaryLines(obj map[string]json.RawMessage) []string {
	keys := make([]string, 0, len(obj))
	for k := range obj {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	var out []string
	for _, k := range keys {
		out = append(out, jsonFieldLines(k, obj[k], 0)...)
	}
	return out
}

// jsonFieldLines renders one "key: value" field, recursing into nested objects/arrays with
// indentation. Scalars go inline; containers get a header line then their children.
func jsonFieldLines(key string, raw json.RawMessage, depth int) []string {
	indent := strings.Repeat("  ", depth)
	v := strings.TrimSpace(string(raw))
	switch {
	case strings.HasPrefix(v, "{"):
		var nested map[string]json.RawMessage
		if json.Unmarshal(raw, &nested) == nil && len(nested) > 0 {
			out := []string{indent + key + ":"}
			nkeys := make([]string, 0, len(nested))
			for k := range nested {
				nkeys = append(nkeys, k)
			}
			sort.Strings(nkeys)
			for _, nk := range nkeys {
				out = append(out, jsonFieldLines(nk, nested[nk], depth+1)...)
			}
			return out
		}
	case strings.HasPrefix(v, "["):
		var items []json.RawMessage
		if json.Unmarshal(raw, &items) == nil {
			out := []string{indent + key + ": (" + itoa(len(items)) + ")"}
			for _, it := range items {
				out = append(out, indent+"  • "+scalarString(it))
			}
			return out
		}
	}
	return []string{indent + key + ": " + scalarString(raw)}
}

// scalarString renders a JSON scalar (or compact fallback) as a single-line human string.
func scalarString(raw json.RawMessage) string {
	if s := jsonString(raw); s != "" {
		return firstLine(s)
	}
	return firstLine(strings.TrimSpace(string(raw)))
}

func jsonString(raw json.RawMessage) string {
	if len(raw) == 0 {
		return ""
	}
	var s string
	if json.Unmarshal(raw, &s) == nil {
		return s
	}
	return ""
}

func firstLine(s string) string {
	s = strings.TrimSpace(s)
	if i := strings.IndexByte(s, '\n'); i >= 0 {
		return strings.TrimSpace(s[:i]) + " …"
	}
	return s
}
