'''opencos.commands.coverage - command handler for: eda coverage ...
coverage utils for merging and reporting across multiple test runs.
examples:
    eda coverage merge eda.work/test1.sim/work.acdb eda.work/test2.sim/work.acdb -o merged.acdb
    eda coverage merge test1/work.acdb test2/work.acdb -o merged.acdb --coverage-report
'''

import os
from opencos.eda_base import Command, Tool
from opencos.utils import status_constants
from opencos import util

class CommandCoverage(Command):
    '''Base class command handler for: eda coverage'''

    command_name = 'coverage'
    CHECK_REQUIRES = [Tool]

    valid_subcommands = ['merge', 'compare']

    coverage_args = {
        'output': '', # TODO(drew): default should go in the work-dir? Not local?
        'coverage-report': True,
        'coverage-report-txt':
        'coverage_merged.txt',
        'coverage-report-html': '',
        'coverage-json': '',
        'coverage-summary': True,
        'coverage-baseline': '',
        'coverage-baseline-auto': False,
        'coverage-baseline-dir': '.coverage_baselines',
        'coverage-show-delta': False,
        'coverage-delta-json': '',
        'coverage-save-baseline': False,
        'coverage-delta-threshold-improve': '',
        'coverage-delta-threshold-regress': '',
        'coverage-threshold-fail': False,
    }

    coverage_args_help = {
        'output': (
            'Output file path for merged coverage database. Example:'
            ' --output=merged.acdb or -o merged.acdb'
        ),
        'coverage-report': 'Generate coverage reports after merging (text format by default)',
        'coverage-report-txt': 'Text coverage report file name (default: coverage_merged.txt)',
        'coverage-report-html': 'HTML coverage report directory (optional, tool-dependent)',
        'coverage-json': 'Generate JSON coverage report after merging (optional)',
        'coverage-summary': 'Display coverage summary in terminal (default: True)',
        'coverage-baseline': (
            'Path to baseline tool-specific coverage database for comparison (for use with'
            ' "merge" and "compare" subcommands)'
        ),
        'coverage-baseline-auto': 'Auto-discover most recent baseline for comparison',
        'coverage-baseline-dir': (
            'Directory for baseline storage (default: .coverage_baselines)'
        ),
        'coverage-show-delta': (
            'Display coverage deltas in terminal (works with merge and compare subcommands)'
        ),
        'coverage-delta-json': (
            'Export coverage delta report as JSON (works with merge and compare subcommands)'
        ),
        'coverage-save-baseline': 'Save merged result as new baseline for future comparisons',
        'coverage-delta-threshold-improve': 'Minimum % improvement required (e.g., 2.0)',
        'coverage-delta-threshold-regress': 'Maximum % regression allowed (e.g., -1.0)',
        'coverage-threshold-fail': 'Exit with error if delta thresholds not met',
    }

    coverage_args_kwargs = {}


    def __init__(self, config: dict):
        Command.__init__(self, config=config)

        self.args.update(self.coverage_args)
        self.args_help.update(self.coverage_args_help)
        self.args_kwargs.update(self.coverage_args_kwargs)

        self.coverage_files = []
        self.unparsed_args = []
        self.subcommand = ''


    def process_tokens(
            self, tokens: list, process_all: bool = True, pwd: str = os.getcwd()
    ) -> list:
        '''Process command tokens and extract subcommand + coverage files'''
        self.unparsed_args = Command.process_tokens(self, tokens=tokens, process_all=False, pwd=pwd)

        if not self.unparsed_args:
            self.error(
                "eda coverage requires a subcommand. Usage: eda coverage merge FILE1 FILE2 ...",
                "valid subcommands are: " + ' '.join(self.valid_subcommands),
                error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR
            )
            return []

        self.subcommand = self.unparsed_args.pop(0)
        if self.subcommand not in self.valid_subcommands:
            self.error(
                f"Unknown coverage subcommand: '{self.subcommand}'. Supported:",
                ' '.join(self.valid_subcommands),
                error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR
            )
            return []

        for arg in self.unparsed_args:
            if os.path.isfile(arg):
                self.coverage_files.append(os.path.abspath(arg))
            else:
                self.error(f"Coverage file not found: {arg}",
                           error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
                return []

        if (self.subcommand == 'merge' and len(self.coverage_files) < 2) or \
           (self.subcommand == 'compare' and len(self.coverage_files) != 2):
            count_msg = 'at least 2' if self.subcommand == 'merge' else 'exactly 2'
            self.error(f"Coverage {self.subcommand} requires {count_msg} input files",
                       error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
            return []

        if self.subcommand == 'merge' and not self.args['output']:
            util.info("eda coverage merge: --output=FILE not set, tool will have to use default")

        util.info(f"coverage {self.subcommand}: {len(self.coverage_files)} input files")

        # Before caling run() / do_it(), let's set up a unique self.args['work-dir'], if
        # one wasn't already set, so we our sub-command included.
        self.do_it()

        # technically we should remove the unparsed_args and return the leftovers, but this
        # is alos acceptable:
        return []


    def do_it(self) -> None:
        '''Execute coverage operation - should be overridden by tool-specific implementations'''
        raise NotImplementedError
