"""Dummy module to create ports to occupy low ip addresses"""
#!/usr/bin/env python
import pyrax
from ansible.module_utils.basic import *


uri_ports = 'https://dfw.networks.api.rackspacecloud.com/v2.0/ports'
uri_networks = 'https://dfw.networks.api.rackspacecloud.com/v2.0/networks'


def get_port(cnw, network_id, ip_list):
    try:
        result, ports = cnw.identity.method_get(uri_ports)
        if result.status_code == 200:
            ports_on_network = filter(lambda port: port['network_id'] == \
                                      network_id, ports['ports'])
            for port in ports_on_network:
                for ip in port['fixed_ips']:
                    if ip['ip_address'] in ip_list:
                        return result.status_code, port
            return 404, {'status': 404, 'message': 'port not found'}

    except Exception as e:
        return 'ERROR', {'status': 'ERROR', 'message': e.message}


def get_subnet_id(cnw, network_id):
    """There will only be a single subnet"""
    result, networks = cnw.identity.method_get(uri_networks)
    if result.status_code == 200:
        network = filter(lambda net: net['id'] == network_id,
                         networks['networks'])
        subnet_id = network[0]['subnets'][0]
        return subnet_id

    return None

def port_present(data):
    """Means port is a part of the subnet and has the ip
    """
    port_name = data['name']
    network_id = data['network_id']
    network_label = data['network_label']

    if network_id and network_label:
        return True, False, {'status': 'ERROR',
                             'data': 'Specify either network_id'
                             ' or network_label, not both'}

    ip_list = data['ip_list'] if data['ip_list'] else []
    cnw = pyrax.cloud_networks
    data_json = {
        'port': {}
    }

    if port_name:
        data_json['port'].update({'name': port_name})

    if network_label:
        network = get_network_by_name(cnw, network_label)
        network_id = network['id']

    if network_id:
        data_json['port'].update({'network_id': network_id})

    subnet_id = get_subnet_id(cnw, network_id)

    if ip_list:
        data_json['port'].update({
            'fixed_ips' : [
                {'subnet_id': subnet_id, 'ip_address': ip} for ip in ip_list
            ]
        })

    try:
        result, port = cnw.identity.method_post(uri_ports, data=data_json)
        if result.status_code == 201:
            return False, True, result.json()['port']
        elif result.status_code == 422:
            return False, False, result.json()
        else:
            return True, False, {'status': result.status_code, 'data':
                                 result.json()}
    except pyrax.exceptions.ClientException as e:
        if e.code == 409:
            # already exists, query it and return port object
            if ip_list:
                status_code, port = get_port(cnw, network_id, ip_list)
                if status_code == 200:
                    return False, False, {'status': 'OK',
                                          'data': 'Port with IP'
                                          ' already exists',
                                          'port': port}
                elif status_code == 404:
                    # Although the IP is technically not there
                    # quark will not allow to assign it for a while
                    # Return an error asking to wait 10 min.
                    return True, False, {'status': 'ERROR',
                                         'data': 'IP is reserved by quark.'
                                         ' Wait 10 min and retry'}
                else:
                    return True, False, {'status': 'hmmm',
                                         'data': 'port not found undetermined'}

            elif port_name:
                port = get_port_by_name(cnw, network_id, port_name)
                if port:
                    return False, False, {'status': 'OK',
                                          'data': port,
                                          'msg': 'Port by this name '
                                          'already exists'}
                else:
                    return True, False, 'Port not found'
            else:
                # the IP could be still reserved by quark, we are good (?)
                return False, False, port
    except Exception as e:
        return True, False, {'status': 'ERROR', 'data': e.message}


def get_port_by_name(cnw, network_id, name):
    try:
        result, ports = cnw.identity.method_get(uri_ports)
        if result.status_code == 200:
            if ports:
                ports_on_network = filter(lambda port: port['network_id'] == \
                                          network_id, ports['ports'])
                ports_with_name = filter(lambda port: port['name'] == name,
                                         ports_on_network)
                return ports_with_name[0] if ports_with_name else None
            else:
                return None
        else:
            return None
    except Exception as e:
        return None

def get_port_by_ip(cnw, network_id, ip_list):
    try:
        result, ports = cnw.identity.method_get(uri_ports)
        if result.status_code == 200:
            ports_on_network = filter(lambda port: port['network_id'] == \
                                      network_id, ports['ports'])
            for port in ports_on_network:
                for ip in port['fixed_ips']:
                    if ip['ip_address'] in ip_list:
                        return port
        else:
            return None
    except Exception as e:
        return None

def delete_port_by_id(cnw, port_id):
    try:
        result, port = cnw.identity.method_delete(uri_ports + '/' +  port_id)
        if result.status_code == 204:
            return False, True, {'status': 'SUCCESS'}
        elif result.status_code == 404:
            return False, False, {'status': result.status_code, 'data':
                                  result.json()}
        else:
            return True, False, {'status': result.status_code, 'data':
                                 result.json()}
    except Exception as e:
        return True, False, {'status': 'ERROR', 'data': e.message}


def get_network_by_name(cnw, network_label):
    try:
        result, networks = cnw.identity.method_get(uri_networks)
        if result.status_code == 200:
            network_list = filter(lambda net: net['name'] == network_label,
                                  networks['networks'])
            return network_list[0]
    except Exception as e:
        return None


def port_absent(data=None):
    cnw = pyrax.cloud_networks
    network_id = data['network_id']
    port_id = data['port_id']
    port_name = data['name']
    ip_list = data['ip_list']
    network_label = data['network_label']

    if not network_id and network_label:
        network = get_network_by_name(cnw, network_label)
        if network:
            network_id = network['id']
        else:
            return True, False, {'status': 'ERROR',
                                 'data': 'did not find the network label {}'
                                 .format(network_label)}

    if port_id:
        return delete_port_by_id(cnw, port_id)

    if port_name:
        port = get_port_by_name(cnw, network_id, port_name)
        if port:
            port_id = port['id']
            return delete_port_by_id(cnw, port_id)
        else:
            return False, False, {'status': 'OK', 'data': 'Port name does not'
                                  ' exist'}

    if ip_list:
        port = get_port_by_ip(cnw, network_id, ip_list)
        if port:
            port_id = port['id']
            return delete_port_by_id(cnw, port_id)
        else:
            return False, False, {'status': 'OK',
                                 'data': 'IP not found on any port. Hope it'
                                 ' is expected'}

    # if we are here we have an error
    return True, False, {'status': 'ERROR', 'data': 'undetermined error'}


def main():
    fields = {
        'ip_list': {'required': False, 'type': 'list'},
        'network_id': {'required': False, 'type': 'str'},
        'network_label': {'required': False, 'type': 'str'},
        'port_id': {'required': False, 'type': 'str'},
        'name': {'required': False, 'type': 'str'},
        'description': {'required': False, 'type': 'str'},
        'region': {'required': True, 'type': 'str'},
        'state': {
            'default': 'present',
            'choices': ['present', 'absent'],
            'type': 'str'
        }
    }
    choice_map = {
        'present': port_present,
        'absent': port_absent
    }