docs: def reply_to_ticket(ticket_id, content, user_id=None):
      """Create a structured reply to a ticket.
      
      :param int ticket_id: The ticket to reply to.
      :param content: ``str``, ``dict``, or iterator of ``(title, content``) pairs.
      :returns: ``(reply_id, reply_content)``.
      
      """
      
      user_comment = None
      exc_type = None
  
      if isinstance(content, dict):
          content = sorted(content.iteritems())
  
      if not isinstance(content, basestring):
          parts = []
          for heading, part in content:
              
              # This one gets special cased into a Reply.
              if heading.lower() == 'user comment':
                  user_comment = part
  
              # Mappings.
              if isinstance(part, dict):
                  parts.append((heading, '\n'.join('%s = %r' % x
                      for x in sorted(part.iteritems())
                  )))
              
              # Iterators.
              elif isinstance(part, (list, tuple)):
                  
                  # Exceptions.
                  if (len(part) == 3 and
                      issubclass(part[0], Exception) and
                      isinstance(part[1], Exception) and
                      isinstance(part[2], types.TracebackType)
                  ):
                      exc_type = part[0]
                      parts.append((heading, '\n'.join(traceback.format_exception(*part)).rstrip()))
                  
                  # Iterators.
                  else:
                      parts.append((heading, '\n'.join('- %r' % x for x in part)))
              
              # The rest.
              else:
                  parts.append((heading, str(part)))
          
          # Join the parts.
          content = '\n\n'.join('%s\n%s\n%s' % (heading, '=' * len(heading), part) for heading, part in parts)
      
      # Get the Shotgun and Project.
      # TODO: Somehow pass this in later.
      shotgun = get_shotgun()
  
      # Convert requested user ID to a login.
      if user_id:
          user = shotgun.find_one('HumanUser', [('id', 'is', user_id)], ['login'])
          if user:
              shotgun.config.sudo_as_login = user['login']
  
      # Make sure we do have a login.
      if not shotgun.config.sudo_as_login:
          user = shotgun.find_one('HumanUser', [('email', 'starts_with', os.getlogin() + '@')], ['login'])
          if user:
              shotgun.config.sudo_as_login = user['login']
      
      reply = None
  
      if user_comment:
          reply = shotgun.create('Reply', {
              'content': user_comment,
              'entity': {'type': 'Ticket', 'id': ticket_id},
          })
  
      if content.strip():
          with tempfile.NamedTemporaryFile(suffix='ticket-%d-reply.txt' % ticket_id) as fh:
              fh.write(content)
              fh.flush()
              shotgun.upload('Ticket', ticket_id, fh.name, 'attachments',
                 display_name='Ticket-%d.%s.%s.txt' % (
                      ticket_id,
                      exc_type.__name__ if exc_type else 'details',
                      datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S'),
                  ),
              )
  
      return reply and reply['id'], content