# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#
# this module wraps generation of kustomizations

import os, io, yaml, subprocess
import nuvolaris.kube as kube
import nuvolaris.kustomize as nku
import nuvolaris.template as ntp

# execute the kustomization of a folder under "deploy"
# specified with `where`
# it generate a kustomization.yaml, adding the header 
# then including all the files of that folder as resources
# you have to pass a list of kustomizations to apply
# you can use various helpers in this module to generate customizations
# it returns the expanded kustomization
def kustomize(where, *what, templates=[], data={}):
    """Test kustomize
    >>> import nuvolaris.kustomize as ku
    >>> import nuvolaris.testutil as tu
    >>> tu.grep(ku.kustomize("test", ku.image("nginx", "busybox")), "kind|image:", sort=True)
    - image: busybox
    kind: Pod
    kind: Service
    >>> tu.grep(ku.kustomize("test", ku.configMapTemplate("test-cm", "test", "test.json", {"item":"value"})), r"_id|value")
    "_id": "test",
    "value": "value"
    >>> tu.grep(ku.kustomize("test", ku.image("nginx", "busybox"), templates=['testcm.yaml'], data={"name":"test-config"}), "name: test-", sort=True)
    name: test-config
    name: test-pod
    name: test-svc
    """
    # prepare the kustomization
    dir = f"deploy/{where}"
    tgt = f"{dir}/kustomization.yaml"
    with open(tgt, "w") as f:
        f.write("apiVersion: kustomize.config.k8s.io/v1beta1\nkind: Kustomization\n")
        for s in list(what):
            f.write(s)
        f.write("resources:\n")
        dirs = os.listdir(f"deploy/{where}")
        dirs.sort()
        for file in dirs:
            if file == "kustomization.yaml":
              continue
            if file.startswith("_"):
              continue
            f.write(f"- {file}\n")
        # adding extra temmplatized resources
        for template in templates:
            out = f"deploy/{where}/__{template}"
            file = ntp.spool_template(template, out, data)
            f.write(f"- __{template}\n")
    res = subprocess.run(["kustomize", "build", dir], capture_output=True)
    return res.stdout.decode("utf-8")

# execute the kustomization of a folder under "deploy"
# specified with `where` returning the expanded kustomization
# it expects that the folder contains already a full kustomization
# this methid will be used to extract the existing kustomization in case
# the nuvolaris operator needs to delete a component
def build(where):
    dir = f"deploy/{where}"
    res = subprocess.run(["kustomize", "build", dir], capture_output=True)
    return res.stdout.decode("utf-8")   

# execute the kustomization of a folder under "deploy"
# specified with `where`
# it generate a kustomization.yaml, adding the header 
# then including all the files of that folder as resources limited to the one
# specified in templates_filter parameter
# you have to pass a list of kustomizations to apply
# you can use various helpers in this module to generate customizations
# it returns the expanded kustomization
def restricted_kustomize(where, *what, templates=[], templates_filter=[],data={}):
    """Test kustomize
    >>> import nuvolaris.kustomize as ku
    >>> import nuvolaris.testutil as tu
    >>> tu.grep(ku.restricted_kustomize("test", ku.image("nginx", "busybox"), templates_filter=["pod.yaml","svc.yaml"]), "kind|image:", sort=True)
    - image: busybox
    kind: Pod
    kind: Service
    >>> tu.grep(ku.restricted_kustomize("test", ku.configMapTemplate("test-cm", "test", "test.json", {"item":"value"}),templates_filter=["pod.yaml","svc.yaml"]), r"_id|value")
    "_id": "test",
    "value": "value"
    >>> tu.grep(ku.restricted_kustomize("test", ku.image("nginx", "busybox"), templates=['testcm.yaml'], templates_filter=["pod.yaml","svc.yaml"],data={"name":"test-config"}), "name: test-", sort=True)
    name: test-config
    name: test-pod
    name: test-svc
    """
    # prepare the kustomization
    dir = f"deploy/{where}"
    tgt = f"{dir}/kustomization.yaml"
    with open(tgt, "w") as f:
        f.write("apiVersion: kustomize.config.k8s.io/v1beta1\nkind: Kustomization\n")
        for s in list(what):
            f.write(s)
        f.write("resources:\n")
        dirs = os.listdir(f"deploy/{where}")
        dirs.sort()
        for file in dirs:
            if file == "kustomization.yaml":
              continue
            if file.startswith("_"):
              continue
            if file in templates_filter:  
              f.write(f"- {file}\n")
        # adding extra temmplatized resources
        for template in templates:
            out = f"deploy/{where}/__{template}"
            file = ntp.spool_template(template, out, data)
            f.write(f"- __{template}\n")
    res = subprocess.run(["kustomize", "build", dir], capture_output=True)
    return res.stdout.decode("utf-8")    

