from __future__ import print_function

import logging
import os
import time
import unittest
from json import loads

from galaxy.tools.verify import verify
from galaxy.tools.verify.test_data import TestDataResolver
from galaxy.web import security
from .driver_util import GalaxyTestDriver

log = logging.getLogger(__name__)

DEFAULT_TOOL_TEST_WAIT = os.environ.get("GALAXY_TEST_DEFAULT_WAIT", 86400)


class FunctionalTestCase(unittest.TestCase):

    def setUp(self):
        # Security helper
        self.security = security.SecurityHelper(id_secret='changethisinproductiontoo')
        self.history_id = os.environ.get('GALAXY_TEST_HISTORY_ID', None)
        self.host = os.environ.get('GALAXY_TEST_HOST')
        self.port = os.environ.get('GALAXY_TEST_PORT')
        default_url = "http://%s:%s" % (self.host, self.port)
        self.url = os.environ.get('GALAXY_TEST_EXTERNAL', default_url)
        self.test_data_resolver = TestDataResolver()
        tool_shed_test_file = os.environ.get('GALAXY_TOOL_SHED_TEST_FILE', None)
        if tool_shed_test_file:
            f = open(tool_shed_test_file, 'r')
            text = f.read()
            f.close()
            self.shed_tools_dict = loads(text)
        else:
            self.shed_tools_dict = {}
        self.keepOutdir = os.environ.get('GALAXY_TEST_SAVE', '')
        if self.keepOutdir > '':
            try:
                os.makedirs(self.keepOutdir)
            except Exception:
                pass

    @classmethod
    def setUpClass(cls):
        """Configure and start Galaxy for a test."""
        cls._test_driver = None

        if not os.environ.get("GALAXY_TEST_ENVIRONMENT_CONFIGURED"):
            cls._test_driver = GalaxyTestDriver()
            cls._test_driver.setup(config_object=cls)

    @classmethod
    def tearDownClass(cls):
        """Shutdown Galaxy server and cleanup temp directory."""
        if cls._test_driver:
            cls._test_driver.tear_down()

    def get_filename(self, filename, shed_tool_id=None):
        # For tool tests override get_filename to point at an installed tool if shed_tool_id is set.
        if shed_tool_id and getattr(self, "shed_tools_dict", None):
            file_dir = self.shed_tools_dict[shed_tool_id]
            if file_dir:
                return os.path.abspath(os.path.join(file_dir, filename))
        return self.test_data_resolver.get_filename(filename)

    # TODO: Make this more generic, shouldn't be related to tool stuff I guess.
    def wait_for(self, func, **kwd):
        sleep_amount = 0.2
        slept = 0
        walltime_exceeded = kwd.get("maxseconds", None)
        if walltime_exceeded is None:
            walltime_exceeded = DEFAULT_TOOL_TEST_WAIT

        exceeded = True
        while slept <= walltime_exceeded:
            result = func()
            if result:
                time.sleep(sleep_amount)
                slept += sleep_amount
                sleep_amount *= 2
            else:
                exceeded = False
                break

        if exceeded:
            message = 'Tool test run exceeded walltime [total %s, max %s], terminating.' % (slept, walltime_exceeded)
            log.info(message)
            raise AssertionError(message)

    # TODO: Move verify_xxx into GalaxyInteractor or some relevant mixin.
    def verify_hid(self, filename, hda_id, attributes, shed_tool_id, hid="", dataset_fetcher=None):
        assert dataset_fetcher is not None

        def get_filename(test_filename):
            return self.get_filename(test_filename, shed_tool_id=shed_tool_id)

        def verify_extra_files(extra_files):
            self._verify_extra_files_content(extra_files, hda_id, shed_tool_id=shed_tool_id, dataset_fetcher=dataset_fetcher)

        data = dataset_fetcher(hda_id)
        item_label = "History item %s" % hid
        verify(
            item_label,
            data,
            attributes=attributes,
            filename=filename,
            get_filename=get_filename,
            keep_outputs_dir=self.keepOutdir,
            verify_extra_files=verify_extra_files,
        )

    def _verify_composite_datatype_file_content(self, file_name, hda_id, base_name=None, attributes=None, dataset_fetcher=None, shed_tool_id=None):
        assert dataset_fetcher is not None

        def get_filename(test_filename):
            return self.get_filename(test_filename, shed_tool_id=shed_tool_id)

        data = dataset_fetcher(hda_id, base_name)
        item_label = "History item %s" % hda_id
        try:
            verify(
                item_label,
                data,
                attributes=attributes,
                filename=file_name,
                get_filename=get_filename,
                keep_outputs_dir=self.keepOutdir,
            )
        except AssertionError as err:
            errmsg = 'Composite file (%s) of %s different than expected, difference:\n' % (base_name, item_label)
            errmsg += str(err)
            raise AssertionError(errmsg)

    def _verify_extra_files_content(self, extra_files, hda_id, dataset_fetcher, shed_tool_id=None):
        files_list = []
        for extra_type, extra_value, extra_name, extra_attributes in extra_files:
            if extra_type == 'file':
                files_list.append((extra_name, extra_value, extra_attributes))
            elif extra_type == 'directory':
                for filename in os.listdir(self.get_filename(extra_value, shed_tool_id=shed_tool_id)):
                    files_list.append((filename, os.path.join(extra_value, filename), extra_attributes))
            else:
                raise ValueError('unknown extra_files type: %s' % extra_type)
        for filename, filepath, attributes in files_list:
            self._verify_composite_datatype_file_content(filepath, hda_id, base_name=filename, attributes=attributes, dataset_fetcher=dataset_fetcher, shed_tool_id=shed_tool_id)
