from __future__ import annotations

import ipaddress
import logging
from typing import Any, Optional, TYPE_CHECKING, List, Union

from ..menu.text_input import TextInput
from ..models.network_configuration import NetworkConfiguration, NicType

from ..networking import list_interfaces
from ..menu import Menu
from ..output import log
from ..menu.list_manager import ListManager

if TYPE_CHECKING:
	_: Any


class ManualNetworkConfig(ListManager):
	"""
	subclass of ListManager for the managing of network configuration accounts
	"""

	def __init__(self, prompt: str, ifaces: Union[None, NetworkConfiguration, List[NetworkConfiguration]]):
		"""
		param: prompt
		type: str
		param: ifaces already defined previously
		type: Dict
		"""

		if ifaces is not None and isinstance(ifaces, list):
			display_values = {iface.iface: iface for iface in ifaces}
		else:
			display_values = {}

		self._action_add = str(_('Add interface'))
		self._action_edit = str(_('Edit interface'))
		self._action_delete = str(_('Delete interface'))

		self._iface_actions = [self._action_edit, self._action_delete]

		super().__init__(prompt, display_values, self._iface_actions, self._action_add)

	def run_manual(self) -> List[NetworkConfiguration]:
		ifaces = super().run()
		if ifaces is not None:
			return list(ifaces.values())
		return []

	def exec_action(self, data: Any):
		if self.action == self._action_add:
			iface_name = self._select_iface(data.keys())
			if iface_name:
				iface = NetworkConfiguration(NicType.MANUAL, iface=iface_name)
				data[iface_name] = self._edit_iface(iface)
		elif self.target:
			iface_name = list(self.target.keys())[0]
			iface = data[iface_name]

			if self.action == self._action_edit:
				data[iface_name] = self._edit_iface(iface)
			elif self.action == self._action_delete:
				del data[iface_name]

	def _select_iface(self, existing_ifaces: List[str]) -> Optional[str]:
		all_ifaces = list_interfaces().values()
		available = set(all_ifaces) - set(existing_ifaces)
		iface = Menu(str(_('Select interface to add')), list(available), skip=True).run()
		return iface

	def _edit_iface(self, edit_iface :NetworkConfiguration):
		iface_name = edit_iface.iface
		modes = ['DHCP (auto detect)', 'IP (static)']
		default_mode = 'DHCP (auto detect)'

		prompt = _('Select which mode to configure for "{}" or skip to use default mode "{}"').format(iface_name, default_mode)
		mode = Menu(prompt, modes, default_option=default_mode).run()

		if mode == 'IP (static)':
			while 1:
				prompt = _('Enter the IP and subnet for {} (example: 192.168.0.5/24): ').format(iface_name)
				ip = TextInput(prompt, edit_iface.ip).run().strip()
				# Implemented new check for correct IP/subnet input
				try:
					ipaddress.ip_interface(ip)
					break
				except ValueError:
					log("You need to enter a valid IP in IP-config mode.", level=logging.WARNING, fg='red')

			# Implemented new check for correct gateway IP address
			while 1:
				gateway = TextInput(_('Enter your gateway (router) IP address or leave blank for none: '),
									edit_iface.gateway).run().strip()
				try:
					if len(gateway) == 0:
						gateway = None
					else:
						ipaddress.ip_address(gateway)
					break
				except ValueError:
					log("You need to enter a valid gateway (router) IP address.", level=logging.WARNING, fg='red')

			if edit_iface.dns:
				display_dns = ' '.join(edit_iface.dns)
			else:
				display_dns = None
			dns_input = TextInput(_('Enter your DNS servers (space separated, blank for none): '), display_dns).run().strip()

			if len(dns_input):
				dns = dns_input.split(' ')

			return NetworkConfiguration(NicType.MANUAL, iface=iface_name, ip=ip, gateway=gateway, dns=dns, dhcp=False)
		else:
			# this will contain network iface names
			return NetworkConfiguration(NicType.MANUAL, iface=iface_name)


def ask_to_configure_network(preset: Union[None, NetworkConfiguration, List[NetworkConfiguration]]) -> Optional[Union[List[NetworkConfiguration], NetworkConfiguration]]:
	"""
		Configure the network on the newly installed system
	"""
	network_options = {
		'none': str(_('No network configuration')),
		'iso_config': str(_('Copy ISO network configuration to installation')),
		'network_manager': str(_('Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)')),
		'manual': str(_('Manual configuration'))
	}
	# for this routine it's easier to set the cursor position rather than a preset value
	cursor_idx = None

	if preset and not isinstance(preset, list):
		if preset.type == 'iso_config':
			cursor_idx = 0
		elif preset.type == 'network_manager':
			cursor_idx = 1

	nic = Menu(_(
		'Select one network interface to configure'),
		list(network_options.values()),
		cursor_index=cursor_idx,
		sort=False
	).run()

	if not nic:
		return preset

	if nic == network_options['none']:
		return None
	elif nic == network_options['iso_config']:
		return NetworkConfiguration(NicType.ISO)
	elif nic == network_options['network_manager']:
		return NetworkConfiguration(NicType.NM)
	elif nic == network_options['manual']:
		manual = ManualNetworkConfig('Configure interfaces', preset)
		return manual.run_manual()

	return preset