# generate image kustomization
def image(name, newName=None, newTag=None):
    """Test image
    >>> import nuvolaris.kustomize as ku
    >>> print(ku.image("busybox"), end='')
    images:
    - name: busybox
    >>> print(ku.image("busybox", "nginx"), end='')
    images:
    - name: busybox
      newName: nginx
    >>> print(ku.image("busybox", newTag="nightly"), end='')
    images:
    - name: busybox
      newTag: nightly
    >>> print(ku.image("busybox", "nginx", "nightly"), end='')
    images:
    - name: busybox
      newName: nginx
      newTag: nightly
    """
    r = f"images:\n- name: {name}\n"
    if newName: r += f"  newName: {newName}\n"
    if newTag:  r += f"  newTag: {newTag}\n"
    return r

# generate a configmap kustomization expanding a template
def configMapTemplate(name, where, template, data):
    """   
    >>> import nuvolaris.testutil as tu
    >>> print(configMapTemplate("test-cm", "test", "test.json", {"item":"value"}), end='')
    configMapGenerator:
    - name: test-cm
      namespace: nuvolaris
      files:
      - test.json=__test.json
    """
    out = f"deploy/{where}/__{template}"
    file = ntp.spool_template(template, out, data)
    return f"""configMapGenerator:
- name: {name}
  namespace: nuvolaris
  files:
  - {template}=__{template}
"""

# generate a patch from a template
def patchTemplate(where, template, data):
    """   
    >>> import nuvolaris.testutil as tu
    >>> import os.path
    >>> data = {"name":"test-pod", "container":"test-pod", "dir":"/usr/share/nginx/html"}
    >>> print(patchTemplate("test",  "set-attach.yaml", data), end='')
    patches:
    - path: __set-attach.yaml
    >>> os.path.exists("deploy/test/__set-attach.yaml")
    True
    """
    out = f"deploy/{where}/__{template}"
    file = ntp.spool_template(template, out, data)
    return f"""patches:
- path: __{template}
"""

# generate a patch from a list of templates
def patchTemplates(where, templates=[], data={}):
    """   
    >>> import nuvolaris.testutil as tu
    >>> import os.path
    >>> data = {"name":"test-pod", "container":"test-pod","dir":"/usr/share/nginx/html"}
    >>> print(patchTemplates("test",  ["set-attach.yaml","cron-init.yaml"], data), end='')
    patches:
    - path: __set-attach.yaml
    - path: __cron-init.yaml
    >>> os.path.exists("deploy/test/__set-attach.yaml")
    True
    """
    paths = []
    for template in templates:
      out = f"deploy/{where}/__{template}"
      file = ntp.spool_template(template, out, data)
      paths.append(f"- path: __{template}\n")

    patches = ""
    for path in paths: 
      patches += path

    return f"""patches:\n{patches}""" 

def secretLiteral(name, *args):
  """
  >>> import nuvolaris.testutil as tu
  >>> import nuvolaris.kustomize as ku
  >>> tu.grep(ku.secretLiteral("test-sec", "user=mike", "pass=hello"), r"name:|user=|pass=")
  - name: test-sec
  - user=mike
  - pass=hello
  """
  res = f"""secretGenerator:
- name: {name}
  namespace: nuvolaris
  literals:
"""
  for arg in args:
    res += f"   - {arg}\n"
  return res

