"""
### Shell

This module contains the Operators that can be used to interact with the shell.
"""
import sys
from subprocess import Popen

from nbox import Operator
from nbox.operator import operator

class ShellCommand(Operator):
  def __init__(self, *commands):
    """Run multiple shell commands, uses `shelex` to prevent injection"""
    super().__init__()
    import string

    self.commands = commands
    all_in = []
    for c in self.commands:
      all_in.extend([tup[1] for tup in string.Formatter().parse(c) if tup[1] is not None])

  def forward(self, *args, **kwargs):
    import shlex
    import subprocess

    for comm in self.commands:
      comm = comm.format(*args, **kwargs)
      comm = shlex.split(comm)
      called_process: subprocess.CompletedProcess = subprocess.run(comm, check = True)
      if called_process.returncode != 0:
        raise Exception(f"Command {comm} failed with return code {called_process.stdout}")

class PythonScript(Operator):
  def __init__(self, fpath, python_path = "/usr/bin/python3", **kwargs):
    """Run any file in python as an operator"""
    super().__init__()
    self.fp = fpath
    self.python_path = python_path
    self.kwargs = kwargs

    self.kwargs_strings = []
    for k,v in kwargs.items():
      if v != None:
        self.kwargs_strings.append(f"--{k}={v}")
      else:
        self.kwargs_strings.append(f"--{k}")
    kwargs = "\ \n".join(self.kwargs_strings)
    self.com = ShellCommand(f"{python_path} {fpath} {kwargs}")

  def forward(self):
    return self.com()

class NBToScript(Operator):
  def __init__(self, notebook_path: str, py_file: str) -> None:
    super().__init__()
    self.nb = notebook_path
    self.trg = py_file

  def forward(self):
    import json

    with open(self.nb, "r") as f:
      data = json.load(f)
    
    code_script = "\n# This is an Autogenerated file by NBToScript Operators\n# Find out more: nimblebox.ai\n"
    # here we can start iterating over the code cells and fill up a script
    for cell in data["cells"]:
      if cell["cell_type"] == "code":
        code_script += "".join(cell["source"]) + "\n"

    with open(self.trg, "w") as f:
      f.write(code_script)

@operator()
def git_clone(
  repository: str,
  path: str = ".",
  branch: str = "",
  depth: int = 1,
  **kwargs
) -> bool:
  """
  Operator to clone a git repository at a given path, usually wherever this operator is called from
  returns True if the clone was successful
  """
  command = ["git", "clone"]
  if branch:
    command.append("-b")
    command.append(branch)
  if depth:
    command.append("--depth")
    command.append(str(depth))
  for k,v in kwargs.items():
    command.append(f"--{k}")
    if v != None:
      command.append(v)
  command.append(repository)
  command.append(path)
  out = Popen(command, stdout=sys.stdout, stderr=sys.stderr).wait()
  return out == 0
