from collections.abc import Awaitable, Callable
from typing import Any, Literal, override

from textual.validation import ValidationResult, Validator

from archinstall.lib.translationhandler import tr
from archinstall.tui.components import InputInfo, InputScreen, LoadingScreen, NotifyScreen, OptionListScreen, SelectListScreen, TableSelectionScreen
from archinstall.tui.menu_item import MenuItemGroup
from archinstall.tui.result import Result, ResultType


class Selection[ValueT]:
	def __init__(
		self,
		group: MenuItemGroup,
		header: str | None = None,
		title: str | None = None,
		allow_skip: bool = True,
		allow_reset: bool = False,
		preview_location: Literal['right', 'bottom'] | None = None,
		multi: bool = False,
		enable_filter: bool = False,
		wrap_preview: bool = False,
	):
		self._header = header
		self._title = title
		self._group: MenuItemGroup = group
		self._allow_skip = allow_skip
		self._allow_reset = allow_reset
		self._preview_location = preview_location
		self._multi = multi
		self._enable_filter = enable_filter
		self._wrap_preview = wrap_preview

	async def show(self) -> Result[ValueT]:
		if self._multi:
			result = await SelectListScreen[ValueT](
				self._group,
				header=self._header,
				allow_skip=self._allow_skip,
				allow_reset=self._allow_reset,
				preview_location=self._preview_location,
				enable_filter=self._enable_filter,
				wrap_preview=self._wrap_preview,
			).run()
		else:
			result = await OptionListScreen[ValueT](
				self._group,
				header=self._header,
				title=self._title,
				allow_skip=self._allow_skip,
				allow_reset=self._allow_reset,
				preview_location=self._preview_location,
				enable_filter=self._enable_filter,
				wrap_preview=self._wrap_preview,
			).run()

		if result.type_ == ResultType.Reset:
			confirmed = await _confirm_reset()

			if confirmed.get_value() is False:
				return await self.show()

		return result


class Confirmation:
	def __init__(
		self,
		header: str,
		group: MenuItemGroup | None = None,
		allow_skip: bool = True,
		allow_reset: bool = False,
		preset: bool = False,
		preview_location: Literal['bottom'] | None = None,
		preview_header: str | None = None,
	):
		self._header = header
		self._allow_skip = allow_skip
		self._allow_reset = allow_reset
		self._preset = preset
		self._preview_location = preview_location
		self._preview_header = preview_header

		if not group:
			self._group = MenuItemGroup.yes_no()
			self._group.set_focus_by_value(preset)
		else:
			self._group = group

	async def show(self) -> Result[bool]:
		result = await OptionListScreen[bool](
			self._group,
			header=self._header,
			allow_skip=self._allow_skip,
			allow_reset=self._allow_reset,
			preview_location=self._preview_location,
			enable_filter=False,
		).run()

		if result.type_ == ResultType.Reset:
			confirmed = await _confirm_reset()

			if confirmed.get_value() is False:
				return await self.show()

		return result


class Notify:
	def __init__(self, header: str):
		self._header = header

	async def show(self) -> Result[bool]:
		_ = await NotifyScreen(header=self._header).run()
		return Result.true()


class GenericValidator(Validator):
	def __init__(self, validator_callback: Callable[[str], str | None]) -> None:
		super().__init__()

		self._validator_callback = validator_callback

	@override
	def validate(self, value: str) -> ValidationResult:
		result = self._validator_callback(value)

		if result is not None:
			return self.failure(result)

		return self.success()


class Input:
	def __init__(
		self,
		header: str | None = None,
		placeholder: str | None = None,
		password: bool = False,
		default_value: str | None = None,
		allow_skip: bool = True,
		allow_reset: bool = False,
		validator_callback: Callable[[str], str | None] | None = None,
		info_callback: Callable[[str], InputInfo | None] | None = None,
	):
		self._header = header
		self._placeholder = placeholder
		self._password = password
		self._default_value = default_value
		self._allow_skip = allow_skip
		self._allow_reset = allow_reset
		self._validator_callback = validator_callback
		self._info_callback = info_callback

	async def show(self) -> Result[str]:
		validator = GenericValidator(self._validator_callback) if self._validator_callback else None

		result = await InputScreen(
			header=self._header,
			placeholder=self._placeholder,
			password=self._password,
			default_value=self._default_value,
			allow_skip=self._allow_skip,
			allow_reset=self._allow_reset,
			validator=validator,
			info_callback=self._info_callback,
		).run()

		if result.type_ == ResultType.Reset:
			confirmed = await _confirm_reset()

			if confirmed.get_value() is False:
				return await self.show()

		return result


class Loading[ValueT]:
	def __init__(
		self,
		header: str | None = None,
		timer: int = 3,
		data_callback: Callable[[], Any] | None = None,
	):
		self._header = header
		self._timer = timer
		self._data_callback = data_callback

	async def show(self) -> Result[ValueT]:
		if self._data_callback:
			result = await LoadingScreen[ValueT](
				header=self._header,
				data_callback=self._data_callback,
			).run()
			return result
		else:
			_ = await LoadingScreen(
				timer=self._timer,
				header=self._header,
			).run()
			return Result.true()


class Table[ValueT]:
	def __init__(
		self,
		header: str | None = None,
		group: MenuItemGroup | None = None,
		group_callback: Callable[[], Awaitable[MenuItemGroup]] | None = None,
		presets: list[ValueT] | None = None,
		allow_reset: bool = False,
		allow_skip: bool = False,
		loading_header: str | None = None,
		multi: bool = False,
		preview_location: Literal['bottom'] | None = None,
		preview_header: str | None = None,
	):
		self._header = header
		self._group = group
		self._data_callback = group_callback
		self._loading_header = loading_header
		self._allow_skip = allow_skip
		self._allow_reset = allow_reset
		self._multi = multi
		self._presets = presets
		self._preview_location = preview_location
		self._preview_header = preview_header

		if self._group is None and self._data_callback is None:
			raise ValueError('Either data or data_callback must be provided')

	async def show(self) -> Result[ValueT]:
		result = await TableSelectionScreen[ValueT](
			header=self._header,
			group=self._group,
			group_callback=self._data_callback,
			allow_skip=self._allow_skip,
			allow_reset=self._allow_reset,
			loading_header=self._loading_header,
			multi=self._multi,
			preview_location=self._preview_location,
			preview_header=self._preview_header,
		).run()

		if result.type_ == ResultType.Reset:
			confirmed = await _confirm_reset()

			if confirmed.get_value() is False:
				return await self.show()

		return result


async def _confirm_reset() -> Result[bool]:
	return await OptionListScreen[bool](
		MenuItemGroup.yes_no(),
		header=tr('Are you sure you want to reset this setting?'),
		allow_skip=False,
		allow_reset=False,
	).run()