# returns a list of kustomized objects
def kustom_list(where, *what, templates=[], data={}):
  """
  >>> import nuvolaris.kustomize as nku
  >>> where = "test"
  >>> what = []
  >>> res = nku.kustom_list(where, *what)
  >>> out = [x['kind'] for x in res['items']]
  >>> out.sort()
  >>> print(out)
  ['Pod', 'Service']
  """
  yml = nku.kustomize(where, *what, templates=templates, data=data)
  stream = io.StringIO(yml)
  res = list(yaml.load_all(stream, yaml.Loader))
  return {"apiVersion": "v1", "kind": "List", "items": res }


# returns a list of kustomized objects restricting the deploy available templates to given ones
def restricted_kustom_list(where, *what, templates=[], templates_filter=[], data={}):
  """
  >>> import nuvolaris.kustomize as nku
  >>> where = "test"
  >>> what = []
  >>> res = nku.restricted_kustom_list(where, *what,templates_filter=["pod.yaml","svc.yaml"])
  >>> out = [x['kind'] for x in res['items']]
  >>> out.sort()
  >>> print(out)
  ['Pod', 'Service']
  """
  yml = nku.restricted_kustomize(where, *what, templates=templates, templates_filter=templates_filter,data=data)
  stream = io.StringIO(yml)
  res = list(yaml.load_all(stream, yaml.Loader))
  return {"apiVersion": "v1", "kind": "List", "items": res }

# load the given yaml file under deploy/{where} folder
def raw(where, yamlfile):
  with open(f"deploy/{where}/{yamlfile}", 'r') as f:
    return list(yaml.load_all(f, yaml.Loader))

def processTemplate(where,template,data,out_template=None):
    """
    merges the given template and write it under the deploy/{where} folder returning a kind list items
    """  
    out = f"deploy/{where}/_{template}"

    if(out_template):
      out = f"deploy/{where}/{out_template}"

    ntp.spool_template(template, out, data)
    with open(out, 'r') as f:
      res = list(yaml.load_all(f, yaml.Loader))
      return {"apiVersion": "v1", "kind": "List", "items": res }

def renderTemplate(where,template,data,out_template):
    """
    merges the given template and write it under the deploy/{where} folder returning the relative generated file path
    """  
    out = f"deploy/{where}/{out_template}"
    ntp.spool_template(template, out, data)
    return out

# generate a kustomization for a persistence volume claim using inline patchesJson6902 format
def patchPersistentVolumeClaim(name, path, value):
    """
    >>> print(patchPersistentVolumeClaim("mongodb-data", "/spec/resources/requests/storage", "10Gi"), end='')
    - target:
        version: v1
        kind: PersistentVolumeClaim
        namespace: nuvolaris
        name: mongodb-data
      patch: |-
        - op: replace
          path: /spec/resources/requests/storage
          value: 10Gi
    """
    return f"""- target:
    version: v1
    kind: PersistentVolumeClaim
    namespace: nuvolaris
    name: {name}
  patch: |-
    - op: replace
      path: {path}
      value: {value}
""" 

# generate a generic customization
def patchGenericEntry(kind, name, path, value, op="replace",apiVersion="v1"):
    """
    >>> print(patchGenericEntry("Secret","postgres-nuvolaris-secret", "/stringData/superUserPassword","xyz"), end='')
    - target:
        version: v1
        kind: Secret
        namespace: nuvolaris
        name: postgres-nuvolaris-secret
      patch: |-
        - op: replace
          path: /stringData/superUserPassword
          value: xyz
    """
    return f"""- target:
    version: {apiVersion}
    kind: {kind}
    namespace: nuvolaris
    name: {name}
  patch: |-
    - op: {op}
      path: {path}
      value: {value}
"""
   
