package newagenttools

import (
	"encoding/json"
	"strings"

	"github.com/LoveLosita/smartflow/backend/newAgent/tools/schedule"
	toolcontextresult "github.com/LoveLosita/smartflow/backend/newAgent/tools/tool_context_result"
)

type contextToolsAddResult struct {
	Tool      string   `json:"tool"`
	Success   bool     `json:"success"`
	Action    string   `json:"action"`
	Domain    string   `json:"domain,omitempty"`
	Packs     []string `json:"packs,omitempty"`
	Mode      string   `json:"mode,omitempty"`
	Message   string   `json:"message,omitempty"`
	Error     string   `json:"error,omitempty"`
	ErrorCode string   `json:"error_code,omitempty"`
}

type contextToolsRemoveResult struct {
	Tool      string   `json:"tool"`
	Success   bool     `json:"success"`
	Action    string   `json:"action"`
	Domain    string   `json:"domain,omitempty"`
	Packs     []string `json:"packs,omitempty"`
	All       bool     `json:"all,omitempty"`
	Message   string   `json:"message,omitempty"`
	Error     string   `json:"error,omitempty"`
	ErrorCode string   `json:"error_code,omitempty"`
}

// NewContextToolsAddHandler 创建 context_tools_add 工具。
//
// 职责边界：
// 1. 这里只负责校验 domain / packs / mode，并产出结构化结果；
// 2. 不直接改 CommonState，真正的激活切流仍由 execute 层读取 observation 后更新快照；
// 3. 因为这里拿不到 CommonState，所以卡片展示的是“本次工具结果返回的 domain/packs/mode”，不是全局最终快照。
func NewContextToolsAddHandler() ToolHandler {
	return func(state *schedule.ScheduleState, args map[string]any) ToolExecutionResult {
		_ = state

		domain := NormalizeToolDomain(readContextToolString(args["domain"]))
		if domain == "" {
			return buildContextToolsAddExecutionResult(args, contextToolsAddResult{
				Tool:      ToolNameContextToolsAdd,
				Success:   false,
				Action:    "reject",
				Error:     "参数非法：domain 仅支持 schedule/taskclass",
				ErrorCode: "invalid_domain",
			})
		}

		mode := strings.ToLower(strings.TrimSpace(readContextToolString(args["mode"])))
		if mode == "" {
			mode = "replace"
		}
		if mode != "replace" && mode != "merge" {
			return buildContextToolsAddExecutionResult(args, contextToolsAddResult{
				Tool:      ToolNameContextToolsAdd,
				Success:   false,
				Action:    "reject",
				Domain:    domain,
				Error:     "参数非法：mode 仅支持 replace/merge",
				ErrorCode: "invalid_mode",
			})
		}

		packsRaw := readContextToolStringSlice(args["packs"])
		packs, errCode, errText := validateContextPacks(domain, packsRaw, false)
		if errCode != "" {
			return buildContextToolsAddExecutionResult(args, contextToolsAddResult{
				Tool:      ToolNameContextToolsAdd,
				Success:   false,
				Action:    "reject",
				Domain:    domain,
				Error:     errText,
				ErrorCode: errCode,
			})
		}

		// 1. schedule 未显式传 packs 时，默认激活最小可用包 mutation+analyze。
		// 2. taskclass 当前没有可选包，所以这里会保持空切片，由 execute 层只保留固定 core。
		// 3. 这样做可以让 observation 直接表达“本次实际生效的可选包集合”，减少展示层再二次猜测。
		if domain == ToolDomainSchedule && len(packsRaw) == 0 {
			packs = ResolveEffectiveToolPacks(domain, nil)
		}

		return buildContextToolsAddExecutionResult(args, contextToolsAddResult{
			Tool:    ToolNameContextToolsAdd,
			Success: true,
			Action:  "activate",
			Domain:  domain,
			Packs:   packs,
			Mode:    mode,
			Message: "已激活目标工具域，可继续调用对应业务工具。",
		})
	}
}

