import requests
import socket
import json
from concurrent.futures import ThreadPoolExecutor
from tqdm import tqdm


class NetworkHelpers:
  def __init__(self):
    print("Network Helper")

  def get_local_ip_address(self):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(('8.8.8.8', 1))  # connect to a public IP address, any will do
    ip_address = s.getsockname()[0]
    s.close()
    return ip_address

  def find_emitters_and_consumers(self):
    """Finds all Emitters and Consumers on the local network."""
    ip_address = self.get_local_ip_address()
    ip_prefix = '.'.join(ip_address.split('.')[:-1]) + '.'
    emitters_and_consumers = {}
    with ThreadPoolExecutor() as executor:
      futures = [executor.submit(self.find_on_ip, ip_prefix + str(i)) for i in range(256)]
      with tqdm(total=len(futures), desc='Scanning network', unit=' IP address') as pbar:
        for future in futures:
          devices = future.result()
          if devices is not None:
            for device in devices:
              emitters_and_consumers[device["id"]] = device
            pbar.update(1)
    return emitters_and_consumers

  def find_on_ip(self, address):
    """Gets the RabbitMQ queues and description of an Emitter or Consumer."""
    result = []
    for port in range(8000, 8011):
      try:
        response = requests.get(f'http://{address}:{port}/specs', timeout=0.1)
        if response.status_code == 200:
          data = json.loads(response.text)
          if 'queues' in data and 'description' in data:
            data['address'] = address
            data['port'] = port
            result.append(data)
      except:
        pass
    if len(result)==0:
      return None
    return result

  def get_available_port(self):
    """Finds an available port to use for the local server."""
    port = 8000
    while True:
      with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        if s.connect_ex(('localhost', port)) != 0:
          return port
      port += 1

  def get_rabbitmq_local_ip(self):
    """Gets the local IP address of the machine running RabbitMQ."""
    local_ip = socket.gethostbyname(socket.gethostname())
    local_network_prefix = '.'.join(local_ip.split('.')[:-1]) + '.'

    open_ports = []
    #for i in tqdm(range(1, 255)):
    for i in range(1, 255):
      ip = local_network_prefix + str(i)
      for port in [5672]:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(0.1)
        result = sock.connect_ex((ip, port))
        if result == 0:
          open_ports.append({'ip': ip, 'port': port})
        sock.close()

    for p in open_ports:
      if p['port'] == 5672:
        return p['ip']

    return None

