from __future__ import annotations

from typing import TYPE_CHECKING, Any, Optional, Dict

from archinstall.default_profiles.profile import Profile, GreeterType
from .profile_model import ProfileConfiguration
from ..menu import Menu, MenuSelectionType, AbstractSubMenu, Selector
from ..interactions.system_conf import select_driver
from ..hardware import GfxDriver

if TYPE_CHECKING:
	_: Any


class ProfileMenu(AbstractSubMenu):
	def __init__(
		self,
		data_store: Dict[str, Any],
		preset: Optional[ProfileConfiguration] = None
	):
		if preset:
			self._preset = preset
		else:
			self._preset = ProfileConfiguration()

		super().__init__(data_store=data_store)

	def setup_selection_menu_options(self):
		self._menu_options['profile'] = Selector(
			_('Type'),
			lambda x: self._select_profile(x),
			display_func=lambda x: x.name if x else None,
			preview_func=self._preview_profile,
			default=self._preset.profile,
			enabled=True
		)

		self._menu_options['gfx_driver'] = Selector(
			_('Graphics driver'),
			lambda preset: self._select_gfx_driver(preset),
			display_func=lambda x: x.value if x else None,
			dependencies=['profile'],
			preview_func=self._preview_gfx,
			default=self._preset.gfx_driver if self._preset.profile and self._preset.profile.is_graphic_driver_supported() else None,
			enabled=self._preset.profile.is_graphic_driver_supported() if self._preset.profile else False
		)

		self._menu_options['greeter'] = Selector(
			_('Greeter'),
			lambda preset: select_greeter(self._menu_options['profile'].current_selection, preset),
			display_func=lambda x: x.value if x else None,
			dependencies=['profile'],
			default=self._preset.greeter if self._preset.profile and self._preset.profile.is_greeter_supported() else None,
			enabled=self._preset.profile.is_greeter_supported() if self._preset.profile else False
		)

	def run(self, allow_reset: bool = True) -> Optional[ProfileConfiguration]:
		super().run(allow_reset=allow_reset)

		if self._data_store.get('profile', None):
			return ProfileConfiguration(
				self._menu_options['profile'].current_selection,
				self._menu_options['gfx_driver'].current_selection,
				self._menu_options['greeter'].current_selection
			)

		return None

	def _select_profile(self, preset: Optional[Profile]) -> Optional[Profile]:
		profile = select_profile(preset)

		if profile is not None:
			if not profile.is_graphic_driver_supported():
				self._menu_options['gfx_driver'].set_enabled(False)
				self._menu_options['gfx_driver'].set_current_selection(None)
			else:
				self._menu_options['gfx_driver'].set_enabled(True)
				self._menu_options['gfx_driver'].set_current_selection(GfxDriver.AllOpenSource)

			if not profile.is_greeter_supported():
				self._menu_options['greeter'].set_enabled(False)
				self._menu_options['greeter'].set_current_selection(None)
			else:
				self._menu_options['greeter'].set_enabled(True)
				self._menu_options['greeter'].set_current_selection(profile.default_greeter_type)
		else:
			self._menu_options['gfx_driver'].set_current_selection(None)
			self._menu_options['greeter'].set_current_selection(None)

		return profile

	def _select_gfx_driver(self, preset: Optional[GfxDriver] = None) -> Optional[GfxDriver]:
		driver = preset
		profile: Optional[Profile] = self._menu_options['profile'].current_selection

		if profile:
			if profile.is_graphic_driver_supported():
				driver = select_driver(current_value=preset)

			if driver and 'Sway' in profile.current_selection_names():
				if driver.is_nvidia():
					prompt = str(_('The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?'))
					choice = Menu(prompt, Menu.yes_no(), default_option=Menu.no(), skip=False).run()

					if choice.value == Menu.no():
						return None

		return driver

	def _preview_gfx(self) -> Optional[str]:
		driver: Optional[GfxDriver] = self._menu_options['gfx_driver'].current_selection

		if driver:
			return driver.packages_text()

		return None

	def _preview_profile(self) -> Optional[str]:
		profile: Optional[Profile] = self._menu_options['profile'].current_selection
		text = ''

		if profile:
			if (sub_profiles := profile.current_selection) is not None:
				text += str(_('Selected profiles: '))
				text += ', '.join([p.name for p in sub_profiles]) + '\n'

			if packages := profile.packages_text(include_sub_packages=True):
				text += f'{packages}'

			if text:
				return text

		return None


def select_greeter(
	profile: Optional[Profile] = None,
	preset: Optional[GreeterType] = None
) -> Optional[GreeterType]:
	if not profile or profile.is_greeter_supported():
		title = str(_('Please chose which greeter to install'))
		greeter_options = [greeter.value for greeter in GreeterType]

		default: Optional[GreeterType] = None

		if preset is not None:
			default = preset
		elif profile is not None:
			default_greeter = profile.default_greeter_type
			default = default_greeter if default_greeter else None

		choice = Menu(
			title,
			greeter_options,
			skip=True,
			default_option=default.value if default else None
		).run()

		match choice.type_:
			case MenuSelectionType.Skip:
				return default

		return GreeterType(choice.single_value)

	return None


def select_profile(
	current_profile: Optional[Profile] = None,
	title: Optional[str] = None,
	allow_reset: bool = True,
	multi: bool = False
) -> Optional[Profile]:
	from archinstall.lib.profile.profiles_handler import profile_handler
	top_level_profiles = profile_handler.get_top_level_profiles()

	display_title = title
	if not display_title:
		display_title = str(_('This is a list of pre-programmed default_profiles'))

	choice = profile_handler.select_profile(
		top_level_profiles,
		current_profile=current_profile,
		title=display_title,
		allow_reset=allow_reset,
		multi=multi
	)

	match choice.type_:
		case MenuSelectionType.Selection:
			profile_selection: Profile = choice.single_value
			select_result = profile_selection.do_on_select()

			if not select_result:
				return select_profile(
					current_profile=current_profile,
					title=title,
					allow_reset=allow_reset,
					multi=multi
				)

			# we're going to reset the currently selected profile(s) to avoid
			# any stale data laying around
			match select_result:
				case select_result.NewSelection:
					profile_handler.reset_top_level_profiles(exclude=[profile_selection])
					current_profile = profile_selection
				case select_result.ResetCurrent:
					profile_handler.reset_top_level_profiles()
					current_profile = None
				case select_result.SameSelection:
					pass

			return current_profile
		case MenuSelectionType.Reset:
			return None
		case MenuSelectionType.Skip:
			return current_profile
