package api

import (
	"context"
	"encoding/json"
	"net/http"
	"strings"

	rpcpkg "github.com/YspCoder/clawgo/pkg/rpc"
	"github.com/YspCoder/clawgo/pkg/tools"
)

func (s *Server) handleSubagentRPC(w http.ResponseWriter, r *http.Request) {
	s.handleRPC(w, r, s.subagentRPCRegistry())
}

func (s *Server) handleNodeRPC(w http.ResponseWriter, r *http.Request) {
	s.handleRPC(w, r, s.nodeRPCRegistry())
}

func (s *Server) handleProviderRPC(w http.ResponseWriter, r *http.Request) {
	s.handleRPC(w, r, s.providerRPCRegistry())
}

func (s *Server) handleWorkspaceRPC(w http.ResponseWriter, r *http.Request) {
	s.handleRPC(w, r, s.workspaceRPCRegistry())
}

func (s *Server) handleConfigRPC(w http.ResponseWriter, r *http.Request) {
	s.handleRPC(w, r, s.configRPCRegistry())
}

func (s *Server) handleCronRPC(w http.ResponseWriter, r *http.Request) {
	s.handleRPC(w, r, s.cronRPCRegistry())
}

func (s *Server) handleSkillsRPC(w http.ResponseWriter, r *http.Request) {
	s.handleRPC(w, r, s.skillsRPCRegistry())
}

func (s *Server) handleRPC(w http.ResponseWriter, r *http.Request, registry *rpcpkg.Registry) {
	if !s.checkAuth(r) {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}
	if r.Method != http.MethodPost {
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}
	var req rpcpkg.Request
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeRPCError(w, http.StatusBadRequest, req.RequestID, rpcError("invalid_argument", "invalid json", nil, false))
		return
	}
	result, rpcErr := registry.Handle(r.Context(), req)
	if rpcErr != nil {
		writeRPCError(w, rpcHTTPStatus(rpcErr), req.RequestID, rpcErr)
		return
	}
	writeJSON(w, rpcpkg.Response{
		OK:        true,
		Result:    result,
		RequestID: strings.TrimSpace(req.RequestID),
	})
}

