#!python
#
# BATSPP
#
# Shell style tests using bats-core
#
## TODO: add timer and print execution time
## TODO: use a separate pass of the test source to fill in the missing test names.
## TODO: implement --discover flag to discover all tests file in a folder.
## TODO: beautify exceptions, catch and hide python traceback, only show batspp traceback.


"""
BATSPP

Shell style tests using bats-core

You can run tests for aliases and more using the
command line with '$ [command]' followed by the
expected output:

 $ echo -e "hello\nworld"
 hello
 world

 $ echo "this is a test" | wc -c
 15

Also you can test bash functions:
 [functions + args] => [expected]:
 fibonacci 9 => "0 1 1 2 3 5 8 13 21 34"
"""


# Standard packages
from os import getpid as os_getpid
from sys import argv as sys_argv


# Installed packages
from mezcla.main import Main
from mezcla import system
from mezcla import debug
from mezcla import glue_helpers as gh
from mezcla import text_utils


# Local packages
from batspp.__version__ import __version__
from batspp.batspp_opts import BatsppOpts
from batspp.batspp_args import BatsppArgs
from batspp.batspp_test import BatsppTest


# Command-line labels and
# enviroment variables constants
FILE = 'file'
SAVE = 'save'
SOURCES = 'sources'
OUTPUT = 'output'
DEBUG = 'debug'
HEXDUMP_DEBUG = 'hexdump_debug'
VERBOSE_DEBUG = 'verbose_debug'
TMP = 'TMP'
TEMP_DIR = 'temp_dir'
COPY_DIR = 'copy_dir'
VISIBLE_PATHS = 'visible_paths'
RUN_OPTS = 'run_options'
SKIP_RUN = 'skip_run'
OMIT_TRACE = 'omit_trace'
DISABLE_ALIASES = 'disable_aliases'
VERSION = 'version'


class Batspp(Main):
    """Argument processing class"""

    # Class-level member variables for arguments
    # (avoids need for class constructor)
    file = ''
    save_path = ''
    sources = []
    output = False
    hexdump_debug = False
    verbose_debug = False
    debug = ''
    temp_dir = ''
    copy_dir = ''
    visible_paths = []
    run_opts = ''
    skip_run = False
    omit_trace = False
    disable_aliases = False
    version = False

    def setup(self) -> None:
        """Process arguments"""
        debug.trace(7, f'batspp.setup() self={self}')

        tmp = system.getenv_text(TMP, "/tmp", "Temporary directory")

        # Check the command-line/enviroment vars options
        self.file = self.get_parsed_argument(FILE, self.file)
        self.save_path = self.get_entered_text(SAVE, self.temp_file)
        self.sources = text_utils.extract_string_list(self.get_entered_text(SOURCES, ''))
        self.output = self.get_entered_bool(OUTPUT, self.output)
        self.hexdump_debug = self.get_entered_bool(HEXDUMP_DEBUG, self.hexdump_debug)
        self.verbose_debug = self.get_entered_bool(VERBOSE_DEBUG, self.verbose_debug)
        self.debug = self.get_entered_text(DEBUG, self.debug)
        self.temp_dir = self.get_entered_text(TEMP_DIR, gh.form_path(tmp, f"batspp-{os_getpid()}"))
        self.copy_dir = self.get_entered_text(COPY_DIR, self.copy_dir)
        self.visible_paths = text_utils.extract_string_list(self.get_entered_text(VISIBLE_PATHS, ''))
        self.run_opts = self.get_entered_text(RUN_OPTS, self.run_opts)
        self.skip_run = self.get_entered_bool(SKIP_RUN, self.skip_run)
        self.omit_trace = self.get_entered_bool(OMIT_TRACE,  self.omit_trace)
        self.disable_aliases = self.get_entered_bool(DISABLE_ALIASES,  self.disable_aliases)
        self.version = self.has_parsed_option(VERSION)

    def run_main_step(self) -> None:
        """Process main script"""

        if self.version:
            print(f'Batspp version {__version__}')
            return

        # Build tests
        test = BatsppTest()
        opts = BatsppOpts(
            hexdump_debug = self.hexdump_debug,
            verbose_debug = self.verbose_debug,
            omit_trace = self.omit_trace,
            disable_aliases = self.disable_aliases,
            )
        args = BatsppArgs(
            sources = self.sources,
            temp_dir = self.temp_dir,
            visible_paths = self.visible_paths,
            run_opts = self.run_opts,
            copy_dir = self.copy_dir,
            debug = self.debug,
            )

        if self.save_path:
            test.transpile_and_save_bats(self.file, self.save_path, args=args, opts=opts)

        if self.output:
            print(test.transpile_to_bats(self.file, args=args, opts=opts))
        elif not self.skip_run:
            print(test.run(self.file, args=args, opts=opts))

    def get_entered_bool(
            self,
            label:str,
            default:bool=False,
            ) -> bool:
        """
        Return entered LABEL var/arg bool by command-line or enviroment variable,
        also can be specified a DEFAULT value
        """
        result = (self.has_parsed_option(label.lower()) or
                  system.getenv_bool(var=label.upper()))
        result = result if result else default
        debug.trace(7, f'batspp.get_entered_bool(label={label}) => {result}')
        return result

    def get_entered_text(
            self,
            label:str,
            default:str='',
            ) -> str:
        """
        Return entered LABEL var/arg text by command-line or enviroment variable,
        also can be specified a DEFAULT value
        """
        result = (self.get_parsed_argument(label=label.lower()) or
                  system.getenv_text(var=label.upper()))
        result = result if result else default
        debug.trace(7, f'batspp.get_entered_text(label={label}) => {result}')
        return result


if __name__ == '__main__':

    print_version = (f"--{VERSION}" in " ".join(sys_argv))

    app = Batspp(
        description = __doc__,
        positional_arguments = [
            (FILE, 'Test filename')
            ] if not print_version else None,
        boolean_options = [
            (VERSION, 'Show installed Batspp version'),
            (OUTPUT, 'Print generated test'),
            (HEXDUMP_DEBUG, 'Print hexdump debug when an assertion fail'),
            (VERBOSE_DEBUG, 'Print debug verbose when an assertion fail'),
            (SKIP_RUN, 'Do not run the test script'),
            (OMIT_TRACE, 'Omit actual/expected trace from test file'),
            (DISABLE_ALIASES, 'Disable alias expansion'),
            ],
        text_options = [
            (SAVE, 'Specify path to save the generated test file'),
            (SOURCES, 'Specify files to be sourced, i.e "file1.bash file2.bash"'),
            (TEMP_DIR, 'Temporary directory to use for tests'),
            (VISIBLE_PATHS, 'Make paths visible to tests file'),
            (RUN_OPTS, 'Options for run Bats command'),
            (COPY_DIR, 'Copy directory to temp. dir for input files, etc.'),
            (DEBUG, 'Add custom debug to actual/expected values'),
            ],
        manual_input = True,
        )

    app.run()
