#!/usr/bin/python3.13

import os
import sys
import re
import argparse
import textwrap
import subprocess
from termcolor import colored, cprint

sys.path.insert(0, os.path.abspath(os.path.dirname(__file__) + '/..'))
import opi
from opi.plugins import PluginManager
from opi.version import __version__
from opi.state import global_state


class PreserveWhiteSpaceWrapRawTextHelpFormatter(argparse.RawTextHelpFormatter):
	def __add_whitespace(self, idx, iWSpace, text):
		if idx == 0:
			return text
		return (' ' * iWSpace) + text

	def _split_lines(self, text, width):
		textRows = text.splitlines()
		for idx,line in enumerate(textRows):
			search = re.search(r'\s*[\d\-]*\.?\s*', line)
			if line.strip() == '':
				textRows[idx] = ' '
			elif search:
				lWSpace = search.end()
				lines = [self.__add_whitespace(i,lWSpace,x) for i,x in enumerate(textwrap.wrap(line, width))]
				textRows[idx] = lines
		return [item for sublist in textRows for item in sublist]


def setup_argparser(plugin_manager):
	ap = argparse.ArgumentParser(
		formatter_class=PreserveWhiteSpaceWrapRawTextHelpFormatter,
		description=textwrap.dedent('''\
			openSUSE Package Installer
			==========================

			Search and install almost all packages available for openSUSE and SLE:
			  1. openSUSE Build Service
			  2. Packman
			  3. Popular packages for various vendors

		'''),
		epilog=textwrap.dedent('''\
			Also these queries (provided by plugins) can be used to install packages from various other vendors:
		''') + plugin_manager.get_plugin_string(' ' * 2))

	ap.add_argument('query', nargs='*', type=str, help=textwrap.dedent('''\
		can be any package name or part of it and will be searched for both at the openSUSE Build Service and Packman.
		If multiple query arguments are provided only results matching all of them are returned.
		Please use the -m option if you want to use the query arguments as individual package queries.
	'''))
	ap.add_argument('-V', '--version', action='version', version=f'opi version {__version__}')
	ap.add_argument('-n', dest='non_interactive', action='store_true', help='run in non interactive mode')
	ap.add_argument('-P', dest='no_plugins', action='store_true', help="don't run any plugins - only search repos, OBS and Packman")
	ap.add_argument('-m', dest='multi_install', action='store_true', help='use query args as space separated package queries')
	ap.add_argument('-v', '--verbose', dest='verbose_mode', action='store_true', help='make the operation more talkative')

	return ap


def repo_query(query):
	try:
		print(f'Searching repos for: {(" ".join(query) if isinstance(query, list) else query)}')

		packages = []
		packages.extend(opi.search_published_packages('openSUSE', query))
		packages.extend(opi.search_published_packages('Packman', query))
		packages = opi.sort_uniq_packages(packages)
		if len(packages) == 0:
			print('No package found.')
			return

		# Print and select a package name option
		package_names = opi.get_package_names(packages)
		selected_pkg_name = opi.ask_for_option(package_names)
		print('You have selected package name:', selected_pkg_name)

		# Inject packages from local repos
		packages = opi.search_local_repos(selected_pkg_name) + packages

		instable_pkg_options = [pkg for pkg in packages if pkg.name == selected_pkg_name]

		# Print and select a package option
		selected_pkg = opi.ask_for_option(instable_pkg_options, option_filter=opi.format_pkg_option, disable_pager=True)
		print('You have selected package:', opi.format_pkg_option(selected_pkg, table=False))
		if isinstance(selected_pkg, opi.OBSPackage):
			if selected_pkg.is_from_personal_project():
				cprint(
					'BE CAREFUL! The package is from a personal repository and NOT reviewed by others.\n'
					'You can ask the author to submit the package to development projects and openSUSE:Factory.\n'
					'Learn more at https://en.opensuse.org/openSUSE:How_to_contribute_to_Factory',
					'red'
				)
			elif selected_pkg.project == 'openSUSE:Factory':
				cprint(
					'BE CAREFUL! You are about to add the Factory Repository.\n'
					'This repo contains the unreleased Tumbleweed distro before openQA tests have been run.\n'
					'Only proceed if you know what you are doing!',
					'yellow'
				)
				if not opi.ask_yes_or_no('Do you want to continue?', default_answer='n'):
					return
			elif selected_pkg.project.startswith('openSUSE:Factory:Staging'):
				cprint(
					'BE CAREFUL! You are about to add a Factory Staging Repository.\n'
					'This repo is used to test submissions to the Factory repo (the unreleased Tumbleweed distro before openQA tests have been run).\n'
					'Only proceed if you know what you are doing!',
					'yellow'
				)
				if not opi.ask_yes_or_no('Do you want to continue?', default_answer='n'):
					return

		# Install selected package
		selected_pkg.install()
	except (opi.NoOptionSelected, opi.HTTPError):
		return


if __name__ == '__main__':
	try:
		pm = PluginManager()
		ap = setup_argparser(pm)
		args = ap.parse_args()

		if not args.query:
			ap.print_help()
			sys.exit()

		if args.verbose_mode:
			global_state.arg_verbose_mode = True

		if args.non_interactive:
			global_state.arg_non_interactive = True
			if subprocess.run(['sudo', '-n', 'true']).returncode != 0:
				print('Error: In non-interactive mode this command must be run as root')
				print('       or sudo must not require interaction.')
				sys.exit(1)

		# Search plugins
		if not args.no_plugins:
			# Iterate over queries, copy list as modifying it from within the loop
			for query in list(args.query):
				# Try to find a matching plugin for the query (and run it); runs just first query if not in multi_install mode
				if pm.run(query):
					# After plugin successfully ran, remove from queries to not try again in repo search
					args.query.remove(query)
					if not args.multi_install:
						sys.exit()

		# Search repos
		if not args.multi_install:
			repo_query(args.query)
		else:
			for query in args.query:
				repo_query(query)

	except KeyboardInterrupt:
		print()
