# frozen_string_literal: true

module Mcp
  module Tools
    module Base
      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
end
