import os
import sys
import pytest
import coverage
import logging
import subprocess
# Look for the 'utils' module from where the script is running
prev_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if prev_dir not in sys.path:
    sys.path.insert(0, prev_dir)
# Utils import
from utils import context
prev_dir = os.path.dirname(prev_dir)
if prev_dir not in sys.path:
    sys.path.insert(0, prev_dir)
from kibot.out_base import BaseOutput
from kibot.pre_base import BasePreFlight
from kibot.gs import GS
from kibot.kiplot import load_actions, _import
from kibot.registrable import RegOutput, RegFilter
from kibot.misc import (MISSING_TOOL, WRONG_INSTALL, BOM_ERROR, DRC_ERROR, ERC_ERROR)
from kibot.bom.columnlist import ColumnList


cov = coverage.Coverage()
mocked_check_output_FNF = True
mocked_check_output_retOK = ''


# Important note:
# - We can't load the plug-ins twice, the import fails.
# - Once we patched them using monkey patch the patch isn't reverted unless we load them again.
# I don't know the real reason, may be related to the way we load plug-ins.
# For this reason this patch is used for more than one case.
def mocked_check_output(cmd, stderr=None):
    logging.debug('mocked_check_output called')
    if mocked_check_output_FNF:
        raise FileNotFoundError()
    else:
        if mocked_check_output_retOK:
            return mocked_check_output_retOK
        e = subprocess.CalledProcessError(10, 'rar')
        e.output = b'THE_ERROR'
        raise e


def mocked_call(cmd):
    logging.debug('Forcing fail on '+str(cmd))
    return 5


def patch_functions(m):
    m.setattr("subprocess.check_output", mocked_check_output)
    m.setattr('kibot.kiplot.exec_with_retry', mocked_call)


def run_compress(ctx, test_import_fail=False):
    with context.cover_it(cov):
        # Load the plug-ins
        load_actions()
        # Create a compress object with the dummy file as source
        out = RegOutput.get_class_for('compress')()
        out.set_tree({'options': {'format': 'RAR', 'files': [{'source': ctx.get_out_path('*')}]}})
        out.config()
        # Setup the GS output dir, needed for the output path
        GS.out_dir = '.'
        # Run the compression and catch the error
        with pytest.raises(SystemExit) as pytest_wrapped_e:
            if test_import_fail:
                _import('out_bogus', os.path.abspath(os.path.join(os.path.dirname(__file__), 'fake_plugin/out_bogus.py')))
            else:
                out.run('')
    return pytest_wrapped_e


def test_no_rar(test_dir, caplog, monkeypatch):
    global mocked_check_output_FNF
    mocked_check_output_FNF = True
    # Create a silly context to get the output path
    ctx = context.TestContext(test_dir, 'test_no_rar', 'test_v5', 'empty_zip', '')
    # The file we pretend to compress
    ctx.create_dummy_out_file('Test.txt')
    # We will patch subprocess.check_output to make rar fail
    with monkeypatch.context() as m:
        patch_functions(m)
        pytest_wrapped_e = run_compress(ctx)
    # Check we exited because rar isn't installed
    assert pytest_wrapped_e.type == SystemExit
    assert pytest_wrapped_e.value.code == MISSING_TOOL
    assert "Missing `rar` command" in caplog.text


def test_rar_fail(test_dir, caplog, monkeypatch):
    global mocked_check_output_FNF
    mocked_check_output_FNF = False
    # Create a silly context to get the output path
    ctx = context.TestContext(test_dir, 'test_no_rar', 'test_v5', 'empty_zip', '')
    # The file we pretend to compress
    ctx.create_dummy_out_file('Test.txt')
    # We will patch subprocess.check_output to make rar fail
    with monkeypatch.context() as m:
        patch_functions(m)
        pytest_wrapped_e = run_compress(ctx)
        pytest_wrapped_e2 = run_compress(ctx, test_import_fail=True)
    # Check we exited because rar isn't installed
    assert pytest_wrapped_e.type == SystemExit
    assert pytest_wrapped_e.value.code == WRONG_INSTALL
    assert "Failed to invoke rar command, error 10" in caplog.text
    # Not in the docker image ... pytest issue?
    # assert "THE_ERROR" in caplog.text
    # Check we exited because the import failed
    assert pytest_wrapped_e2.type == SystemExit
    assert pytest_wrapped_e2.value.code == WRONG_INSTALL
    assert "Unable to import plug-ins:" in caplog.text


