package newagentexecute

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

	newagentmodel "github.com/LoveLosita/smartflow/backend/newAgent/model"
	newagenttools "github.com/LoveLosita/smartflow/backend/newAgent/tools"
)

func shouldForceFeasibilityNegotiation(
	flowState *newagentmodel.CommonState,
	registry *newagenttools.ToolRegistry,
	toolName string,
) bool {
	if flowState == nil || registry == nil {
		return false
	}
	if !flowState.HealthCheckDone || flowState.HealthIsFeasible {
		return false
	}
	if !registry.IsWriteTool(toolName) || !registry.RequiresScheduleState(toolName) {
		return false
	}
	return true
}

func buildInfeasibleNegotiationQuestion(flowState *newagentmodel.CommonState) string {
	capacityGap := 0
	reasonCode := "capacity_insufficient"
	if flowState != nil {
		capacityGap = flowState.HealthCapacityGap
		if strings.TrimSpace(flowState.HealthReasonCode) != "" {
			reasonCode = strings.TrimSpace(flowState.HealthReasonCode)
		}
	}
	return fmt.Sprintf(
		"当前计划不可行：analyze_health 判断当前约束不可行（capacity_gap=%d，reason=%s）。在继续写操作前，请先与用户协商：扩展时间窗、放宽约束、缩减范围或预算，或接受风险收口。",
		capacityGap,
		reasonCode,
	)
}

func buildInfeasibleBlockedResult(flowState *newagentmodel.CommonState) string {
	capacityGap := 0
	reasonCode := "capacity_insufficient"
	if flowState != nil {
		capacityGap = flowState.HealthCapacityGap
		if strings.TrimSpace(flowState.HealthReasonCode) != "" {
			reasonCode = strings.TrimSpace(flowState.HealthReasonCode)
		}
	}
	return fmt.Sprintf(
		"已阻断本次写操作：analyze_health 判定当前约束不可行（capacity_gap=%d，reason=%s）。请先与用户协商：扩展时间窗 / 放宽约束 / 缩减范围或预算 / 接受风险收口。",
		capacityGap,
		reasonCode,
	)
}

type contextToolsResultEnvelope struct {
	Tool    string   `json:"tool"`
	Success bool     `json:"success"`
	Domain  string   `json:"domain,omitempty"`
	Packs   []string `json:"packs,omitempty"`
	Mode    string   `json:"mode,omitempty"`
	All     bool     `json:"all,omitempty"`
}

type analyzeHealthResultEnvelope struct {
	Tool        string                         `json:"tool"`
	Success     bool                           `json:"success"`
	Feasibility *analyzeHealthFeasibilityBrief `json:"feasibility,omitempty"`
	Decision    *analyzeHealthDecisionBrief    `json:"decision,omitempty"`
}

type analyzeHealthFeasibilityBrief struct {
	IsFeasible  bool   `json:"is_feasible"`
	CapacityGap int    `json:"capacity_gap"`
	ReasonCode  string `json:"reason_code"`
}

type analyzeHealthDecisionBrief struct {
	ShouldContinueOptimize bool   `json:"should_continue_optimize"`
	PrimaryProblem         string `json:"primary_problem,omitempty"`
	RecommendedOperation   string `json:"recommended_operation,omitempty"`
	IsForcedImperfection   bool   `json:"is_forced_imperfection"`
	ImprovementSignal      string `json:"improvement_signal,omitempty"`
}

type upsertTaskClassResultEnvelope struct {
	Tool       string                         `json:"tool"`
	Success    bool                           `json:"success"`
	Validation *upsertTaskClassValidationPart `json:"validation,omitempty"`
	Error      string                         `json:"error,omitempty"`
	ErrorCode  string                         `json:"error_code,omitempty"`
}

type upsertTaskClassValidationPart struct {
	OK     bool     `json:"ok"`
	Issues []string `json:"issues"`
}

