#!/usr/bin/env python
# -*- coding: utf-8 -*-
# File:                Ampel-core/ampel/cli/DBCommand.py
# License:             BSD-3-Clause
# Author:              valery brinnel <firstname.lastname@gmail.com>
# Date:                14.03.2021
# Last Modified Date:  20.08.2022
# Last Modified By:    valery brinnel <firstname.lastname@gmail.com>

from argparse import ArgumentParser
from collections.abc import Sequence
from typing import Any

from ampel.cli.AbsCoreCommand import AbsCoreCommand
from ampel.cli.ArgParserBuilder import ArgParserBuilder
from ampel.cli.main import AmpelArgumentParser
from ampel.core.AmpelContext import AmpelContext
from ampel.log.AmpelLogger import AmpelLogger
from ampel.log.LogFlag import LogFlag

# Help parameter descriptions
hlp = {
	'import': 'Import saved ampel databases',
	'save': 'Save a databases into file',
	'delete': 'Deletes all database starting with the provided prefix (ex: AmpelTest)',
	'prefix': 'Prefix of the collections to delete (ex: AmpelTest)',
	'config': 'Path to an ampel config file (yaml/json)',
	'secrets': 'Path to a YAML secrets store in sops format',
	'log-profile': 'One of: default, compact, headerless, verbose, debug',
	'debug': 'debug',
	'force': 'Delete potententially existing view before view creation',
	'view': 'Create or discard collection views',
	'index': 'Create or recreate collection indexes',
}

class DBCommand(AbsCoreCommand):


	@staticmethod
	def get_sub_ops() -> list[str]:
		return ['import', 'export', 'delete', 'view', 'index']


	# Mandatory implementation
	def get_parser(self, sub_op: None | str = None) -> ArgumentParser | AmpelArgumentParser:

		if sub_op in self.parsers:
			return self.parsers[sub_op]

		sub_ops = self.get_sub_ops()
		if sub_op is None or sub_op not in sub_ops:
			return AmpelArgumentParser.build_choice_help(
				'db', sub_ops, hlp, description = 'Import, export or delete ampel databases. Create or remove views.'
			)

		builder = ArgParserBuilder('db')
		builder.add_parsers(sub_ops, hlp)
		builder.notation_add_note_references()
		builder.notation_add_example_references()

		builder.req('config')
		builder.req('in', 'import')
		builder.req('out', 'export')

		builder.add_group('view', 'View arguments', sub_ops='view')
		builder.xargs(
			group='required', sub_ops='view', xargs = [
				{'name': 'create', 'action': 'store_true'},
				{'name': 'discard', 'action': 'store_true'}
			]
		)
		builder.xargs(
			group='required', sub_ops='view', xargs = [
				{'name': 'channel'},
				{'name': 'channels-or', 'nargs': '+'},
				{'name': 'channels-and', 'nargs': '+'}
			]
		)

		# Optional
		builder.opt('secrets')
		builder.opt('one-db', default=False, action='store_true')
		builder.opt('debug', action='store_true')
		builder.opt('force', 'view|index', action='store_true')
		builder.opt('col', 'index', action='append')

		builder.example('import', '-in /path/to/file')
		builder.example('export', '-out /path/to/file')
		builder.example('delete', '-mongo.prefix AmpelTest')
		builder.example('view', '-create -channel CHAN1')
		builder.example('view', '-create -channels-or CHAN1 CHAN2')
		builder.example('view', '-discard -channel CHAN1')

		self.parsers.update(
			builder.get()
		)

		return self.parsers[sub_op]


	# Mandatory implementation
	def run(self, args: dict[str, Any], unknown_args: Sequence[str], sub_op: None | str = None) -> None:

		ctx: AmpelContext = self.get_context(args, unknown_args, one_db=args['one_db'], require_existing_db=sub_op != 'index')
		db = ctx.db

		logger = AmpelLogger.from_profile(
			ctx, 'console_debug' if args['debug'] else 'console_info',
			base_flag = LogFlag.MANUAL_RUN
		)

		if sub_op == 'delete':

			logger.info(f'Deleting databases with prefix {ctx.db.prefix}')
			ctx.db.drop_all_databases()
			logger.info('Done')

		elif sub_op == 'index':

			for col_name, col_model in ctx.db.col_config.items():
				if args['col'] and col_name not in args['col']:
					continue
				col = ctx.db.get_collection(col_name)
				ctx.db.set_col_index(col, col_model, logger, force_overwrite=args['force'])

		elif sub_op == 'view':

			if 'create' not in args and 'discard' not in args:
				logger.error('Either provide "create" or "discard" in combination with view command')
				return

			try:
				if x := args.get('channel'):
					logger.info(f'{"Creating" if args["create"] else "Removing"} view for channel {x}')
					db.create_one_view(x, logger, args['force']) if args['create'] else db.delete_one_view(x, logger)
				elif x := args.get('channels_or'):
					logger.info(f'{"Creating" if args["create"] else "Removing"} view for channels {x}')
					db.create_or_view(x, logger, args['force']) if args['create'] else db.delete_or_view(x, logger)
				elif x := args.get('channels_and'):
					logger.info(f'{"Creating" if args["create"] else "Removing"} view for channels {x}')
					db.create_and_view(x, logger, args['force']) if args['create'] else db.delete_and_view(x, logger)
				else:
					logger.error('Channel(s) required\n')
					return
			except Exception as e:
				print(e)
				if 'already exists' in str(e):
					#logger.info('View already exist')
					logger.info(str(e))
				else:
					raise e

		elif sub_op in {'export', 'import'}:
			raise NotImplementedError()
