import gzip
import json
import os
import tempfile
from pathlib import Path
from unittest import mock

import responses  # type: ignore
from launchable.utils.http_client import get_base_url
from tests.cli_test_case import CliTestCase
from tests.helper import ignore_warnings


class MinitestTest(CliTestCase):
    test_files_dir = Path(__file__).parent.joinpath(
        '../data/minitest/').resolve()
    result_file_path = test_files_dir.joinpath('record_test_result.json')

    @responses.activate
    @mock.patch.dict(os.environ, {"LAUNCHABLE_TOKEN": CliTestCase.launchable_token})
    def test_record_test_minitest(self):
        result = self.cli('record', 'tests',  '--session',
                          self.session, 'minitest', str(self.test_files_dir) + "/")
        self.assertEqual(result.exit_code, 0)

        payload = json.loads(gzip.decompress(
            responses.calls[1].request.body).decode())

        expected = self.load_json_from_file(self.result_file_path)
        self.assert_json_orderless_equal(expected, payload)

    @responses.activate
    @mock.patch.dict(os.environ, {"LAUNCHABLE_TOKEN": CliTestCase.launchable_token})
    def test_record_test_minitest_chunked(self):
        result = self.cli('record', 'tests',  '--session', self.session,
                          '--post-chunk', 5, 'minitest', str(self.test_files_dir) + "/")
        self.assertEqual(result.exit_code, 0)

        payload1 = json.loads(gzip.decompress(
            responses.calls[1].request.body).decode())
        expected1 = self.load_json_from_file(
            self.test_files_dir.joinpath('record_test_result_chunk1.json'))

        payload2 = json.loads(gzip.decompress(
            responses.calls[2].request.body).decode())
        expected2 = self.load_json_from_file(
            self.test_files_dir.joinpath('record_test_result_chunk2.json'))

        # concat 1st and 2nd request for unstable order
        self.assert_json_orderless_equal(
            expected1['events'] + expected2['events'], payload1['events'] + payload2['events'])

    @responses.activate
    @mock.patch.dict(os.environ, {"LAUNCHABLE_TOKEN": CliTestCase.launchable_token})
    @ignore_warnings
    def test_subset(self):
        test_path = Path("test", "example_test.rb")
        responses.replace(responses.POST, "{}/intake/organizations/{}/workspaces/{}/subset".format(get_base_url(), self.organization, self.workspace), json={
            'testPaths': [[{'name': str(test_path)}]],
            'rest': [],
            'subsettingId': 123,
            'summary': {
                'subset': {'duration': 10, 'candidates': 1, 'rate': 100},
                'rest': {'duration': 0, 'candidates': 0, 'rate': 0}
            },
            "isBrainless": False,
        }, status=200)

        result = self.cli('subset', '--target', '20%', '--session', self.session, '--base', str(self.test_files_dir),
                          'minitest', str(self.test_files_dir) + "/test/**/*.rb")

        self.assertEqual(result.exit_code, 0)
        output = Path(self.test_files_dir, "test", "example_test.rb")
        self.assertIn(str(output), result.output)

    @responses.activate
    @mock.patch.dict(os.environ, {"LAUNCHABLE_TOKEN": CliTestCase.launchable_token})
    def test_subset_with_invalid_path(self):
        result = self.cli('subset', '--target', '20%', '--session', self.session, '--base', str(self.test_files_dir),
                          'minitest', str(self.test_files_dir) + "/dummy")

        self.assertEqual(result.exit_code, 0)
        self.assertTrue(
            "Error: no tests found matching the path." in result.output)

    @responses.activate
    @mock.patch.dict(os.environ, {"LAUNCHABLE_TOKEN": CliTestCase.launchable_token})
    @ignore_warnings
    def test_subset_split(self):
        test_path = Path("test", "example_test.rb")
        responses.replace(responses.POST, "{}/intake/organizations/{}/workspaces/{}/subset".format(get_base_url(), self.organization, self.workspace), json={
            'testPaths': [[{'name': str(test_path)}]],
            'rest': [],
            'subsettingId': 123,
            'summary': {
                'subset': {'duration': 10, 'candidates': 1, 'rate': 100},
                'rest': {'duration': 0, 'candidates': 0, 'rate': 0}
            },
            "isBrainless": False,
        }, status=200)

        result = self.cli('subset', '--target', '20%', '--session', self.session, '--base', str(self.test_files_dir), '--split',
                          'minitest', str(self.test_files_dir) + "/test/**/*.rb")

        self.assertEqual(result.exit_code, 0)
        self.assertIn('subset/123', result.output)

    @responses.activate
    @mock.patch.dict(os.environ, {"LAUNCHABLE_TOKEN": CliTestCase.launchable_token})
    @ignore_warnings
    def test_split_subset(self):
        test_path = Path("test", "example_test.rb")
        responses.replace(responses.POST, "{}/intake/organizations/{}/workspaces/{}/subset/456/slice".format(get_base_url(), self.organization, self.workspace),
                          json={'testPaths': [[{'name': str(test_path)}]], 'rest': [], 'subsettingId': 123}, status=200)

        rest = tempfile.NamedTemporaryFile(delete=False)
        result = self.cli('split-subset', '--subset-id', 'subset/456', '--base', str(self.test_files_dir),
                          '--bin', '2/2', '--rest', rest.name, 'minitest')

        self.assertEqual(result.exit_code, 0)
        output = Path(self.test_files_dir, "test", "example_test.rb")
        self.assertEqual(str(output), result.output.rstrip("\n"))
        self.assertEqual(rest.read().decode().rstrip("\n"), str(output))
        rest.close()
        os.unlink(rest.name)
