from os import getenv
from shimoku.exceptions import ActivityError, CacheError
import unittest
from utils import initiate_shimoku

s = initiate_shimoku()
business_id: str = getenv('BUSINESS_ID')
mock: bool = getenv('MOCK') == 'TRUE'
async_execution: bool = getenv('ASYNC_EXECUTION') == 'TRUE'

s.set_workspace(uuid=business_id)
s.set_menu_path('test_activity')

activity_name = 'test_activity'

_activity = s.activities.get_activity(name=activity_name)
if not _activity:
    _activity = s.activities.create_activity(name=activity_name)

activity_id = _activity['id']
run_id = s.activities.create_run(uuid=activity_id)['id']


def delete_new_activity_if_it_exists():
    """
    Deletes the activity created in the test_create_delete_get_activities test.
    """
    try:
        s.activities.delete_activity(name='new_' + activity_name)
    except ActivityError:
        pass


class TestActivity(unittest.TestCase):

    def test_get_run_settings(self):
        """
        Makes a call to the activity metadata API to get run settings.
        """
        s.activities.get_run_settings(uuid=activity_id, run_id=run_id)
        s.activities.get_run_settings(uuid=activity_id, run_id=run_id)
        s.activities.get_run_settings(name=activity_name, run_id=run_id)
        s.activities.get_run_settings(name=activity_name, run_id=run_id)

    def test_create_delete_get_activities(self):
        """
        Makes various calls to the activity metadata API to create, delete and get activities in different ways.
        """

        new_activity_name = '0_new_'+activity_name
        n_activities_before = len(s.activities.get_activities())

        activity = s.activities.create_activity(name=new_activity_name)

        new_activity_id = activity['id']

        assert activity['name'] == new_activity_name
        assert activity['runs'] == []

        n_activities_mid = len(s.activities.get_activities())
        assert n_activities_mid == n_activities_before + 1

        s.set_menu_path(name='test_activity')

        n_activities_mid = len(s.activities.get_activities())
        assert n_activities_mid == n_activities_before + 1

        s.activities.delete_activity(uuid=new_activity_id)

        n_activities_after = len(s.activities.get_activities())

        assert n_activities_before == n_activities_after

        self.activity_doesnt_exist(new_activity_name)

    def test_update_activity(self):
        """
        Makes various calls to the activity metadata API to update activities.
        """
        new_activity_name = '1_new_'+activity_name

        n_activities_before = len(s.activities.get_activities())

        activity = s.activities.create_activity(name=new_activity_name)

        new_activity_id = activity['id']

        assert activity['name'] == new_activity_name

        s.activities.update_activity(uuid=new_activity_id, new_name='updated ' + new_activity_name)

        assert not s.activities.get_activity(name=new_activity_name)
        assert s.activities.get_activity(name='updated ' + new_activity_name)
        assert s.activities.create_activity(name=new_activity_name)
        self.cant_create_activity(new_activity_name)

        s.activities.delete_activity(uuid=new_activity_id)
        s.activities.delete_activity(name=new_activity_name)

        n_activities_after = len(s.activities.get_activities())

        assert n_activities_before == n_activities_after

    def test_create_get_runs(self):
        """
        Makes various calls to the activity metadata API to create, and get runs.
        """
        new_activity_name = '2_new_'+activity_name
        activity = s.activities.create_activity(name=new_activity_name)
        new_activity_id = activity['id']

        assert activity['name'] == new_activity_name
        assert activity['runs'] == []

        run1 = s.activities.create_run(name=new_activity_name)
        run2 = s.activities.create_run(uuid=new_activity_id)
        run3 = s.activities.create_run(name=new_activity_name)

        activity = s.activities.get_activity(uuid=new_activity_id, how_many_runs=1)

        assert activity['name'] == new_activity_name
        assert len(activity['runs']) == 1
        assert activity['runs'][0]['id'] in [run1['id'], run2['id'], run3['id']]

        activity = s.activities.get_activity(name=new_activity_name, how_many_runs=3)
        activities_run_ids = [run['id'] for run in activity['runs']]

        assert activity['name'] == new_activity_name
        assert len(activity['runs']) == 3
        assert run1['id'] in activities_run_ids
        assert run2['id'] in activities_run_ids
        assert run3['id'] in activities_run_ids

        # delete the activity and test that it doesn't exist
        s.activities.delete_activity(name=new_activity_name)

        self.activity_doesnt_exist(new_activity_name)

    def test_create_get_run_logs(self):
        """
        Makes various calls to the activity metadata API to create, and get logs.
        """
        new_activity_name = '3_new_'+activity_name
        activity = s.activities.create_activity(name=new_activity_name)
        new_activity_id = activity['id']

        assert activity['name'] == new_activity_name
        assert activity['runs'] == []

        run = s.activities.create_run(name=new_activity_name)

        log1 = s.activities.create_run_log(name=new_activity_name, run_id=run['id'],
                                           message='test message 1', severity='INFO')
        log2 = s.activities.create_run_log(uuid=new_activity_id, run_id=run['id'],
                                           message='test message 2', severity='DEBUG', tags={'tag1': 'tag1', 'tag2': 'tag2'})
        log3 = s.activities.create_run_log(name=new_activity_name, run_id=run['id'],
                                           message='test message 3', severity='ERROR', tags={'tag1': 'tag1'})

        activity = s.activities.get_activity(name=new_activity_name, how_many_runs=1)
        run = activity['runs'][0]
        logs = s.activities.get_run_logs(uuid=new_activity_id, run_id=run['id'])

        # test that the logs are in the correct order, they are ordered by creation time
        assert len(logs) == 3
        assert log1 == logs[0]
        assert log2 == logs[1]
        assert log3 == logs[2]

        # delete the activity and test that it doesn't exist
        s.activities.delete_activity(name=new_activity_name)

        self.activity_doesnt_exist(new_activity_name)

    def test_default_activity_settings(self):
        """
        Tests that the default settings are being set and used correctly.
        """
        new_activity_name = '4_new_' + activity_name
        # test that the default settings are being set
        settings = {'name': 'test1', 'value': 'test1'}
        activity = s.activities.create_activity(name=new_activity_name, settings=settings)

        assert activity['settings'] == settings

        # test that the default settings persist in the API
        s.set_menu_path(name='test_activity')

        activity = s.activities.get_activity(uuid=activity['id'])
        assert activity['settings'] == settings

        # delete the activity and test that it doesn't exist
        s.activities.delete_activity(name=new_activity_name)

        self.activity_doesnt_exist(new_activity_name)

    def test_get_run_logs(self):
        """
        Makes various calls to the activity metadata API to create, and get logs.
        """
        new_activity_name = '5_new_'+activity_name
        activity = s.activities.create_activity(name=new_activity_name)
        new_activity_id = activity['id']

        run = s.activities.create_run(name=new_activity_name)

        log1 = s.activities.create_run_log(name=new_activity_name, run_id=run['id'],
                                           message='test message 1', severity='INFO')
        log2 = s.activities.create_run_log(uuid=new_activity_id, run_id=run['id'],
                                           message='test message 2', severity='DEBUG', tags={'tag1': 'tag1', 'tag2': 'tag2'})
        log3 = s.activities.create_run_log(name=new_activity_name, run_id=run['id'],
                                           message='test message 3', severity='ERROR', tags={'tag1': 'tag1'})

        activity = s.activities.get_activity(name=new_activity_name, how_many_runs=1)
        run = activity['runs'][0]

        # test that the logs can be retrieved by run id
        logs = s.activities.get_run_logs(uuid=new_activity_id, run_id=run['id'])
        assert len(logs) == 3
        assert log1 == logs[0]
        assert log2 == logs[1]
        assert log3 == logs[2]

        # delete the activity and test that it doesn't exist
        s.activities.delete_activity(name=new_activity_name)

        self.activity_doesnt_exist(new_activity_name)

    def test_execute_activity(self):
        """
        Executes an activity and tests that the activity and run are created, also tests that the run has a log
        and that the settings are being passed correctly. The activity must have a webhook linked for it to work.
        """
        s.activities.create_webhook(name=activity_name, url='https://www.google.com')

        run = s.activities.execute_activity(name=activity_name)
        activity = s.activities.get_activity(name=activity_name, how_many_runs=1)

        # Runs are ordered by execution time, so the first run is the most recently executed
        assert activity['runs'][0]['id'] == run['id']
        assert len(activity['runs'][0]['logs']) == 1 or mock

        settings = [{'name': 'test1', 'value': 'test1'}, {'name': 'test2', 'value': 'test2'}]

        run1 = s.activities.execute_activity(name=activity_name, run_settings=settings[0])
        run2 = s.activities.execute_activity(name=activity_name, run_settings=settings[1])
        run3 = s.activities.create_run(name=activity_name, settings=run2['id'])
        s.activities.execute_run(name=activity_name, run_id=run3['id'])

        activity = s.activities.get_activity(name=activity_name, how_many_runs=4)

        assert activity['runs'][0]['id'] == run['id']
        assert activity['runs'][0]['settings'] == {}
        assert len(activity['runs'][0]['logs']) == 1 or mock
        assert activity['runs'][1]['id'] == run1['id']
        assert activity['runs'][1]['settings'] == settings[0]
        assert len(activity['runs'][1]['logs']) == 1 or mock
        assert activity['runs'][2]['id'] == run2['id']
        assert activity['runs'][2]['settings'] == settings[1]
        assert len(activity['runs'][2]['logs']) == 1 or mock
        assert activity['runs'][3]['id'] == run3['id']
        assert activity['runs'][3]['settings'] == settings[1]
        assert len(activity['runs'][3]['logs']) == 1 or mock

    def test_button_and_form_execute_activity(self):
        """
        Creates a button in the dashboard with the capability to execute an activity.
        """
        s.set_menu_path('test_activity')

        s.plt.activity_button(
            activity_name=activity_name, order=0, label='test_button',
            cols_size=12, align='start',
        )
        s.plt.activity_button(
            activity_name=activity_name, order=1, label='test_button',
            cols_size=12, align='center'
        )
        s.plt.activity_button(
            activity_name=activity_name, order=2, label='test_button',
            rows_size=2, cols_size=12, align='right'
        )

        for i in range(6):
            s.plt.activity_button(
                activity_name=activity_name, order=3+i, label='test_button',
                rows_size=2, cols_size=12-i*2, align='stretch', padding=f'0,{i},0,{i}'
            )

        s.plt.activity_button(
            activity_name=activity_name, order=9, label='test_button',
            cols_size=12, align='center'
        )

        form_sent = (
            "<head>"
            "<style>"  # Styles title
            ".component-title{height:auto; width:100%; "
            "border-radius:16px; padding:16px;"
            "display:flex; align-items:center;"
            "background-color:var(--chart-C1); color:var(--color-white);}"
            "<style>.base-white{color:var(--color-white);}</style>"
            "</head>"  # Styles subtitle
            "<div class='component-title'>"
            "<div class='big-icon-banner'></div>"
            "<div class='text-block'>"
            "<h1>The activity has been called</h1>"
            "<p class='base-white'>"
            "Activity Test</p>"
            "</div>"
            "</div>"
        )

        s.plt.set_modal(modal_name='Activity called')
        s.plt.html(html=form_sent, order=0)
        s.plt.pop_out_of_modal()

        form_groups = {
            'Personal information': [
                {
                    'mapping': 'name',
                    'fieldName': 'name',
                    'inputType': 'text',
                },
            ]
        }

        s.plt.generate_input_form_groups(
            order=10, form_groups=form_groups,
            activity_name=activity_name,
            modal='Activity called',
        )
        if async_execution:
            s.activate_async_execution()

    def activity_doesnt_exist(self, _activity_name):
        with self.assertRaises(ActivityError):
            s.activities.delete_activity(name=_activity_name)

    def cant_create_activity(self, _activity_name):
        with self.assertRaises(CacheError):
            s.activities.create_activity(name=_activity_name)
