import itertools
import os
import sys
import unittest
from pathlib import Path
from unittest.mock import patch

import invoke

from dbt_invoke import properties
from dbt_invoke.internal import _utils

PARENT_DIR = Path(__file__).parent


class TestDbtInvoke(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        """
        Overrides unittest.TestCase.setUpClass

        :return: None
        """
        cls.logger = _utils.get_logger('dbt-invoke', level='DEBUG')
        cls.config_path = Path(PARENT_DIR, 'test_config.yml')
        cls.config = _utils.parse_yaml(cls.config_path)

        cls.project_dir = Path(PARENT_DIR, cls.config['project_name'])
        cls.profiles_dir = Path(PARENT_DIR, cls.config['project_name'])
        cls.test_base_dir = PARENT_DIR
        cls.expected_properties = cls.config['expected_properties']
        cls.expected_dbt_ls_results = cls.config['expected_dbt_ls_results']
        cls.ctx = invoke.Context()
        _utils.get_project_info(cls.ctx, project_dir=cls.project_dir)
        cls.macro_name = '_log_columns_list'
        cls.macro_value = _utils.MACROS[cls.macro_name]
        cls.macro_path = Path(
            cls.ctx.config['macro_paths'][0],
            f'{cls.macro_name}.sql',
        )
        cls.dbt_seed = (
            'dbt seed'
            f' --project-dir {cls.project_dir}'
            f' --profiles-dir {cls.project_dir}'
            f' --target-path {cls.project_dir}/target'
        )
        cls.dbt_clean = (
            'dbt clean'
            f' --project-dir {cls.project_dir}'
            f' --profiles-dir {cls.project_dir}'
        )
        cls.dbt_run = (
            'dbt run'
            f' --project-dir {cls.project_dir}'
            f' --profiles-dir {cls.project_dir}'
            f' --target-path {cls.project_dir}/target'
        )
        cls.dbt_snapshot = (
            'dbt snapshot'
            f' --project-dir {cls.project_dir}'
            f' --profiles-dir {cls.project_dir}'
            f' --target-path {cls.project_dir}/target'
        )
        cls.dbt_compile = (
            'dbt compile'
            f' --project-dir {cls.project_dir}'
            f' --profiles-dir {cls.project_dir}'
            f' --target-path {cls.project_dir}/target'
        )
        invoke.run(cls.dbt_seed)
        invoke.run(cls.dbt_clean)
        invoke.run(cls.dbt_compile)
        invoke.run(cls.dbt_run)
        invoke.run(cls.dbt_snapshot)

    def setUp(self):
        """
        Overrides unittest.TestCase.setUp

        :return: None
        """
        if self.macro_path.exists():
            os.remove(self.macro_path)
        with patch('builtins.input', return_value='y'):
            properties.delete(
                self.ctx,
                project_dir=self.project_dir,
                profiles_dir=self.profiles_dir,
            )

    def tearDown(self):
        """
        Overrides unittest.TestCase.tearDown

        :return: None
        """
        if self.macro_path.exists():
            os.remove(self.macro_path)

    @classmethod
    def tearDownClass(cls):
        """
        Overrides unittest.TestCase.tearDownClass

        :return: None
        """
        invoke.run(cls.dbt_clean)

    def compare_files(self, path1, path2):
        path1_text = path1.read_text()
        path2_text = path2.read_text()
        path1_lines = path1_text.splitlines()
        path2_lines = path2_text.splitlines()
        zipped_lines = itertools.zip_longest(path1_lines, path2_lines)
        for i, (path1_line, path2_line) in enumerate(zipped_lines):
            if path1_line != path2_line:
                self.logger.info(
                    f"Found mismatch in line {i + 1}:\nFile 1: {path1_line}\nFile 2: {path2_line}"
                )
                return False
        return True


if __name__ == '__main__':
    loader = unittest.TestLoader()
    suite = loader.discover(PARENT_DIR)
    runner = unittest.TextTestRunner(verbosity=2)
    result = runner.run(suite)
    sys.exit(not result.wasSuccessful())