// NewContextToolsRemoveHandler 创建 context_tools_remove 工具。
//
// 职责边界：
// 1. 这里只解释 domain / packs / all 的语义，并返回结构化结果；
// 2. all=true 表示清空全部业务工具域，domain+packs 表示移除某域下的可选包；
// 3. 实际 CommonState 的域/包更新，仍由 execute 层统一消费 observation 完成。
func NewContextToolsRemoveHandler() ToolHandler {
	return func(state *schedule.ScheduleState, args map[string]any) ToolExecutionResult {
		_ = state

		all := readContextToolBool(args["all"])
		domainRaw := strings.ToLower(strings.TrimSpace(readContextToolString(args["domain"])))
		packsRaw := readContextToolStringSlice(args["packs"])

		// 兼容旧写法：domain=all 也视为清空全部业务工具域。
		if domainRaw == "all" {
			all = true
		}
		if all {
			return buildContextToolsRemoveExecutionResult(args, contextToolsRemoveResult{
				Tool:    ToolNameContextToolsRemove,
				Success: true,
				Action:  "clear_all",
				All:     true,
				Message: "已移除全部业务工具域，仅保留 context 管理工具。",
			})
		}

		domain := NormalizeToolDomain(domainRaw)
		if domain == "" {
			return buildContextToolsRemoveExecutionResult(args, contextToolsRemoveResult{
				Tool:      ToolNameContextToolsRemove,
				Success:   false,
				Action:    "reject",
				Error:     "参数非法：需要提供 domain=schedule/taskclass 或 all=true",
				ErrorCode: "invalid_domain",
			})
		}

		packs, errCode, errText := validateContextPacks(domain, packsRaw, true)
		if errCode != "" {
			return buildContextToolsRemoveExecutionResult(args, contextToolsRemoveResult{
				Tool:      ToolNameContextToolsRemove,
				Success:   false,
				Action:    "reject",
				Domain:    domain,
				Error:     errText,
				ErrorCode: errCode,
			})
		}

		if len(packs) > 0 {
			return buildContextToolsRemoveExecutionResult(args, contextToolsRemoveResult{
				Tool:    ToolNameContextToolsRemove,
				Success: true,
				Action:  "deactivate_packs",
				Domain:  domain,
				Packs:   packs,
				Message: "已移除指定工具包。",
			})
		}

		return buildContextToolsRemoveExecutionResult(args, contextToolsRemoveResult{
			Tool:    ToolNameContextToolsRemove,
			Success: true,
			Action:  "deactivate",
			Domain:  domain,
			Message: "已移除指定工具域。",
		})
	}
}

func buildContextToolsAddExecutionResult(args map[string]any, payload contextToolsAddResult) ToolExecutionResult {
	observation := marshalContextToolsAddResult(payload)
	legacy := LegacyResult(ToolNameContextToolsAdd, args, observation)
	view := toolcontextresult.BuildAddView(toContextToolsAddPayload(payload), observation)
	return buildContextToolExecutionResult(legacy, args, view)
}

func buildContextToolsRemoveExecutionResult(args map[string]any, payload contextToolsRemoveResult) ToolExecutionResult {
	observation := marshalContextToolsRemoveResult(payload)
	legacy := LegacyResult(ToolNameContextToolsRemove, args, observation)
	view := toolcontextresult.BuildRemoveView(toContextToolsRemovePayload(payload), observation)
	return buildContextToolExecutionResult(legacy, args, view)
}

// buildContextToolExecutionResult 负责把子包纯展示结构包回 ToolExecutionResult。
//
// 职责边界：
// 1. 只做 ContextResultView -> ToolDisplayView 的协议桥接；
// 2. 不改写 ObservationText，确保模型侧仍消费原始 observation JSON；
// 3. 错误码与错误文案继续复用父包现有 JSON/text 解析逻辑，避免多套失败判定分叉。
func buildContextToolExecutionResult(
	legacy ToolExecutionResult,
	args map[string]any,
	view toolcontextresult.ContextResultView,
) ToolExecutionResult {
	result := legacy
	status := normalizeToolStatus(result.Status)
	if status == "" {
		status = ToolStatusDone
	}

	collapsed := cloneAnyMap(view.Collapsed)
	if collapsed == nil {
		collapsed = make(map[string]any)
	}
	expanded := cloneAnyMap(view.Expanded)
	if expanded == nil {
		expanded = make(map[string]any)
	}

	collapsed["status"] = status
	if _, exists := collapsed["status_label"]; !exists {
		collapsed["status_label"] = resolveToolStatusLabelCN(status)
	}
	if _, exists := expanded["raw_text"]; !exists {
		expanded["raw_text"] = result.ObservationText
	}

	result.Status = status
	result.Success = status == ToolStatusDone
	result.ResultView = &ToolDisplayView{
		ViewType:  strings.TrimSpace(view.ViewType),
		Version:   view.Version,
		Collapsed: collapsed,
		Expanded:  expanded,
	}
	if title, ok := readStringAnyMap(collapsed, "title"); ok {
		result.Summary = title
	}
	if !result.Success {
		errorCode, errorMessage := extractToolErrorInfo(result.ObservationText, status)
		if strings.TrimSpace(result.ErrorCode) == "" {
			result.ErrorCode = strings.TrimSpace(errorCode)
		}
		if strings.TrimSpace(result.ErrorMessage) == "" {
			result.ErrorMessage = strings.TrimSpace(errorMessage)
		}
	}
	return EnsureToolResultDefaults(result, args)
}