func updateActiveToolDomainSnapshot(flowState *newagentmodel.CommonState, toolName string, result string) {
	if flowState == nil || !newagenttools.IsContextManagementTool(toolName) {
		return
	}

	var envelope contextToolsResultEnvelope
	if err := json.Unmarshal([]byte(result), &envelope); err != nil {
		return
	}
	if !envelope.Success {
		return
	}

	switch strings.TrimSpace(toolName) {
	case newagenttools.ToolNameContextToolsAdd:
		domain := newagenttools.NormalizeToolDomain(envelope.Domain)
		if domain == "" {
			return
		}
		nextPacks := newagenttools.ResolveEffectiveToolPacks(domain, envelope.Packs)
		mode := strings.ToLower(strings.TrimSpace(envelope.Mode))
		if mode == "merge" && newagenttools.NormalizeToolDomain(flowState.ActiveToolDomain) == domain {
			merged := make([]string, 0, len(flowState.ActiveToolPacks)+len(nextPacks))
			seen := make(map[string]struct{}, len(flowState.ActiveToolPacks)+len(nextPacks))
			current := newagenttools.ResolveEffectiveToolPacks(domain, flowState.ActiveToolPacks)
			for _, pack := range current {
				if _, exists := seen[pack]; exists {
					continue
				}
				seen[pack] = struct{}{}
				merged = append(merged, pack)
			}
			for _, pack := range nextPacks {
				if _, exists := seen[pack]; exists {
					continue
				}
				seen[pack] = struct{}{}
				merged = append(merged, pack)
			}
			nextPacks = merged
		}
		flowState.ActiveToolDomain = domain
		flowState.ActiveToolPacks = nextPacks
	case newagenttools.ToolNameContextToolsRemove:
		if envelope.All {
			flowState.ActiveToolDomain = ""
			flowState.ActiveToolPacks = nil
			return
		}
		domain := newagenttools.NormalizeToolDomain(envelope.Domain)
		if domain == "" {
			return
		}
		currentDomain := newagenttools.NormalizeToolDomain(flowState.ActiveToolDomain)
		if currentDomain != domain {
			return
		}

		removedPacks := newagenttools.NormalizeToolPacks(domain, envelope.Packs)
		if len(removedPacks) == 0 {
			flowState.ActiveToolDomain = ""
			flowState.ActiveToolPacks = nil
			return
		}

		currentEffective := newagenttools.ResolveEffectiveToolPacks(domain, flowState.ActiveToolPacks)
		if len(currentEffective) == 0 {
			flowState.ActiveToolDomain = ""
			flowState.ActiveToolPacks = nil
			return
		}

		removedSet := make(map[string]struct{}, len(removedPacks))
		for _, pack := range removedPacks {
			removedSet[pack] = struct{}{}
		}
		remaining := make([]string, 0, len(currentEffective))
		for _, pack := range currentEffective {
			if _, shouldRemove := removedSet[pack]; shouldRemove {
				continue
			}
			remaining = append(remaining, pack)
		}
		if len(remaining) == 0 {
			flowState.ActiveToolDomain = ""
			flowState.ActiveToolPacks = nil
			return
		}
		flowState.ActiveToolPacks = remaining
	}
}

func updateHealthFeasibilitySnapshot(flowState *newagentmodel.CommonState, toolName string, result string) {
	if flowState == nil || !strings.EqualFold(strings.TrimSpace(toolName), toolAnalyzeHealth) {
		return
	}

	flowState.HealthCheckDone = false
	flowState.HealthIsFeasible = true
	flowState.HealthCapacityGap = 0
	flowState.HealthReasonCode = ""

	var envelope analyzeHealthResultEnvelope
	if err := json.Unmarshal([]byte(result), &envelope); err != nil {
		return
	}
	if !envelope.Success || envelope.Feasibility == nil {
		return
	}

	flowState.HealthCheckDone = true
	flowState.HealthIsFeasible = envelope.Feasibility.IsFeasible
	flowState.HealthCapacityGap = envelope.Feasibility.CapacityGap
	flowState.HealthReasonCode = strings.TrimSpace(envelope.Feasibility.ReasonCode)
}

