text: # Secrets view
  
  import time
  import random
  
  from sqlalchemy import or_, func
  from tabulate import tabulate
  from passwordgenerator import pwgenerator
  
  from ..models.base import get_session
  from ..models.Secret import SecretModel
  from ..modules.misc import confirm, clear_screen
  from ..modules.carry import global_scope
  from ..modules import autocomplete
  from .categories import get_name as get_category_name, pick, all as all_categories
  from . import clipboard, menu
  
  
  def all():
      """
          Return a list of all secrets
      """
  
      return get_session().query(SecretModel).order_by(SecretModel.id).all()
  
  
  def to_table(rows=[]):
      """
          Transform rows in a table
      """
  
      # Retrieve id and name
      all_secrets = [[secret.id, get_category_name(secret.category_id), secret.name,
                      secret.url, secret.login] for secret in rows]
  
      if len(all_secrets) > 0:
          return tabulate(all_secrets, headers=['Item', 'Category', 'Name', 'URL', 'Login'])
      else:
          return 'Empty!'
  
  
  def count():
      """
          Return a count of all secrets
      """
  
      return get_session().query(SecretModel).count()
  
  
  def get_by_id(id_):
      """
          Get a secret by ID
      """
  
      return get_session().query(SecretModel).get(int(id_))
  
  
  def get_names(limit=2000):
      """ Return secret's names for auto-completion """
  
      results = get_session().query(SecretModel.name).\
          filter(SecretModel.name != '').\
          limit(limit).\
          all()
  
      if results:
          return [result.name for result in results]
  
      return []
  
  
  def get_top_logins(limit=10):
      """ Return most popular logins for auto-completion """
  
      count_ = func.count('*')
  
      results = get_session().query(SecretModel.login, count_).\
          filter(SecretModel.login != '').\
          group_by(SecretModel.login).\
          order_by(count_.desc()).\
          limit(limit).\
          all()
  
      if results:
          return [result.login for result in results]
  
      return []
  
  
  def add(name, url='', login='', password='', notes='', category_id=None):
      """
          Create a new secret
      """
  
      secret = SecretModel(name=name,
                           url=url,
                           login=login,
                           password=password,
                           notes=notes,
                           category_id=category_id)
      get_session().add(secret)
      get_session().commit()
  
      return True
  
  
  def add_input():
      """
          Ask user for a secret details and create it
      """
  
      # Clear screen
      clear_screen()
  
      # Ask user input
      category_id = None
      if len(all_categories()) > 0:
          category_id = pick(
              message='* Choose a category number (or leave empty for none): ', optional=True)
          if category_id is False:
              return False
  
      name = menu.get_input(message='* Name: ')
      if name is False:
          return False
  
      url = menu.get_input(message='* URL: ')
      if url is False:
          return False
  
      # Get list for auto-completion
      autocomplete.set_parameters(list_=get_top_logins(), case_sensitive=True)
      login = autocomplete.get_input_autocomplete(
          message='* Login (use [tab] for autocompletion): ')
      if login is False:
          return False
  
      suggestion = pwgenerator.generate()
      print('* Password suggestion: %s' % (suggestion))
      password = menu.get_input(
          message='* Password: ', secure=True)
      if password is False:
          return False
  
      notes = notes_input()
      if notes is False:
          return False
  
      # Save item
      add(name=name,
          url=url,
          login=login,
          password=password,
          notes=notes,
          category_id=category_id or None)
  
      print()
      print('The new item has been saved to your vault.')
      print()
  
      time.sleep(2)
  
      return True
  
  
  def notes_input():
      """
          Ask user to input notes
      """
  
      print('* Notes: (press [ENTER] twice to complete)')
      notes = []
      for i in range(15):  # Max 15 lines
          input_ = menu.get_input(message="> ")
          if input_ is False:
              return False
          elif input_ == "":
              break
          else:
              notes.append(input_)
  
      return "\n".join(notes)
  
  
  def delete(id_):
      """
          Delete a secret
      """
  
      secret = get_session().query(SecretModel).filter(
          SecretModel.id == int(id_)).first()
  
      if secret:
          get_session().delete(secret)
          get_session().commit()
  
          return True
  
      return False
  
  
  def delete_confirm(id_):
      """
          Delete a secret (ID is an input, just asking for confirmation)
      """
  
      if confirm('Confirm deletion?', False):
          result = delete(id_)
  
          if result is True:
              print()
              print('The secret has been deleted.')
  
              time.sleep(2)
  
          return result
  
      return False
  
  
  def search(query):
      """
          Search by keyword
      """
  
      query = '%' + str(query) + '%'
  
      return get_session().query(SecretModel) \
          .filter(or_(SecretModel.name.like(query), SecretModel.url.like(query), SecretModel.login.like(query))) \
          .order_by(SecretModel.id).all()
  
  
  def search_dispatch(query):
      """
          Run a user search. If the query is an integer we will first search by id, otherwise,
          it will be a keyword based search
      """
  
      if type(query) is int or query.isdigit():
          # Search an ID matching the input
          row = get_by_id(int(query))
  
          if row:
              return [row]
  
      # Otherwise return search result
      return search(query)
  
  
  def search_input():
      """
          Ask user to input a search query
      """
  
      # Ask user input
      print()
      autocomplete.set_parameters(list_=get_names(), case_sensitive=False)
      query = autocomplete.get_input_autocomplete(
          message='Enter search: ')
  
      if not query:
          print()
          print('Empty search!')
          return False
  
      # To prevent fat-finger errors, the search menu will also respond to common commands
      if query in ['s', 'a', 'l', 'q']:  # Common commands
          return query
      elif query == 'b':  # Return to previous menu
          return False
  
      # Get results
      results = search_dispatch(query)
  
      if len(results) == 1:  # Exactly one result
          return item_view(results[0])
      elif len(results) > 1:  # More than one result
          return search_results(results)
      else:
          try:
              print('No results!')
  
              time.sleep(2)
          except KeyboardInterrupt:
              pass
          except Exception:  # Other Exception
              pass
  
          return False
  
  
  def search_results(rows):
      """
          Display search results
      """
  
      print()
      print(to_table(rows))
      print()
  
      # Ask user input
      input_ = menu.get_input(
          message='Select a result # or type any key to go back to the main menu: ')
  
      if input_:
          try:
              result = [row for row in rows if row.id == int(input_)]
  
              if result:
                  return item_view(result[0])
          except ValueError:  # Non integer
              pass
  
      return False
  
  
  def item_view(item):
      """
          Show a secret
      """
  
      # Clear screen
      clear_screen()
  
      print(to_table([item]))
      print()
  
      # Show eventual notes
      if item.notes:
          print('Notes:')
          print(item.notes)
          print()
  
      # Show item menu
      return item_menu(item)
  
  
  def item_menu(item):
      """
          Item menu
      """
  
      while True:
          command = menu.get_input(
              message='Choose a command [%s%s%s%s%s%s / (e)dit / (d)elete / (s)earch / (b)ack to Vault]: ' % (
                      'copy ' if item.login or item.password or item.url else '',
                      '(l)ogin, ' if item.login else '',
                      '(p)assword' if item.password else '',
                      ' or ' if (
                          item.login or item.password) and item.url else '',
                      '(u)rl to clipboard' if item.url else '',
                      ' / sh(o)w password' if item.password else '',
              ),
              lowercase=True,
              non_locking_values=['l', 'q']
          )
  
          if command is False:
              print()
  
          # Action based on command
          if command == 'l':  # Copy login to the clipboard
              clipboard.copy(item.login, 'login')
              clipboard.wait()
          elif command == 'p':  # Copy a secret to the clipboard
              clipboard.copy(item.password)
              clipboard.wait()
          elif command == 'u':  # Copy URL to the clipboard
              clipboard.copy(item.url, 'URL')
              clipboard.wait()
          elif command == 'o':  # Show a secret
              return show_secret(item)
          elif command == 'e':  # Edit an item
              item_menu_edit(item)
          elif command == 'd':  # Delete an item
              delete_confirm(item.id)
              return
          elif command in ['s', 'b', 'q']:  # Common commands
              return command
  
  
  def item_menu_edit(item):
      """
          Edit an item
      """
  
      command = menu.get_input(
          message='Choose what you would like to edit [(c)ategory / (n)ame  / (u)rl / (l)ogin / (p)assword / n(o)tes / (b)ack to Vault]: ',
          lowercase=True,
          non_locking_values=['l', 'q']
      )
  
      # Action based on command
      if command == 'c':  # Edit category
          edit_input('category', item)
          return
      elif command == 'n':  # Edit name
          edit_input('name', item)
          return
      elif command == 'u':  # Edit URL
          edit_input('url', item)
          return
      elif command == 'l':  # Edit login
          edit_input('login', item)
          return
      elif command == 'p':  # Edit password
          edit_input('password', item)
          return
      elif command == 'o':  # Edit notes
          edit_input('notes', item)
          return
      elif command == 'b':  # Back to vault menu
          return
  
      return
  
  
  def edit_input(element_name, item):
      """
          Edit an item
      """
  
      if element_name == 'category':
          print('* Current nategory: %s' %
                (get_category_name(item.category_id) or 'Empty!'))
          category_id = pick(message='* New category: ', optional=True)
  
          if category_id is not False:
              item.category_id = category_id
          else:
              time.sleep(2)
              print('\nCancelled!')
              return False
      elif element_name == 'name':
          print('* Current name: %s' % (item.name or 'Empty!'))
          name = menu.get_input(message='* New name: ')
  
          if name is not False:
              item.name = name
          else:
              print('\nCancelled!')
              time.sleep(2)
              return False
      elif element_name == 'url':
          print('* Current URL: %s' % (item.url or 'Empty!'))
          url = menu.get_input(message='* New URL: ')
  
          if url is not False:
              item.url = url
          else:
              print('\nCancelled!')
              time.sleep(2)
              return False
      elif element_name == 'login':
          print('* Current login: %s' % (item.login or 'Empty!'))
          login = menu.get_input(message='* New login: ')
  
          if login is not False:
              item.login = login
          else:
              print('\nCancelled!')
              time.sleep(2)
              return False
      elif element_name == 'password':
          print('* Password suggestion: %s' % (pwgenerator.generate()))
          password = menu.get_input(message='* New password: ', secure=True)
  
          if password is not False:
              item.password = password
          else:
              print('\nCancelled!')
              time.sleep(2)
              return False
      elif element_name == 'notes':
          print('* Current notes: %s' % (item.notes or 'Empty!'))
          notes = notes_input()
  
          if notes is not False:
              item.notes = notes
          else:
              print('\nCancelled!')
              time.sleep(2)
              return False
      else:
          raise ValueError('Element `%s` not not exists.' % (element_name))
  
      # Process update
      get_session().add(item)
      get_session().commit()
  
      print('The %s has been updated.' % (element_name))
      time.sleep(2)
  
      return True
  
  
  def show_secret(item):
      """
          Show a secret for X seconds and erase it from the screen
      """
  
      try:
          print("* The password will be hidden after %s seconds." %
                (global_scope['conf'].hideSecretTTL))
          print('* The password is: %s' % (item.password), end="\r")
  
          time.sleep(int(global_scope['conf'].hideSecretTTL))
      except KeyboardInterrupt:
          # Will catch `^-c` and immediately hide the password
          pass
  
      print('* The password is: ' + '*' * (
          len(item.password) + random.randint(1, 8)))
  
      return item_view(item)
  