from typing import override

from archinstall.lib.disk.fido import Fido2
from archinstall.lib.menu.abstract_menu import AbstractSubMenu
from archinstall.lib.menu.helpers import Confirmation, Selection
from archinstall.lib.menu.util import get_password
from archinstall.lib.models.authentication import AuthenticationConfiguration, U2FLoginConfiguration, U2FLoginMethod
from archinstall.lib.models.users import Password, User
from archinstall.lib.output import FormattedOutput
from archinstall.lib.translationhandler import tr
from archinstall.lib.user.user_menu import select_users
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType


class AuthenticationMenu(AbstractSubMenu[AuthenticationConfiguration]):
	def __init__(self, preset: AuthenticationConfiguration | None = None):
		if preset:
			self._auth_config = preset
		else:
			self._auth_config = AuthenticationConfiguration()

		menu_options = self._define_menu_options()
		self._item_group = MenuItemGroup(menu_options, checkmarks=True)

		super().__init__(
			self._item_group,
			config=self._auth_config,
			allow_reset=True,
		)

	@override
	async def show(self) -> AuthenticationConfiguration | None:
		return await super().show()

	def _define_menu_options(self) -> list[MenuItem]:
		return [
			MenuItem(
				text=tr('Root password'),
				action=lambda x: select_root_password(),
				preview_action=self._prev_root_pwd,
				key='root_enc_password',
			),
			MenuItem(
				text=tr('User account'),
				action=self._create_user_account,
				preview_action=self._prev_users,
				key='users',
			),
			MenuItem(
				text=tr('U2F login setup'),
				action=select_u2f_login,
				value=self._auth_config.u2f_config,
				preview_action=self._prev_u2f_login,
				key='u2f_config',
			),
		]

	async def _create_user_account(self, preset: list[User] | None = None) -> list[User]:
		preset = [] if preset is None else preset
		users = await select_users(preset=preset)
		return users

	def _prev_users(self, item: MenuItem) -> str | None:
		users: list[User] | None = item.value

		if users:
			return FormattedOutput.as_table(users)
		return None

	def _prev_root_pwd(self, item: MenuItem) -> str | None:
		if item.value is not None:
			password: Password = item.value
			return f'{tr("Root password")}: {password.hidden()}'
		return None

	def _depends_on_u2f(self) -> bool:
		devices = Fido2.get_fido2_devices()
		if not devices:
			return False
		return True

	def _prev_u2f_login(self, item: MenuItem) -> str | None:
		if item.value is not None:
			u2f_config: U2FLoginConfiguration = item.value

			login_method = u2f_config.u2f_login_method.display_value()
			output = tr('U2F login method: ') + login_method

			output += '\n'
			output += tr('Passwordless sudo: ') + (tr('Enabled') if u2f_config.passwordless_sudo else tr('Disabled'))

			return output

		devices = Fido2.get_fido2_devices()
		if not devices:
			return tr('No U2F devices found')

		return None


async def select_root_password() -> Password | None:
	password = await get_password(header=tr('Enter root password'), allow_skip=True)
	return password


async def select_u2f_login(preset: U2FLoginConfiguration | None) -> U2FLoginConfiguration | None:
	devices = Fido2.get_fido2_devices()
	if not devices:
		return None

	items = []
	for method in U2FLoginMethod:
		items.append(MenuItem(method.display_value(), value=method))

	group = MenuItemGroup(items)

	if preset is not None:
		group.set_selected_by_value(preset.u2f_login_method)

	result = await Selection[U2FLoginMethod](
		group,
		allow_skip=True,
		allow_reset=True,
	).show()

	match result.type_:
		case ResultType.Selection:
			u2f_method = result.get_value()
			header = tr('Enable passwordless sudo?')

			result_sudo = await Confirmation(
				header=header,
				allow_skip=True,
				preset=False,
			).show()

			passwordless_sudo = result_sudo.item() == MenuItem.yes()

			return U2FLoginConfiguration(
				u2f_login_method=u2f_method,
				passwordless_sudo=passwordless_sudo,
			)
		case ResultType.Skip:
			return preset
		case ResultType.Reset:
			return None