func updateTaskClassUpsertSnapshot(flowState *newagentmodel.CommonState, toolName string, result string) {
	if flowState == nil || !strings.EqualFold(strings.TrimSpace(toolName), "upsert_task_class") {
		return
	}

	flowState.TaskClassUpsertLastTried = true
	flowState.TaskClassUpsertLastSuccess = false
	flowState.TaskClassUpsertLastIssues = nil

	var envelope upsertTaskClassResultEnvelope
	if err := json.Unmarshal([]byte(result), &envelope); err != nil {
		flowState.TaskClassUpsertConsecutiveFailures++
		return
	}

	success := envelope.Success
	issues := make([]string, 0)
	if envelope.Validation != nil {
		issues = append(issues, parseAnyToStringSlice(any(envelope.Validation.Issues))...)
		if !envelope.Validation.OK {
			success = false
		}
	}
	if !success && strings.TrimSpace(envelope.Error) != "" && len(issues) == 0 {
		issues = append(issues, strings.TrimSpace(envelope.Error))
	}
	issues = uniqueNonEmptyStrings(issues)

	flowState.TaskClassUpsertLastSuccess = success
	flowState.TaskClassUpsertLastIssues = issues
	if success {
		flowState.TaskClassUpsertConsecutiveFailures = 0
		return
	}
	flowState.TaskClassUpsertConsecutiveFailures++
}

func uniqueNonEmptyStrings(values []string) []string {
	if len(values) == 0 {
		return nil
	}
	seen := make(map[string]struct{}, len(values))
	result := make([]string, 0, len(values))
	for _, value := range values {
		text := strings.TrimSpace(value)
		if text == "" {
			continue
		}
		if _, exists := seen[text]; exists {
			continue
		}
		seen[text] = struct{}{}
		result = append(result, text)
	}
	return result
}

func updateHealthSnapshotV2(flowState *newagentmodel.CommonState, toolName string, result string) {
	if flowState == nil || !strings.EqualFold(strings.TrimSpace(toolName), toolAnalyzeHealth) {
		return
	}

	prevSignal := strings.TrimSpace(flowState.HealthImprovementSignal)
	flowState.HealthCheckDone = false
	flowState.HealthIsFeasible = true
	flowState.HealthCapacityGap = 0
	flowState.HealthReasonCode = ""
	flowState.HealthShouldContinueOptimize = false
	flowState.HealthTightnessLevel = ""
	flowState.HealthPrimaryProblem = ""
	flowState.HealthRecommendedOperation = ""
	flowState.HealthIsForcedImperfection = false
	flowState.HealthImprovementSignal = ""

	var envelope struct {
		Success     bool                           `json:"success"`
		Feasibility *analyzeHealthFeasibilityBrief `json:"feasibility,omitempty"`
		Metrics     struct {
			Tightness *struct {
				TightnessLevel string `json:"tightness_level"`
			} `json:"tightness,omitempty"`
		} `json:"metrics"`
		Decision *analyzeHealthDecisionBrief `json:"decision,omitempty"`
	}
	if err := json.Unmarshal([]byte(result), &envelope); err != nil {
		flowState.HealthStagnationCount = 0
		return
	}
	if !envelope.Success || envelope.Feasibility == nil {
		flowState.HealthStagnationCount = 0
		return
	}

	flowState.HealthCheckDone = true
	flowState.HealthIsFeasible = envelope.Feasibility.IsFeasible
	flowState.HealthCapacityGap = envelope.Feasibility.CapacityGap
	flowState.HealthReasonCode = strings.TrimSpace(envelope.Feasibility.ReasonCode)
	if envelope.Metrics.Tightness != nil {
		flowState.HealthTightnessLevel = strings.TrimSpace(envelope.Metrics.Tightness.TightnessLevel)
	}
	if envelope.Decision != nil {
		flowState.HealthShouldContinueOptimize = envelope.Decision.ShouldContinueOptimize
		flowState.HealthPrimaryProblem = strings.TrimSpace(envelope.Decision.PrimaryProblem)
		flowState.HealthRecommendedOperation = strings.TrimSpace(envelope.Decision.RecommendedOperation)
		flowState.HealthIsForcedImperfection = envelope.Decision.IsForcedImperfection
		flowState.HealthImprovementSignal = strings.TrimSpace(envelope.Decision.ImprovementSignal)
	}
	if signal := strings.TrimSpace(flowState.HealthImprovementSignal); signal != "" && prevSignal != "" && signal == prevSignal {
		flowState.HealthStagnationCount++
		return
	}
	flowState.HealthStagnationCount = 0
}