class NoGetTargets(BaseOutput):
    def __init__(self):
        self.options = True
        self.comment = 'Fake'
        self.name = 'dummy'
        self.type = 'none'
        self._sch_related = True


class DummyPre(BasePreFlight):
    def __init__(self):
        super().__init__('dummy', True)
        self._sch_related = True


def test_no_get_targets(caplog):
    test = NoGetTargets()
    test_pre = DummyPre()
    # Also check the dependencies fallback
    GS.sch = None
    GS.sch_file = 'fake'
    with context.cover_it(cov):
        test.get_targets('')
        files = test.get_dependencies()
        files_pre = test_pre.get_dependencies()
    assert "Output 'Fake' (dummy) [none] doesn't implement get_targets(), plese report it" in caplog.text
    assert files == [GS.sch_file]
    assert files_pre == [GS.sch_file]


def test_ibom_parse_fail(test_dir, caplog, monkeypatch):
    global mocked_check_output_FNF
    mocked_check_output_FNF = False
    global mocked_check_output_retOK
    mocked_check_output_retOK = b'ERROR Parsing failed'
    # We will patch subprocess.check_output to make ibom fail
    with monkeypatch.context() as m:
        patch_functions(m)
        with context.cover_it(cov):
            # Load the plug-ins
            load_actions()
            # Create an ibom object
            out = RegOutput.get_class_for('ibom')()
            out.set_tree({})
            out.config()
            # Setup the GS output dir, needed for the output path
            #GS.out_dir = '.'
            with pytest.raises(SystemExit) as pytest_wrapped_e:
                out.run('')
    assert pytest_wrapped_e.type == SystemExit
    assert pytest_wrapped_e.value.code == BOM_ERROR
    # logging.debug(caplog.text)
    assert "Failed to create BoM" in caplog.text
    mocked_check_output_retOK = ''


def test_var_rename_no_variant():
    with context.cover_it(cov):
        # Load the plug-ins
        load_actions()
        # Create an ibom object
        filter = RegFilter.get_class_for('var_rename')()
        GS.variant = None
        # Should just return
        filter.filter(None)


def test_bom_no_sch():
    with context.cover_it(cov):
        # Load the plug-ins
        load_actions()
        # Create an ibom object
        GS.sch = None
        out = RegOutput.get_class_for('bom')()
        columns = out.options._get_columns()
        assert columns == ColumnList.COLUMNS_DEFAULT
        out = RegOutput.get_class_for('kibom')()
        options = out.options()
        columns = options.conf._get_columns()
        assert columns == ColumnList.COLUMNS_DEFAULT


def test_pre_xrc_fail(test_dir, caplog, monkeypatch):
    ctx = context.TestContext(test_dir, 'test_pre_xrc_fail', 'test_v5', 'empty_zip', '')
    with monkeypatch.context() as m:
        patch_functions(m)
        with context.cover_it(cov):
            load_actions()
            GS.set_pcb(ctx.board_file)
            sch = ctx.board_file
            GS.set_sch(sch.replace('.kicad_pcb', '.sch'))
            GS.out_dir = test_dir
            pre_drc = BasePreFlight.get_class_for('run_drc')('run_drc', True)
            pre_erc = BasePreFlight.get_class_for('run_erc')('run_erc', True)
            with pytest.raises(SystemExit) as e1:
                pre_drc.run()
            with pytest.raises(SystemExit) as e2:
                pre_erc.run()
    assert e1.type == SystemExit
    assert e1.value.code == DRC_ERROR
    assert e2.type == SystemExit
    assert e2.value.code == ERC_ERROR
    ctx.clean_up()
