"""Project-owned existing bridge overlap evidence; never allocation or address-policy authorization."""
import ipaddress
import re

from network_pools import PRIVATE
from network_readback import expected_host_records

LIMIT = 256
REASONS = frozenset({'', 'selected_ownership_unverified', 'selected_bridge_unsupported',
                     'host_records_incomplete', 'selected_bridge_records_missing', 'overlap_evidence_limit'})
KINDS = frozenset({'route_other', 'route_connected', 'route_local', 'route_broadcast',
                   'route_other_source', 'route_connected_source', 'route_local_source', 'route_broadcast_source',
                   'route_gateway', 'route_src', 'address_network', 'address_local', 'address_peer',
                   'address_broadcast', 'resolver'})


def profile(network, inventory, project):
    """Bind only an unambiguous ordinary Compose name/project/key and its actual IPAM gateway."""
    labels = network['Labels']
    key = labels.get('com.docker.compose.network', '')
    if (labels.get('com.docker.compose.project') != project or not re.fullmatch(r'[A-Za-z0-9_.-]{1,128}', key)
            or network['Name'] != project + '_' + key
            or sum(n['Name'] == network['Name'] for n in inventory['networks']) != 1
            or sum(n['Id'] == network['Id'] for n in inventory['networks']) != 1):
        return None, 'selected_ownership_unverified'
    ipam = network['IPAM']
    if (network['Driver'] != 'bridge' or network['Scope'] != 'local' or network['EnableIPv6']
            or network['Options'] or ipam['Driver'] != 'default' or ipam['Options']
            or len(ipam['Config']) != 1):
        return None, 'selected_bridge_unsupported'
    pool = ipam['Config'][0]
    try:
        subnet = ipaddress.IPv4Network(pool['Subnet'], strict=True)
        gateway = ipaddress.IPv4Address(pool['Gateway'])
        if (set(pool) - {'Subnet', 'Gateway', 'IPRange', 'AuxiliaryAddresses'}
                or pool.get('IPRange') not in (None, '') or pool.get('AuxiliaryAddresses') not in (None, {})
                or subnet.prefixlen > 30
                or not any(subnet.subnet_of(space) for space in PRIVATE) or gateway not in subnet
                or gateway in (subnet.network_address, subnet.broadcast_address)):
            raise ValueError()
    except (KeyError, ValueError, TypeError):
        return None, 'selected_bridge_unsupported'
    return {'subnet': subnet, 'gateway': str(gateway), 'bridge': 'br-' + network['Id'][:12]}, ''


def pool_membership(inventory, subnet):
    if inventory['pool_failure'] or inventory['pool_source'] not in ('engine_info', 'versioned_moby_builtin'):
        return 'unverified'
    return ('inside_verified_pools' if any(subnet.subnet_of(ipaddress.IPv4Network(p['base']))
                                         for p in inventory['pools']) else 'outside_verified_pools')


def host_overlaps(records, selected):
    expected = expected_host_records(str(selected['subnet']), selected['gateway'])
    own, conflicts = set(), set()
    for row in records:
        if not selected['subnet'].overlaps(ipaddress.IPv4Network(row['subnet'])):
            continue
        if row['device'] == selected['bridge'] and row['subnet'] in expected.get(row['kind'], set()):
            own.add((row['subnet'], row['kind']))
        else:
            relation = 'selected_bridge' if row['device'] == selected['bridge'] else 'foreign_device'
            conflicts.add((row['subnet'], row['kind'], relation if row['device'] else 'unattributed'))
    return own, [{'subnet': subnet, 'kind': kind, 'device_relation': relation}
                 for subnet, kind, relation in sorted(conflicts)]


def selected_validation(inventory, network, project):
    """A clear snapshot does not establish a reservation outside the verified daemon pool bases."""
    selected, reason = profile(network, inventory, project)
    result = {'ownership': 'unverified' if reason == 'selected_ownership_unverified' else 'matched',
              'overlap': 'unverified', 'reason': reason, 'pool_membership': 'unverified',
              'bridge': '', 'gateway': '', 'self_record_count': 0, 'docker_conflicts': [], 'host_conflicts': []}
    if selected is None:
        return result
    subnet = selected['subnet']
    result.update(bridge=selected['bridge'], gateway=selected['gateway'],
                  pool_membership=pool_membership(inventory, subnet))
    docker = sorted({p['Subnet'] for n in inventory['networks'] if n['Id'] != network['Id']
                     for p in n['IPAM']['Config'] if ipaddress.ip_network(p['Subnet']).version == 4
                     and subnet.overlaps(ipaddress.IPv4Network(p['Subnet']))})
    records = inventory['host_records']
    own, host = host_overlaps(records, selected)
    required = {(str(subnet), 'route_connected'), (str(subnet), 'address_network'),
                (selected['gateway'] + '/32', 'route_local'), (selected['gateway'] + '/32', 'address_local')}
    if (inventory.get('complete') is not True
            or set(inventory['host_excluded']) != {row['subnet'] for row in records}):
        reason = 'host_records_incomplete'
    elif not required.issubset(own):
        reason = 'selected_bridge_records_missing'
    elif len(docker) > LIMIT or len(host) > LIMIT:
        reason = 'overlap_evidence_limit'
    result.update(reason=reason, self_record_count=len(own), docker_conflicts=docker[:LIMIT],
                  host_conflicts=host[:LIMIT], overlap='conflict' if docker or host else
                  ('unverified' if reason else 'clear'))
    return result
