package promptcompiler

import (
	"fmt"
	"strings"
)

const workflowAgentEditMetadataKey = "workflowEdit"
const maxWorkflowAgentEditDiagnostics = 20

// WorkflowEditDiagnostic is the normalized diagnostic projection used by the
// Workflow Builder edit template.
type WorkflowEditDiagnostic struct {
	Severity string
	Code     string
	Path     string
	Location string
	Message  string
}

// WorkflowEditPromptData is the typed Workflow Builder edit request. The
// renderer receives no JSON-shaped metadata and owns all fixed wording.
type WorkflowEditPromptData struct {
	PlanID                string
	BaseDraftRevision     int64
	TargetLabel           string
	Diagnostics           []WorkflowEditDiagnostic
	AdditionalDiagnostics int
}

func normalizeWorkflowAgentEditPromptData(metadata map[string]any) (WorkflowEditPromptData, bool) {
	raw, ok := metadata[workflowAgentEditMetadataKey].(map[string]any)
	if !ok || promptStringMapValue(raw, "kind") != "request" {
		return WorkflowEditPromptData{}, false
	}
	planID := promptStringMapValue(raw, "planId")
	responderAgentID := promptStringMapValue(raw, "responderAgentId")
	revision, revisionOK := promptPositiveInteger(raw["baseDraftRevision"])
	target, targetOK := raw["target"].(map[string]any)
	targetKind := promptStringMapValue(target, "kind")
	targetID := promptStringMapValue(target, "id")
	if planID == "" || responderAgentID == "" || !revisionOK || !targetOK || targetKind == "" || targetID == "" {
		return WorkflowEditPromptData{}, false
	}
	targetLabel := targetKind + ":" + targetID
	if targetNodeType := promptStringMapValue(target, "nodeType"); targetNodeType != "" {
		targetLabel += " (" + targetNodeType + ")"
	}
	data := WorkflowEditPromptData{PlanID: planID, BaseDraftRevision: revision, TargetLabel: targetLabel}
	if rawDiagnostics, ok := raw["diagnostics"].([]any); ok {
		limit := len(rawDiagnostics)
		if limit > maxWorkflowAgentEditDiagnostics {
			data.AdditionalDiagnostics = limit - maxWorkflowAgentEditDiagnostics
			limit = maxWorkflowAgentEditDiagnostics
		}
		for _, item := range rawDiagnostics[:limit] {
			diagnostic, ok := item.(map[string]any)
			if !ok {
				continue
			}
			parts := make([]string, 0, 3)
			if severity := promptStringMapValue(diagnostic, "severity"); severity != "" {
				parts = append(parts, severity)
			}
			if code := promptStringMapValue(diagnostic, "code"); code != "" {
				parts = append(parts, code)
			}
			if path := promptStringMapValue(diagnostic, "path"); path != "" {
				parts = append(parts, path)
			}
			data.Diagnostics = append(data.Diagnostics, WorkflowEditDiagnostic{
				Severity: promptStringMapValue(diagnostic, "severity"),
				Code:     promptStringMapValue(diagnostic, "code"),
				Path:     promptStringMapValue(diagnostic, "path"),
				Location: workflowDiagnosticLocation(diagnostic),
				Message:  compactPromptLine(promptStringMapValue(diagnostic, "message")),
			})
		}
	}
	return data, true
}

func formatWorkflowAgentEditPrompt(data WorkflowEditPromptData) string {
	return renderRegisteredPromptText("event.workflow_edit", newWorkflowEditPromptInput(data))
}

func workflowDiagnosticLocation(diagnostic map[string]any) string {
	parts := make([]string, 0, 3)
	if line, ok := promptPositiveInteger(diagnostic["line"]); ok {
		parts = append(parts, fmt.Sprintf("line %d", line))
	}
	if column, ok := promptPositiveInteger(diagnostic["column"]); ok {
		parts = append(parts, fmt.Sprintf("column %d", column))
	}
	if nodeID := promptStringMapValue(diagnostic, "nodeId"); nodeID != "" {
		parts = append(parts, "node "+nodeID)
	}
	return strings.Join(parts, ", ")
}

func compactPromptLine(value string) string {
	return strings.Join(strings.Fields(value), " ")
}

func promptPositiveInteger(value any) (int64, bool) {
	switch typed := value.(type) {
	case float64:
		result := int64(typed)
		return result, result > 0 && float64(result) == typed
	case int:
		return int64(typed), typed > 0
	case int64:
		return typed, typed > 0
	default:
		return 0, false
	}
}
