import base64
import json
import os
import yaml
from kubernetes import client
from .. import LoggableChild


class LSSTVolumeManager(LoggableChild):
    '''Class to provide support for document-driven Volume assignment.
    '''
    volume_list = []
    k8s_volumes = []
    k8s_vol_mts = []

    def make_volumes_from_config(self):
        '''Create volume definition representation from document.
        Override this in a subclass if you like.
        '''
        vollist = []
        config = []
        config_file = self.parent.config.volume_definition_file
        if not os.path.exists(config_file):
            return vollist
        with open(config_file, "r") as fp:
            config = json.load(fp)
        for mtpt in config:
            mountpoint = mtpt["mountpoint"]  # Fatal error if it doesn't exist
            if mtpt.get("disabled"):
                continue
            if mountpoint[0] != "/":
                mountpoint = "/" + mountpoint
            host = (mtpt.get("fileserver-host") or
                    self.parent.config.fileserver_host)
            export = mtpt.get("fileserver-export") or (
                "/exports" + mountpoint)
            mode = (mtpt.get("mode") or "ro").lower()
            k8s_vol = mtpt.get("kubernetes-volume")
            if k8s_vol:
                raise ValueError("Shadow PVs and matching PVCs " +
                                 "are no longer supported!")
            hostpath = mtpt.get("hostpath")
            vollist.append({
                "mountpoint": mountpoint,
                "hostpath": hostpath,
                "host": host,
                "export": export,
                "mode": mode,
            })
        self.volume_list = vollist
        self.log.debug("Volumes: {}".format(vollist))
        self._define_k8s_object_representations()

    def _define_k8s_object_representations(self):
        self.k8s_volumes = []
        self.k8s_vol_mts = []
        for vol in self.volume_list:
            k8svol = None
            k8smt = None
            if vol.get("hostpath"):
                k8svol = self._define_k8s_hostpath_vol(vol)
            else:
                k8svol = self._define_k8s_nfs_vol(vol)
            k8smt = self._define_k8s_mtpt(vol)
            if k8svol and k8smt:
                self.k8s_volumes.append(k8svol)
                self.k8s_vol_mts.append(k8smt)

    def _define_k8s_hostpath_vol(self, vol):
        return client.V1Volume(
            name=self._get_volume_name_for_mountpoint(vol["mountpoint"]),
            host_path=client.V1HostPath(
                path=vol["mountpoint"]
            )
        )

    def _define_k8s_nfs_vol(self, vol):
        knf = client.V1NFSVolumeSource(
            path=vol["export"],
            server=vol["host"]
        )
        if vol["mode"] == "ro":
            knf.read_only = True
        return client.V1Volume(
            name=self._get_volume_name_for_mountpoint(vol["mountpoint"]),
            nfs=knf
        )

    def _define_k8s_mtpt(self, vol):
        mt = client.V1VolumeMount(
            mount_path=vol["mountpoint"],
            name=self._get_volume_name_for_mountpoint(vol["mountpoint"]),
        )
        if vol["mode"] == "ro":
            mt.read_only = True
        return mt

    def _get_volume_name_for_mountpoint(self, mountpoint):
        return mountpoint[1:].replace('/', '-')

    def _get_volume_yaml_str(self, left_pad=0):
        vols = self.k8s_volumes
        if not vols:
            self.log.warning("No volumes defined.")
            return ''
        vl = []
        for vol in vols:
            nm = vol.name
            hp = vol.host_path
            nf = vol.nfs
            vo = {"name": nm}
            if hp:
                vo["hostPath"] = {"path": hp.path}
            elif nf:
                am = "ReadWriteMany"
                if nf.read_only:
                    am = "ReadOnlyMany"
                vo["nfs"] = {"server": nf.server,
                             "path": nf.path,
                             "accessMode": am}
            vl.append(vo)
        vs = {"volumes": vl}
        ystr = yaml.dump(vs)
        return self._left_pad(ystr, left_pad)

    def _get_volume_mount_yaml_str(self, left_pad=0):
        vms = self.k8s_vol_mts
        if not vms:
            self.log.warning("No volume mounts defined.")
            return ''
        vl = []
        for vm in vms:
            vo = {}
            vo["name"] = vm.name
            vo["mountPath"] = vm.mount_path
            if vm.read_only:
                vo["readOnly"] = True
            vl.append(vo)
        vs = {"volumeMounts": vl}
        ystr = yaml.dump(vs)
        return self._left_pad(ystr, left_pad)

    def _left_pad(self, line_str, left_pad=0):
        pad = ' ' * left_pad
        ylines = line_str.split("\n")
        padlines = [pad + l for l in ylines]
        return "\n".join(padlines)

    def get_dask_volume_b64(self):
        '''Return the base-64 encoding of the K8s statements to create
        the pod's mountpoints.  Probably better handled as a ConfigMap.
        '''
        vmt_yaml_str = self._get_volume_mount_yaml_str(left_pad=4)
        vol_yaml_str = self._get_volume_yaml_str(left_pad=2)
        ystr = "{}\n{}".format(vmt_yaml_str, vol_yaml_str)
        benc = base64.b64encode(ystr.encode('utf-8')).decode('utf-8')
        return benc

    def dump(self):
        '''Return contents dict for aggregation and pretty-printing.
        '''
        vd = {"parent": str(self.parent),
              "volume_list": self.volume_list,
              "k8s_volumes": [str(x) for x in self.k8s_volumes],
              "k8s_vol_mts": [str(x) for x in self.k8s_vol_mts]}
        return vd
