'''Callabe EDASuggester from opencos.tui, as a smart autocomplete for eda-ui'''

import argparse
import shlex

from textual.suggester import Suggester

from opencos import util, eda_config, eda_base, eda_tool_helper, engine

UTIL_ARGPARSER = util.get_argparser()
CONFIG_ARGPARSER = eda_config.get_argparser()
EDA_BASE_ARGPARSER = eda_base.get_argparser()


def run_argparser_on_list(tokens: list, parser) -> (argparse.Namespace, list):
    '''Consumes args from an argparser, returns (parsed Namespace, unparsed list)'''
    parsed = None
    unparsed = tokens
    try:
        parsed, unparsed = parser.parse_known_args(tokens + [''])
        unparsed = list(filter(None, unparsed))
    except argparse.ArgumentError:
        util.error(f'problem attempting to parse_known_args for tokens={tokens}')
    return parsed, unparsed



class EDASuggester(Suggester):
    '''Used by opencos.tui.MainScreen.compose for Input(..., suggester)'''

    def __init__(self, config):
        super().__init__(use_cache=False)
        self.config = config


    async def _find_best_match(self, last_word: str, active_parsers: list) -> str:
        '''Finds best match of 'last_word' in a list of argparsers'''

        # 1. If it starts with '--', pull all options from all 4 parsers
        if last_word.startswith("-"):
            all_options = []
            for p in active_parsers:
                # This reaches into argparse internals to get long/short flags
                # pylint: disable=protected-access
                all_options.extend(p._option_string_actions.keys())

            matches = [opt for opt in all_options if opt.startswith(last_word)]
            return f'{matches[0]}' if matches else ''

        # 2. If it's a command/target (not a flag)
        # Check against your 'Targets' tree data
        # TODO(drew): this does not work yet, would have use "eda targets" equivalent
        # on this last_word:
        #targets = self.config.get("all_targets", [])
        #matches = [t for t in targets if t.startswith(last_word)]
        #return matches[0] if matches else None
        return ''


    async def get_suggestion(self, value: str) -> str:
        '''
        Given a value (space separated line), attempts to get a suggested
        arg or word, given EDA's argparsing flow
        '''

        parts = shlex.split(value)

        if not parts:
            return ''

        # 1. Determine Context
        # Use base parsers to 'eat' the global flags
        unparsed = parts.copy()
        _, unparsed = run_argparser_on_list(
            tokens=unparsed, parser=UTIL_ARGPARSER
        )
        _, unparsed = run_argparser_on_list(
            tokens=unparsed, parser=CONFIG_ARGPARSER
        )
        eda_parsed, unparsed = run_argparser_on_list(
            tokens=unparsed, parser=EDA_BASE_ARGPARSER
        )

        # 2. Identify Command + Tool handling class parser:
        command = engine.get_and_remove_command_from_tokens(
            tokens=unparsed, config=self.config, is_sub_command=False
        )
        if command and command in self.config.get('command_has_subcommands', []):
            _ = engine.get_and_remove_command_from_tokens(
                tokens=unparsed, config=self.config, is_sub_command=True
            )

        tool = eda_parsed.tool

        active_parsers = [
            UTIL_ARGPARSER,
            CONFIG_ARGPARSER,
            EDA_BASE_ARGPARSER
        ]

        if not command or command not in self.config.get('DEFAULT_HANDLERS', {}):
            return self._find_best_match(parts[-1], active_parsers)

        _auto_tools_choices = self.config.get('auto_tools_order', {}).get(command, [])

        cls = None
        if tool and \
           tool in _auto_tools_choices and \
           tool in self.config.get('tools_loaded', []):

            cls = eda_tool_helper.get_handler(tool=tool, eda_command=command,
                                              config=self.config, warn=False)

            # TODO(drew): we may be able to avoid this if we use cls.args (dict) but we won't have
            # bool --no-[bool-arg]

            cmd_handler = cls(config=self.config)
            active_parsers.extend(cmd_handler.get_argparser())

        return self._find_best_match(parts[-1], active_parsers)
