# frozen_string_literal: true

module Mcp
  module Tools
    class BaseService
      # override this method when renaming tools
      def self.tool_aliases
        []
      end

      def initialize(name:)
        @name = name
      end

      def set_cred(**)
        raise NoMethodError
      end

      def description
        raise NoMethodError
      end

      def input_schema
        raise NoMethodError
      end

      def execute(request: nil, params: nil) # rubocop: disable Lint/UnusedMethodArgument -- request param to match Mcp::Tools::ApiTool
        args = params[:arguments]
        validate_arguments!(args)
        perform(args)
      rescue ArgumentError => e
        Response.error("Validation error: #{e.message}")
      rescue StandardError => e
        Response.error("Tool execution failed: #{e.message}")
      end

      def to_h
        result = {
          name: name,
          description: description,
          inputSchema: input_schema
        }

        result[:icons] = [icons.first] if icons.present?

        tool_annotations = annotations
        result[:annotations] = tool_annotations if tool_annotations.present?

        result
      end

      def icons
        IconConfig.gitlab_icons
      end

      # Tool availability check, returns `true` by default.
      # Tools should override this method if they need to check for specific conditions.
      def available?
        true
      end

      # Returns tool annotations (e.g., readOnly flag for governance controls).
      # Tools should override this method to provide custom annotations.
      # Returns empty hash by default, which will be omitted from to_h output.
      def annotations
        {}
      end

      protected

      def perform(_arguments = {}, _query = {})
        raise NoMethodError
      end

      private

      attr_reader :name, :current_user, :access_token

      def validate_arguments!(arguments)
        schemer = JSONSchemer.schema(input_schema)
        json_arguments = arguments ? arguments.deep_stringify_keys : {}
        errors = schemer.validate(json_arguments).to_a

        return if errors.empty?

        validations = errors.map do |error|
          case error['type']
          when 'required'
            "#{error['details']['missing_keys'].first} is missing"
          when 'maxItems'
            field = error['data_pointer'].sub(%r{^/}, '')
            "#{field} cannot contain more than #{error['schema']['maxItems']} items"
          when 'enum'
            field = error['data_pointer'].sub(%r{^/}, '')
            "Invalid #{field}: '#{error['data']}'. Must be one of: #{error['schema']['enum'].join(', ')}"
          else
            field = error['data_pointer'].sub(%r{^/}, '')
            "#{field} is invalid"
          end
        end.uniq

        raise ArgumentError, validations.join(', ')
      end

      def input_schema_pagination_params
        {
          per_page: {
            type: 'integer',
            description: 'Number of items to list per page. (default: 20)',
            minimum: 1
          },
          page: {
            type: 'integer',
            description: 'Page number to retrieve. (default: 1)',
            minimum: 1
          }
        }
      end

      def version
        raise NoMethodError
      end
    end
  end
end