func (s *Server) buildSubagentRegistry() *rpcpkg.Registry {
	svc := s.subagentRPCService()
	reg := rpcpkg.NewRegistry()
	rpcpkg.RegisterJSON(reg, "subagent.list", func(ctx context.Context, req rpcpkg.ListSubagentsRequest) (interface{}, *rpcpkg.Error) {
		return svc.List(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "subagent.snapshot", func(ctx context.Context, req rpcpkg.SnapshotRequest) (interface{}, *rpcpkg.Error) {
		return svc.Snapshot(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "subagent.get", func(ctx context.Context, req rpcpkg.GetSubagentRequest) (interface{}, *rpcpkg.Error) {
		return svc.Get(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "subagent.spawn", func(ctx context.Context, req rpcpkg.SpawnSubagentRequest) (interface{}, *rpcpkg.Error) {
		return svc.Spawn(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "subagent.dispatch_and_wait", func(ctx context.Context, req rpcpkg.DispatchAndWaitRequest) (interface{}, *rpcpkg.Error) {
		return svc.DispatchAndWait(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "subagent.registry", func(ctx context.Context, req rpcpkg.RegistryRequest) (interface{}, *rpcpkg.Error) {
		return svc.Registry(ctx, req)
	})
	return reg
}

func (s *Server) subagentRPCRegistry() *rpcpkg.Registry {
	if s == nil {
		return rpcpkg.NewRegistry()
	}
	s.subagentRPCOnce.Do(func() {
		s.subagentRPCReg = s.buildSubagentRegistry()
	})
	if s.subagentRPCReg == nil {
		return rpcpkg.NewRegistry()
	}
	return s.subagentRPCReg
}

func (s *Server) buildNodeRegistry() *rpcpkg.Registry {
	svc := s.nodeRPCService()
	reg := rpcpkg.NewRegistry()
	rpcpkg.RegisterJSON(reg, "node.register", func(ctx context.Context, req rpcpkg.RegisterNodeRequest) (interface{}, *rpcpkg.Error) {
		return svc.Register(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "node.heartbeat", func(ctx context.Context, req rpcpkg.HeartbeatNodeRequest) (interface{}, *rpcpkg.Error) {
		return svc.Heartbeat(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "node.dispatch", func(ctx context.Context, req rpcpkg.DispatchNodeRequest) (interface{}, *rpcpkg.Error) {
		return svc.Dispatch(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "node.artifact.list", func(ctx context.Context, req rpcpkg.ListNodeArtifactsRequest) (interface{}, *rpcpkg.Error) {
		return svc.ListArtifacts(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "node.artifact.get", func(ctx context.Context, req rpcpkg.GetNodeArtifactRequest) (interface{}, *rpcpkg.Error) {
		return svc.GetArtifact(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "node.artifact.delete", func(ctx context.Context, req rpcpkg.DeleteNodeArtifactRequest) (interface{}, *rpcpkg.Error) {
		return svc.DeleteArtifact(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "node.artifact.prune", func(ctx context.Context, req rpcpkg.PruneNodeArtifactsRequest) (interface{}, *rpcpkg.Error) {
		return svc.PruneArtifacts(ctx, req)
	})
	return reg
}

func (s *Server) nodeRPCRegistry() *rpcpkg.Registry {
	if s == nil {
		return rpcpkg.NewRegistry()
	}
	s.nodeRPCOnce.Do(func() {
		s.nodeRPCReg = s.buildNodeRegistry()
	})
	if s.nodeRPCReg == nil {
		return rpcpkg.NewRegistry()
	}
	return s.nodeRPCReg
}

func (s *Server) buildProviderRegistry() *rpcpkg.Registry {
	svc := s.providerRPCService()
	reg := rpcpkg.NewRegistry()
	rpcpkg.RegisterJSON(reg, "provider.list_models", func(ctx context.Context, req rpcpkg.ListProviderModelsRequest) (interface{}, *rpcpkg.Error) {
		return svc.ListModels(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "provider.models.update", func(ctx context.Context, req rpcpkg.UpdateProviderModelsRequest) (interface{}, *rpcpkg.Error) {
		return svc.UpdateModels(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "provider.chat", func(ctx context.Context, req rpcpkg.ProviderChatRequest) (interface{}, *rpcpkg.Error) {
		return svc.Chat(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "provider.count_tokens", func(ctx context.Context, req rpcpkg.ProviderCountTokensRequest) (interface{}, *rpcpkg.Error) {
		return svc.CountTokens(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "provider.runtime.view", func(ctx context.Context, req rpcpkg.ProviderRuntimeViewRequest) (interface{}, *rpcpkg.Error) {
		return svc.RuntimeView(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "provider.runtime.action", func(ctx context.Context, req rpcpkg.ProviderRuntimeActionRequest) (interface{}, *rpcpkg.Error) {
		return svc.RuntimeAction(ctx, req)
	})
	return reg
}

func (s *Server) providerRPCRegistry() *rpcpkg.Registry {
	if s == nil {
		return rpcpkg.NewRegistry()
	}
	s.providerRPCOnce.Do(func() {
		s.providerRPCReg = s.buildProviderRegistry()
	})
	if s.providerRPCReg == nil {
		return rpcpkg.NewRegistry()
	}
	return s.providerRPCReg
}

func (s *Server) buildWorkspaceRegistry() *rpcpkg.Registry {
	svc := s.workspaceRPCService()
	reg := rpcpkg.NewRegistry()
	rpcpkg.RegisterJSON(reg, "workspace.list_files", func(ctx context.Context, req rpcpkg.ListWorkspaceFilesRequest) (interface{}, *rpcpkg.Error) {
		return svc.ListFiles(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "workspace.read_file", func(ctx context.Context, req rpcpkg.ReadWorkspaceFileRequest) (interface{}, *rpcpkg.Error) {
		return svc.ReadFile(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "workspace.write_file", func(ctx context.Context, req rpcpkg.WriteWorkspaceFileRequest) (interface{}, *rpcpkg.Error) {
		return svc.WriteFile(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "workspace.delete_file", func(ctx context.Context, req rpcpkg.DeleteWorkspaceFileRequest) (interface{}, *rpcpkg.Error) {
		return svc.DeleteFile(ctx, req)
	})
	return reg
}

func (s *Server) workspaceRPCRegistry() *rpcpkg.Registry {
	if s == nil {
		return rpcpkg.NewRegistry()
	}
	s.workspaceRPCOnce.Do(func() {
		s.workspaceRPCReg = s.buildWorkspaceRegistry()
	})
	if s.workspaceRPCReg == nil {
		return rpcpkg.NewRegistry()
	}
	return s.workspaceRPCReg
}

func (s *Server) buildConfigRegistry() *rpcpkg.Registry {
	svc := s.configRPCService()
	reg := rpcpkg.NewRegistry()
	rpcpkg.RegisterJSON(reg, "config.view", func(ctx context.Context, req rpcpkg.ConfigViewRequest) (interface{}, *rpcpkg.Error) {
		return svc.View(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "config.save", func(ctx context.Context, req rpcpkg.ConfigSaveRequest) (interface{}, *rpcpkg.Error) {
		return svc.Save(ctx, req)
	})
	return reg
}

func (s *Server) configRPCRegistry() *rpcpkg.Registry {
	if s == nil {
		return rpcpkg.NewRegistry()
	}
	s.configRPCOnce.Do(func() {
		s.configRPCReg = s.buildConfigRegistry()
	})
	if s.configRPCReg == nil {
		return rpcpkg.NewRegistry()
	}
	return s.configRPCReg
}

func (s *Server) buildCronRegistry() *rpcpkg.Registry {
	svc := s.cronRPCService()
	reg := rpcpkg.NewRegistry()
	rpcpkg.RegisterJSON(reg, "cron.list", func(ctx context.Context, req rpcpkg.ListCronJobsRequest) (interface{}, *rpcpkg.Error) {
		return svc.List(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "cron.get", func(ctx context.Context, req rpcpkg.GetCronJobRequest) (interface{}, *rpcpkg.Error) {
		return svc.Get(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "cron.mutate", func(ctx context.Context, req rpcpkg.MutateCronJobRequest) (interface{}, *rpcpkg.Error) {
		return svc.Mutate(ctx, req)
	})
	return reg
}

func (s *Server) cronRPCRegistry() *rpcpkg.Registry {
	if s == nil {
		return rpcpkg.NewRegistry()
	}
	s.cronRPCOnce.Do(func() {
		s.cronRPCReg = s.buildCronRegistry()
	})
	if s.cronRPCReg == nil {
		return rpcpkg.NewRegistry()
	}
	return s.cronRPCReg
}

func (s *Server) buildSkillsRegistry() *rpcpkg.Registry {
	svc := s.skillsRPCService()
	reg := rpcpkg.NewRegistry()
	rpcpkg.RegisterJSON(reg, "skills.view", func(ctx context.Context, req rpcpkg.SkillsViewRequest) (interface{}, *rpcpkg.Error) {
		return svc.View(ctx, req)
	})
	rpcpkg.RegisterJSON(reg, "skills.mutate", func(ctx context.Context, req rpcpkg.SkillsMutateRequest) (interface{}, *rpcpkg.Error) {
		return svc.Mutate(ctx, req)
	})
	return reg
}

func (s *Server) skillsRPCRegistry() *rpcpkg.Registry {
	if s == nil {
		return rpcpkg.NewRegistry()
	}
	s.skillsRPCOnce.Do(func() {
		s.skillsRPCReg = s.buildSkillsRegistry()
	})
	if s.skillsRPCReg == nil {
		return rpcpkg.NewRegistry()
	}
	return s.skillsRPCReg
}

func writeRPCError(w http.ResponseWriter, status int, requestID string, rpcErr *rpcpkg.Error) {
	if rpcErr == nil {
		rpcErr = rpcError("internal", "rpc error", nil, false)
	}
	writeJSONStatus(w, status, rpcpkg.Response{
		OK:        false,
		Error:     rpcErr,
		RequestID: strings.TrimSpace(requestID),
	})
}

func (s *Server) handleSubagentLegacyAction(ctx context.Context, action string, args map[string]interface{}) (interface{}, *rpcpkg.Error) {
	registry := s.subagentRPCRegistry()
	req := rpcpkg.Request{
		Method: legacySubagentActionMethod(action),
		Params: mustJSONMarshal(mapSubagentLegacyArgs(action, args)),
	}
	result, rpcErr := registry.Handle(ctx, req)
	if rpcErr != nil && !strings.HasPrefix(strings.TrimSpace(req.Method), "subagent.") {
		if s.onSubagents == nil {
			return nil, rpcError("unavailable", "subagent runtime handler not configured", nil, false)
		}
		fallback, err := s.onSubagents(ctx, action, args)
		if err != nil {
			return nil, rpcErrorFrom(err)
		}
		return fallback, nil
	}
	return result, rpcErr
}

var legacySubagentActionMethods = map[string]string{
	"":                  "subagent.list",
	"list":              "subagent.list",
	"snapshot":          "subagent.snapshot",
	"get":               "subagent.get",
	"info":              "subagent.get",
	"spawn":             "subagent.spawn",
	"create":            "subagent.spawn",
	"dispatch_and_wait": "subagent.dispatch_and_wait",
	"registry":          "subagent.registry",
}

func legacySubagentActionMethod(action string) string {
	normalized := strings.ToLower(strings.TrimSpace(action))
	if method, ok := legacySubagentActionMethods[normalized]; ok {
		return method
	}
	return strings.TrimSpace(action)
}

var legacySubagentArgMappers = map[string]func(map[string]interface{}) interface{}{
	"snapshot": func(args map[string]interface{}) interface{} {
		return rpcpkg.SnapshotRequest{Limit: tools.MapIntArg(args, "limit", 0)}
	},
	"get": func(args map[string]interface{}) interface{} {
		return rpcpkg.GetSubagentRequest{ID: tools.MapStringArg(args, "id")}
	},
	"info": func(args map[string]interface{}) interface{} {
		return rpcpkg.GetSubagentRequest{ID: tools.MapStringArg(args, "id")}
	},
	"spawn": buildLegacySpawnSubagentRequest,
	"create": func(args map[string]interface{}) interface{} {
		return buildLegacySpawnSubagentRequest(args)
	},
	"dispatch_and_wait": func(args map[string]interface{}) interface{} {
		return rpcpkg.DispatchAndWaitRequest{
			Task:           tools.MapStringArg(args, "task"),
			Label:          tools.MapStringArg(args, "label"),
			Role:           tools.MapStringArg(args, "role"),
			AgentID:        tools.MapStringArg(args, "agent_id"),
			ThreadID:       tools.MapStringArg(args, "thread_id"),
			CorrelationID:  tools.MapStringArg(args, "correlation_id"),
			ParentRunID:    tools.MapStringArg(args, "parent_run_id"),
			MaxRetries:     tools.MapIntArg(args, "max_retries", 0),
			RetryBackoffMS: tools.MapIntArg(args, "retry_backoff_ms", 0),
			TimeoutSec:     tools.MapIntArg(args, "timeout_sec", 0),
			MaxTaskChars:   tools.MapIntArg(args, "max_task_chars", 0),
			MaxResultChars: tools.MapIntArg(args, "max_result_chars", 0),
			WaitTimeoutSec: tools.MapIntArg(args, "wait_timeout_sec", 0),
			Channel:        firstNonEmptyString(tools.MapStringArg(args, "channel"), tools.MapStringArg(args, "origin_channel")),
			ChatID:         firstNonEmptyString(tools.MapStringArg(args, "chat_id"), tools.MapStringArg(args, "origin_chat_id")),
		}
	},
}

func mapSubagentLegacyArgs(action string, args map[string]interface{}) interface{} {
	normalized := strings.ToLower(strings.TrimSpace(action))
	if mapper, ok := legacySubagentArgMappers[normalized]; ok && mapper != nil {
		return mapper(args)
	}
	return args
}

func buildLegacySpawnSubagentRequest(args map[string]interface{}) interface{} {
	return rpcpkg.SpawnSubagentRequest{
		Task:           tools.MapStringArg(args, "task"),
		Label:          tools.MapStringArg(args, "label"),
		Role:           tools.MapStringArg(args, "role"),
		AgentID:        tools.MapStringArg(args, "agent_id"),
		MaxRetries:     tools.MapIntArg(args, "max_retries", 0),
		RetryBackoffMS: tools.MapIntArg(args, "retry_backoff_ms", 0),
		TimeoutSec:     tools.MapIntArg(args, "timeout_sec", 0),
		MaxTaskChars:   tools.MapIntArg(args, "max_task_chars", 0),
		MaxResultChars: tools.MapIntArg(args, "max_result_chars", 0),
		Channel:        firstNonEmptyString(tools.MapStringArg(args, "channel"), tools.MapStringArg(args, "origin_channel")),
		ChatID:         firstNonEmptyString(tools.MapStringArg(args, "chat_id"), tools.MapStringArg(args, "origin_chat_id")),
	}
}

func mustJSONMarshal(value interface{}) json.RawMessage {
	if value == nil {
		return json.RawMessage([]byte("{}"))
	}
	data, err := json.Marshal(value)
	if err != nil {
		return json.RawMessage([]byte("{}"))
	}
	return data
}
