"""
This module provides utility functions for printing package version information
in a structured format, including the main package and its dependencies.

Functions:

- :py:func:`show_version`: Print the version of the main package and its dependencies.
"""

# Imports
import sys

from .config import StouputilsConfig as Cfg


# Show version function
def show_version(main_package: str = "stouputils", primary_color: str = Cfg.CYAN, secondary_color: str = Cfg.GREEN, max_depth: int = 2) -> None:
	""" Print the version of the main package and its dependencies.

	Used by the "stouputils --version" command.

	Args:
		main_package	(str):	Name of the main package to show version for
		primary_color	(str):	Color to use for the primary package name (defaults to cyan)
		secondary_color	(str):	Color to use for the secondary package names (defaults to green)
		max_depth		(int):	Maximum depth for dependency tree (<= 2 for flat, >=3 for tree)
	"""
	# Imports
	from importlib.metadata import requires, version
	def ver(package_name: str) -> str:
		try:
			return version(package_name)
		except Exception:
			return ""

	def get_deps(package_name: str) -> list[str]:
		""" Get the list of dependency names for a package """
		try:
			deps: list[str] = requires(package_name) or []
			# Remove duplicates while preserving order, then sort
			unique_deps: list[str] = list(dict.fromkeys([
				dep
					.split(">")[0]
					.split("<")[0]
					.split("=")[0]
					.split("[")[0]
					.split(";")[0]
					.strip()
				for dep in deps
			]))
			return sorted(unique_deps)
		except Exception:
			return []

	def print_tree(package_name: str, prefix: str = "", is_last: bool = True, visited: set[str] | None = None, fully_displayed: set[str] | None = None, depth: int = 0, max_depth: int = 3) -> None:
		""" Recursively print the dependency tree """
		if visited is None:
			visited = set()
		if fully_displayed is None:
			fully_displayed = set()

		# Prevent infinite recursion and limit depth
		if package_name in visited or depth > max_depth:
			return
		visited.add(package_name)

		# Get version
		v: str = ver(package_name).split("version: ")[-1]
		if not v:
			return

		# Determine the tree characters
		connector: str = "└── " if is_last else "├── "

		# Check if this package was already fully displayed
		already_shown: bool = package_name in fully_displayed

		# Print current package
		if depth == 0:
			print(f"{primary_color}{package_name}  {secondary_color}v{v}{Cfg.RESET}")
		else:
			if already_shown:
				print(f"{prefix}{connector}{primary_color}{package_name}  {secondary_color}v{v} {Cfg.YELLOW}[Already shown ^]{Cfg.RESET}")
				# Still mark as fully displayed even when already shown
				fully_displayed.add(package_name)
				return
			else:
				print(f"{prefix}{connector}{primary_color}{package_name}  {secondary_color}v{v}{Cfg.RESET}")

		# Get dependencies
		deps: list[str] = get_deps(package_name)

		# Filter dependencies that will actually be displayed (have a version)
		valid_deps: list[str] = [dep for dep in deps if ver(dep)]

		# Print dependencies recursively
		for i, dep in enumerate(valid_deps):
			# Determine if this is the last element to display
			is_last_dep: bool = (i == len(valid_deps) - 1)

			# Extension is based on whether the CURRENT node is last, not the child
			extension: str = "    " if is_last else "│   "
			new_prefix: str = prefix + extension if depth > 0 else ""
			print_tree(dep, new_prefix, is_last_dep, visited.copy(), fully_displayed, depth + 1, max_depth)

		# Mark this package as fully displayed (with all its dependencies)
		fully_displayed.add(package_name)

	# Get Python version header
	python_version: str = f" Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro} "

	if max_depth >= 3:
		# Display as tree structure
		minimum_separator_length: int = len(python_version) + 10
		separator_length: int = minimum_separator_length
		python_text_length: int = len(python_version)
		left_dashes: int = (separator_length - python_text_length) // 2
		right_dashes: int = separator_length - python_text_length - left_dashes
		separator_with_python: str = "─" * left_dashes + python_version + "─" * right_dashes
		separator: str = "─" * separator_length

		print(f"{primary_color}{separator_with_python}{Cfg.RESET}")
		print_tree(main_package, max_depth=max_depth - 1)
		print(f"{primary_color}{separator}{Cfg.RESET}")
	else:
		# Display as flat list (original behavior)
		deps: list[str] = requires(main_package) or []
		dep_names: list[str] = sorted([
			dep
				.split(">")[0]
				.split("<")[0]
				.split("=")[0]
				.split("[")[0]
			for dep in deps
		])
		all_deps: list[tuple[str, str]] = [
			(x, ver(x).split("version: ")[-1])
			for x in (main_package, *dep_names)
		]
		all_deps = [pair for pair in all_deps if pair[1]]  # Filter out packages with no version found
		longest_name_length: int = max(len(name) for name, _ in all_deps)
		longest_version_length: int = max(len(ver) for _, ver in all_deps)

		minimum_separator_length: int = len(python_version) + 10	# Always at least 5 dashes on each side
		separator_length: int = max(minimum_separator_length, longest_name_length + longest_version_length + 4)
		python_text_length: int = len(python_version)
		left_dashes: int = (separator_length - python_text_length) // 2
		right_dashes: int = separator_length - python_text_length - left_dashes
		separator_with_python: str = "─" * left_dashes + python_version + "─" * right_dashes
		separator: str = "─" * separator_length

		for pkg, v in all_deps:
			pkg_spacing: str = " " * (longest_name_length - len(pkg))

			# Highlight the main package with a different style
			if pkg == main_package:
				print(f"{primary_color}{separator_with_python}{Cfg.RESET}")
				print(f"{primary_color}{pkg}{pkg_spacing}  {secondary_color}v{v}{Cfg.RESET}")
				print(f"{primary_color}{separator}{Cfg.RESET}")
			else:
				print(f"{primary_color}{pkg}{pkg_spacing}  {secondary_color}v{v}{Cfg.RESET}")
	return

# Show version cli
def show_version_cli() -> None:
	""" Handle the "stouputils --version" CLI command """
	# Determine max depth (flat or tree structure)
	max_depth: int = 2  # Flat by default

	# Check for tree argument
	if "--tree" in sys.argv or "-t" in sys.argv:
		# Find position of tree argument
		pos: int = sys.argv.index("--tree") if "--tree" in sys.argv else sys.argv.index("-t")

		# Check for depth argument
		if pos + 1 < len(sys.argv):
			try:
				max_depth = int(sys.argv[pos + 1])
				sys.argv.pop(pos + 1)  # Remove depth argument
			except ValueError:
				pass  # Keep default if conversion fails
		sys.argv.pop(pos)  # Remove the --tree/-t argument

	# Handle specific package argument
	if len(sys.argv) >= 3 and not sys.argv[2].startswith("-"):
		return show_version(sys.argv[2], max_depth=max_depth)

	# Else, show default package version
	return show_version(max_depth=max_depth)