func toContextToolsAddPayload(payload contextToolsAddResult) toolcontextresult.ContextToolsAddPayload {
	return toolcontextresult.ContextToolsAddPayload{
		Tool:      payload.Tool,
		Success:   payload.Success,
		Action:    payload.Action,
		Domain:    payload.Domain,
		Packs:     append([]string(nil), payload.Packs...),
		Mode:      payload.Mode,
		Message:   payload.Message,
		Error:     payload.Error,
		ErrorCode: payload.ErrorCode,
	}
}

func toContextToolsRemovePayload(payload contextToolsRemoveResult) toolcontextresult.ContextToolsRemovePayload {
	return toolcontextresult.ContextToolsRemovePayload{
		Tool:      payload.Tool,
		Success:   payload.Success,
		Action:    payload.Action,
		Domain:    payload.Domain,
		Packs:     append([]string(nil), payload.Packs...),
		All:       payload.All,
		Message:   payload.Message,
		Error:     payload.Error,
		ErrorCode: payload.ErrorCode,
	}
}

func validateContextPacks(domain string, packs []string, forRemove bool) ([]string, string, string) {
	normalizedDomain := NormalizeToolDomain(domain)
	if normalizedDomain == "" {
		return nil, "invalid_domain", "参数非法：domain 非法"
	}
	if len(packs) == 0 {
		return nil, "", ""
	}

	if normalizedDomain == ToolDomainTaskClass {
		return nil, "unsupported_packs_for_domain", "参数非法：taskclass 暂不支持 packs"
	}

	normalized := make([]string, 0, len(packs))
	seen := make(map[string]struct{}, len(packs))
	for _, raw := range packs {
		trimmed := strings.TrimSpace(raw)
		if trimmed == "" {
			continue
		}
		pack := NormalizeToolPack(normalizedDomain, trimmed)
		if pack == "" {
			return nil, "invalid_pack", "参数非法：存在不支持的 pack"
		}
		if IsFixedToolPack(normalizedDomain, pack) {
			if forRemove {
				return nil, "fixed_pack_forbidden", "参数非法：core 为固定包，不允许 remove"
			}
			return nil, "fixed_pack_forbidden", "参数非法：core 为固定包，不允许 add"
		}
		if _, exists := seen[pack]; exists {
			continue
		}
		seen[pack] = struct{}{}
		normalized = append(normalized, pack)
	}
	if len(normalized) == 0 {
		return nil, "invalid_pack", "参数非法：packs 为空或无效"
	}
	return normalized, "", ""
}

func readContextToolString(raw any) string {
	text, _ := raw.(string)
	return strings.TrimSpace(text)
}

func readContextToolStringSlice(raw any) []string {
	switch typed := raw.(type) {
	case []string:
		out := make([]string, 0, len(typed))
		for _, item := range typed {
			text := strings.TrimSpace(item)
			if text == "" {
				continue
			}
			out = append(out, text)
		}
		return out
	case []any:
		out := make([]string, 0, len(typed))
		for _, item := range typed {
			text, ok := item.(string)
			if !ok {
				continue
			}
			text = strings.TrimSpace(text)
			if text == "" {
				continue
			}
			out = append(out, text)
		}
		return out
	case string:
		text := strings.TrimSpace(typed)
		if text == "" {
			return nil
		}
		parts := strings.Split(text, ",")
		out := make([]string, 0, len(parts))
		for _, part := range parts {
			part = strings.TrimSpace(part)
			if part == "" {
				continue
			}
			out = append(out, part)
		}
		return out
	default:
		return nil
	}
}

func readContextToolBool(raw any) bool {
	switch v := raw.(type) {
	case bool:
		return v
	case string:
		value := strings.ToLower(strings.TrimSpace(v))
		return value == "1" || value == "true" || value == "yes"
	case float64:
		return v != 0
	case float32:
		return v != 0
	case int:
		return v != 0
	case int8:
		return v != 0
	case int16:
		return v != 0
	case int32:
		return v != 0
	case int64:
		return v != 0
	default:
		return false
	}
}

func marshalContextToolsAddResult(result contextToolsAddResult) string {
	raw, err := json.Marshal(result)
	if err != nil {
		return `{"tool":"context_tools_add","success":false,"action":"reject","error":"result encode failed","error_code":"encode_failed"}`
	}
	return string(raw)
}

func marshalContextToolsRemoveResult(result contextToolsRemoveResult) string {
	raw, err := json.Marshal(result)
	if err != nil {
		return `{"tool":"context_tools_remove","success":false,"action":"reject","error":"result encode failed","error_code":"encode_failed"}`
	}
	return string(raw)
}
