"""CSShellCmd class emulates the behaviour of a shell to edit the CS config.
"""

from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
__RCSID__ = "$Id$"

import cmd
import os

import six

from DIRAC.Core.Base.CLI import CLI, colorize
from DIRAC.ConfigurationSystem.Client.ConfigurationData import gConfigurationData
from DIRAC.ConfigurationSystem.private.Modificator import Modificator
from DIRAC.ConfigurationSystem.Client.ConfigurationClient import ConfigurationClient


class CSShellCLI(CLI):

  def __init__(self):
    CLI.__init__(self)
    self.serverURL = ""
    self.serverName = ""
    self.modificator = None
    self.connected = False
    self.dirty = False
    self.root = "/"

    self.do_connect("")

  def update_prompt(self):
    if self.connected:
      self.prompt = "[" + colorize(self.serverName, "green") + ":" + self.root + " ]% "
    else:
      self.prompt = "[" + colorize("disconnected", "red") + ":" + self.root + " ]% "

  def do_connect(self, line):
    """connect
    Connect to the CS
    Usage: connect <URL> (Connect to the CS at the specified URL)
           connect       (Connect to the default CS URL of your config)
    """
    if line == "":
      self.serverURL = gConfigurationData.getMasterServer()
      self.serverName = gConfigurationData.getName()
    else:
      self.serverURL = self.serverName = line

    if self.serverURL is None:
      print("Unable to connect to the default server. Maybe you don't have a proxy ?")
      return self.do_disconnect("")

    print("Trying to connect to " + self.serverURL + "...", end=' ')

    self.modificator = Modificator(ConfigurationClient(url=self.serverURL))
    rv = self.modificator.loadFromRemote()
    rv2 = self.modificator.loadCredentials()

    if rv['OK'] == False or rv2['OK'] == False:
      print("failed: ", end=' ')
      if rv['OK'] is False:
        print(rv['Message'])
      else:
        print(rv2['Message'])
      self.connected = False
      self.update_prompt()
    else:
      self.connected = True
      self.update_prompt()
      print("done.")

  def do_disconnect(self, _line):
    """Disconnect from CS"""
    if self.connected and self.dirty:
      res = six.moves.input("Do you want to commit your changes into the CS ? [y/N] ")
      if res.lower() in ["y", "yes"]:
        self.do_commit("")

    self.serverURL = self.serverName = ""
    self.modificator = None
    self.connected = False
    self.update_prompt()

  def do_ls(self, line):
    """ls
    List the sections and options of CS of the current root"""
    if self.connected:
      secs = self.modificator.getSections(self.root)
      opts = self.modificator.getOptions(self.root)
      if line.startswith("-") and "l" in line:
        for i in secs:
          print(colorize(i, "blue") + "  ")
        for i in opts:
          print(i + " ")
      else:
        for i in secs:
          print(colorize(i, "blue") + "  ", end=' ')
        for i in opts:
          print(i + " ", end=' ')
        print("")

  def do_cd(self, line):
    """cd
    Go one directory deeper in the CS"""
    # Check if invariant holds
    if self.connected:
      assert(self.root == "/" or not self.root.endswith("/"))
      assert(self.root.startswith("/"))
      secs = self.modificator.getSections(self.root)
      if line == "..":
        self.root = os.path.dirname(self.root)
        self.update_prompt()
      else:
        if os.path.normpath(line) in secs:
          if self.root == "/":
            self.root = self.root + os.path.normpath(line)
          else:
            self.root = self.root + "/" + os.path.normpath(line)
          self.update_prompt()
        else:
          print("cd: no such section: " + line)

  def complete_cd(self, text, _line, _begidx, _endidx):
    secs = self.modificator.getSections(self.root)
    return [(s + "/") for s in secs if s.startswith(text)]

  def do_cat(self, line):
    """cat
    Read the content of an option in the CS"""
    if self.connected:
      opts = self.modificator.getOptionsDict(self.root)

      if line in opts:
        print(opts[line])
      else:
        print("cat: No such option")

  def complete_cat(self, text, _line, _begidx, _endidx):
    opts = self.modificator.getOptions(self.root)
    return [o for o in opts if o.startswith(text)]

  do_less = do_cat
  complete_less = complete_cat

  def do_mkdir(self, line):
    """mkdir
    Create a new section in the CS"""
    if self.connected:
      self.modificator.createSection(self.root + "/" + line)
      self.dirty = True

  complete_mkdir = complete_cd

  def do_rmdir(self, line):
    """rmdir
    Delete a section in the CS"""
    if self.connected:
      self.modificator.removeSection(self.root + "/" + line)
      self.dirty = True

  complete_rmdir = complete_cd

  def do_rm(self, line):
    """rm
    Delete an option in the CS"""
    if self.connected:
      self.modificator.removeOption(self.root + "/" + line)
      self.dirty = True

  complete_rm = complete_cat

  def do_set(self, line):
    """set
    Set an option in the CS (or create it if it does not exists)
    Usage: set <str> to set a string option (will be stored as a string in CS)
           set <str>,<str>,... to set a list option (will be stored as a list in CS)
    """
    if self.connected:
      line = line.split(" ", 2)
      if len(line) != 2:
        print("Usage: set <key> <value>")
      else:
        self.modificator.setOptionValue(self.root + "/" + line[0], line[1])
        self.dirty = True

  complete_set = complete_cat

  def do_unset(self, line):
    """unset
    Unset an option in the CS: Making the option equal to the
    empty string."""
    if self.connected:
      self.modificator.setOptionValue(self.root + "/" + line, "")
      self.dirty = True

  complete_unset = complete_cat

  def do_commit(self, _line):
    """commit
    Commit the modifications to the CS"""
    if self.connected and self.dirty:
      self.modificator.commit()

  def default(self, line):
    """Override [Cmd.default(line)] function."""
    if line == "EOF":
      if self.prompt:
        print()
      return self.do_quit(line)
    else:
      cmd.Cmd.default(self, line)

  def do_quit(self, _line):
    """quit
    Quit"""
    self.do_disconnect("")
    CLI.do_quit(self, _line)
