# pylint: skip-file
# flake8: noqa


class ManageNodeException(Exception):
    ''' manage-node exception class '''
    pass


class ManageNodeConfig(OpenShiftCLIConfig):
    ''' ManageNodeConfig is a DTO for the manage-node command.'''
    def __init__(self, kubeconfig, node_options):
        super(ManageNodeConfig, self).__init__(None, None, kubeconfig, node_options)


# pylint: disable=too-many-instance-attributes
class ManageNode(OpenShiftCLI):
    ''' Class to wrap the oc command line tools '''

    # pylint allows 5
    # pylint: disable=too-many-arguments
    def __init__(self,
                 config,
                 verbose=False):
        ''' Constructor for ManageNode '''
        super(ManageNode, self).__init__(None, kubeconfig=config.kubeconfig, verbose=verbose)
        self.config = config

    def evacuate(self):
        ''' formulate the params and run oadm manage-node '''
        return self._evacuate(node=self.config.config_options['node']['value'],
                              selector=self.config.config_options['selector']['value'],
                              pod_selector=self.config.config_options['pod_selector']['value'],
                              dry_run=self.config.config_options['dry_run']['value'],
                              grace_period=self.config.config_options['grace_period']['value'],
                              force=self.config.config_options['force']['value'],
                             )
    def get_nodes(self, node=None, selector=''):
        '''perform oc get node'''
        _node = None
        _sel = None
        if node:
            _node = node
        if selector:
            _sel = selector

        results = self._get('node', rname=_node, selector=_sel)
        if results['returncode'] != 0:
            return results

        nodes = []
        items = None
        if results['results'][0]['kind'] == 'List':
            items = results['results'][0]['items']
        else:
            items = results['results']

        for node in items:
            _node = {}
            _node['name'] = node['metadata']['name']
            _node['schedulable'] = True
            if 'unschedulable' in node['spec']:
                _node['schedulable'] = False
            nodes.append(_node)

        return nodes

    def get_pods_from_node(self, node, pod_selector=None):
        '''return pods for a node'''
        results = self._list_pods(node=[node], pod_selector=pod_selector)

        if results['returncode'] != 0:
            return results

        # When a selector or node is matched it is returned along with the json.
        # We are going to split the results based on the regexp and then
        # load the json for each matching node.
        # Before we return we are going to loop over the results and pull out the node names.
        # {'node': [pod, pod], 'node': [pod, pod]}
        # 3.2 includes the following lines in stdout: "Listing matched pods on node:"
        all_pods = []
        if "Listing matched" in results['results']:
            listing_match = re.compile('\n^Listing matched.*$\n', flags=re.MULTILINE)
            pods = listing_match.split(results['results'])
            for pod in pods:
                if pod:
                    all_pods.extend(json.loads(pod)['items'])

        # 3.3 specific
        else:
            # this is gross but I filed a bug...
            # https://bugzilla.redhat.com/show_bug.cgi?id=1381621
            # build our own json from the output.
            all_pods = json.loads(results['results'])['items']

        return all_pods

    def list_pods(self):
        ''' run oadm manage-node --list-pods'''
        _nodes = self.config.config_options['node']['value']
        _selector = self.config.config_options['selector']['value']
        _pod_selector = self.config.config_options['pod_selector']['value']

        if not _nodes:
            _nodes = self.get_nodes(selector=_selector)
        else:
            _nodes = [{'name': name} for name in _nodes]

        all_pods = {}
        for node in _nodes:
            results = self.get_pods_from_node(node['name'], pod_selector=_pod_selector)
            if isinstance(results, dict):
                return results
            all_pods[node['name']] = results

        results = {}
        results['nodes'] = all_pods
        results['returncode'] = 0
        return results

    def schedulable(self):
        '''oadm manage-node call for making nodes unschedulable'''
        nodes = self.config.config_options['node']['value']
        selector = self.config.config_options['selector']['value']

        if not nodes:
            nodes = self.get_nodes(selector=selector)
        else:
            tmp_nodes = []
            for name in nodes:
                tmp_result = self.get_nodes(name)
                if isinstance(tmp_result, dict):
                    tmp_nodes.append(tmp_result)
                    continue
                tmp_nodes.extend(tmp_result)
            nodes = tmp_nodes

        # This is a short circuit based on the way we fetch nodes.
        # If node is a dict/list then we've already fetched them.
        for node in nodes:
            if isinstance(node, dict) and 'returncode' in node:
                return {'results': nodes, 'returncode': node['returncode']}
            if isinstance(node, list) and 'returncode' in node[0]:
                return {'results': nodes, 'returncode': node[0]['returncode']}
        # check all the nodes that were returned and verify they are:
        # node['schedulable'] == self.config.config_options['schedulable']['value']
        if any([node['schedulable'] != self.config.config_options['schedulable']['value'] for node in nodes]):

            results = self._schedulable(node=self.config.config_options['node']['value'],
                                        selector=self.config.config_options['selector']['value'],
                                        schedulable=self.config.config_options['schedulable']['value'])

            # 'NAME                            STATUS    AGE\\nip-172-31-49-140.ec2.internal   Ready     4h\\n'  # E501
            # normalize formatting with previous return objects
            if results['results'].startswith('NAME'):
                nodes = []
                # removing header line and trailing new line character of node lines
                for node_results in results['results'].split('\n')[1:-1]:
                    parts = node_results.split()
                    nodes.append({'name': parts[0], 'schedulable': parts[1] == 'Ready'})
                results['nodes'] = nodes

            return results

        results = {}
        results['returncode'] = 0
        results['changed'] = False
        results['nodes'] = nodes

        return results

    @staticmethod
    def run_ansible(params, check_mode):
        '''run the idempotent ansible code'''
        nconfig = ManageNodeConfig(params['kubeconfig'],
                                   {'node': {'value': params['node'], 'include': True},
                                    'selector': {'value': params['selector'], 'include': True},
                                    'pod_selector': {'value': params['pod_selector'], 'include': True},
                                    'schedulable': {'value': params['schedulable'], 'include': True},
                                    'list_pods': {'value': params['list_pods'], 'include': True},
                                    'evacuate': {'value': params['evacuate'], 'include': True},
                                    'dry_run': {'value': params['dry_run'], 'include': True},
                                    'force': {'value': params['force'], 'include': True},
                                    'grace_period': {'value': params['grace_period'], 'include': True},
                                   })

        oadm_mn = ManageNode(nconfig)
        # Run the oadm manage-node commands
        results = None
        changed = False
        if params['schedulable'] != None:
            if check_mode:
                # schedulable returns results after the fact.
                # We need to redo how this works to support check_mode completely.
                return {'changed': True, 'msg': 'CHECK_MODE: would have called schedulable.'}
            results = oadm_mn.schedulable()
            if 'changed' not in results:
                changed = True

        if params['evacuate']:
            results = oadm_mn.evacuate()
            changed = True
        elif params['list_pods']:
            results = oadm_mn.list_pods()

        if not results or results['returncode'] != 0:
            return {'failed': True, 'msg': results}

        return {'changed': changed, 'results': results, 'state': "present"}
