package rpc

import (
	"encoding/json"
	"fmt"
	"sync"
)

// JSON-RPC 2.0 and ACP error codes.
const (
	CodeParseError           = -32700
	CodeInvalidRequest       = -32600
	CodeMethodNotFound       = -32601
	CodeInvalidParams        = -32602
	CodeInternalError        = -32603
	CodeAuthenticationFailed = -32000
	CodeResourceNotFound     = -32002
)

// MethodHandler handles a specific JSON-RPC method.
type MethodHandler func(params json.RawMessage) (interface{}, error)

// Route represents a registered method route.
type Route struct {
	Method    string
	Handler   MethodHandler
	IsRequest bool // true for requests, false for notifications
	Optional  bool // if true, missing handler is not an error
}

// Router dispatches JSON-RPC methods to registered handlers.
type Router struct {
	mu     sync.RWMutex
	routes map[string]*Route
}

// NewRouter creates a new method router.
func NewRouter() *Router {
	return &Router{
		routes: make(map[string]*Route),
	}
}

// HandleRequest registers a handler for incoming requests.
func (r *Router) HandleRequest(method string, handler MethodHandler) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.routes[method] = &Route{
		Method:    method,
		Handler:   handler,
		IsRequest: true,
	}
}

// HandleNotification registers a handler for incoming notifications.
func (r *Router) HandleNotification(method string, handler MethodHandler) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.routes[method] = &Route{
		Method:    method,
		Handler:   handler,
		IsRequest: false,
	}
}

// HandleOptionalRequest registers an optional handler for incoming requests.
// If no handler is registered for the method, a method-not-found error is returned.
func (r *Router) HandleOptionalRequest(method string, handler MethodHandler) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.routes[method] = &Route{
		Method:    method,
		Handler:   handler,
		IsRequest: true,
		Optional:  true,
	}
}

// Lookup finds the route for a given method.
func (r *Router) Lookup(method string) (*Route, bool) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	route, ok := r.routes[method]
	return route, ok
}

// MakeHandler creates a Connection handler from this router.
func (r *Router) MakeHandler() Handler {
	return func(method string, params json.RawMessage, isRequest bool) (interface{}, error) {
		route, ok := r.Lookup(method)
		if !ok {
			if isRequest {
				return nil, NewRPCError(int(CodeMethodNotFound),
					fmt.Sprintf("Method not found: %s", method))
			}
			return nil, nil
		}
		return route.Handler(params)
	}
}
