# frozen_string_literal: true

module MCP
  module Methods
    INITIALIZE = "initialize"
    PING = "ping"
    LOGGING_SET_LEVEL = "logging/setLevel"
    # Sessionless capability discovery (MCP 2026-07-28 draft, SEP-2575).
    SERVER_DISCOVER = "server/discover"

    PROMPTS_GET = "prompts/get"
    PROMPTS_LIST = "prompts/list"
    COMPLETION_COMPLETE = "completion/complete"

    RESOURCES_LIST = "resources/list"
    RESOURCES_READ = "resources/read"
    RESOURCES_TEMPLATES_LIST = "resources/templates/list"
    RESOURCES_SUBSCRIBE = "resources/subscribe"
    RESOURCES_UNSUBSCRIBE = "resources/unsubscribe"

    TOOLS_CALL = "tools/call"
    TOOLS_LIST = "tools/list"

    ROOTS_LIST = "roots/list"
    SAMPLING_CREATE_MESSAGE = "sampling/createMessage"
    ELICITATION_CREATE = "elicitation/create"

    # Notification methods
    NOTIFICATIONS_INITIALIZED = "notifications/initialized"
    NOTIFICATIONS_TOOLS_LIST_CHANGED = "notifications/tools/list_changed"
    NOTIFICATIONS_PROMPTS_LIST_CHANGED = "notifications/prompts/list_changed"
    NOTIFICATIONS_RESOURCES_LIST_CHANGED = "notifications/resources/list_changed"
    NOTIFICATIONS_RESOURCES_UPDATED = "notifications/resources/updated"
    NOTIFICATIONS_ROOTS_LIST_CHANGED = "notifications/roots/list_changed"
    NOTIFICATIONS_MESSAGE = "notifications/message"
    NOTIFICATIONS_PROGRESS = "notifications/progress"
    NOTIFICATIONS_CANCELLED = "notifications/cancelled"
    NOTIFICATIONS_ELICITATION_COMPLETE = "notifications/elicitation/complete"

    class MissingRequiredCapabilityError < StandardError
      attr_reader :method
      attr_reader :capability

      def initialize(method, capability)
        super("Server does not support #{capability} (required for #{method})")
        @method = method
        @capability = capability
      end
    end

    class << self
      def notification?(method)
        method.is_a?(String) && method.start_with?("notifications/")
      end

      def ensure_capability!(method, capabilities)
        case method
        when PROMPTS_GET, PROMPTS_LIST
          require_capability!(method, capabilities, :prompts)
        when NOTIFICATIONS_PROMPTS_LIST_CHANGED
          require_capability!(method, capabilities, :prompts)
          require_capability!(method, capabilities, :prompts, :listChanged)
        when RESOURCES_LIST, RESOURCES_TEMPLATES_LIST, RESOURCES_READ
          require_capability!(method, capabilities, :resources)
        when NOTIFICATIONS_RESOURCES_LIST_CHANGED
          require_capability!(method, capabilities, :resources)
          require_capability!(method, capabilities, :resources, :listChanged)
        when RESOURCES_SUBSCRIBE, RESOURCES_UNSUBSCRIBE, NOTIFICATIONS_RESOURCES_UPDATED
          require_capability!(method, capabilities, :resources)
          require_capability!(method, capabilities, :resources, :subscribe)
        when TOOLS_CALL, TOOLS_LIST
          require_capability!(method, capabilities, :tools)
        when NOTIFICATIONS_TOOLS_LIST_CHANGED
          require_capability!(method, capabilities, :tools)
          require_capability!(method, capabilities, :tools, :listChanged)
        when LOGGING_SET_LEVEL, NOTIFICATIONS_MESSAGE
          require_capability!(method, capabilities, :logging)
        when COMPLETION_COMPLETE
          require_capability!(method, capabilities, :completions)
        when ROOTS_LIST
          require_capability!(method, capabilities, :roots)
        when SAMPLING_CREATE_MESSAGE
          require_capability!(method, capabilities, :sampling)
        when ELICITATION_CREATE
          require_capability!(method, capabilities, :elicitation)
        when INITIALIZE, PING, SERVER_DISCOVER, NOTIFICATIONS_INITIALIZED, NOTIFICATIONS_ROOTS_LIST_CHANGED,
             NOTIFICATIONS_PROGRESS, NOTIFICATIONS_CANCELLED, NOTIFICATIONS_ELICITATION_COMPLETE
          # No specific capability required.
        end
      end

      private

      def require_capability!(method, capabilities, *keys)
        name = keys.join(".") # :resources, :subscribe -> "resources.subscribe"
        has_capability = capabilities.dig(*keys)
        return if has_capability

        raise MissingRequiredCapabilityError.new(method, name)
      end
    end
  end
end
