import os
import tempfile
import unittest
from unittest import mock

import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))

from swai_inst import installer, utils


APP_ID = 'org.example.directory-test'
WILDCARD_APP_ID = 'org.example.*'


def _manifest(icon_url=None):
    icon_value = 'null' if icon_url is None else icon_url
    return f"""app_name: Directory Test
app_id: {APP_ID}
icon_url: {icon_value}
categories: []
"""


def _manifest_for_app(app_id, icon_url=None):
    icon_value = 'null' if icon_url is None else icon_url
    return f"""app_name: Directory Test
app_id: {app_id}
icon_url: {icon_value}
categories: []
"""


class DirectoryPathTests(unittest.TestCase):
    def tearDown(self):
        utils.set_share_directory(None)

    def test_app_data_is_user_local_with_custom_install_share(self):
        with tempfile.TemporaryDirectory() as temporary_directory:
            custom_share = os.path.join(temporary_directory, 'custom-share')
            home_directory = os.path.join(temporary_directory, 'home')
            utils.set_share_directory(custom_share)

            self.assertEqual(
                utils.get_app_data_path(APP_ID, home_directory),
                os.path.join(
                    home_directory,
                    '.local',
                    'share',
                    'swai',
                    'app_data',
                    APP_ID,
                ),
            )

    def test_app_id_cannot_escape_data_directory(self):
        with tempfile.TemporaryDirectory() as temporary_directory:
            with self.assertRaises(ValueError):
                utils.get_app_data_path('../outside', temporary_directory)

    def test_global_removal_uses_each_users_local_data_directory(self):
        with tempfile.TemporaryDirectory() as temporary_directory:
            custom_share = os.path.join(temporary_directory, 'global-share')
            first_home = os.path.join(temporary_directory, 'first-home')
            second_home = os.path.join(temporary_directory, 'second-home')
            utils.set_share_directory(custom_share)

            swai_dir, _, _ = utils.get_install_paths(local_install=False)
            manifest_path = os.path.join(swai_dir, f'{APP_ID}.swai')
            with open(manifest_path, 'w', encoding='utf-8') as manifest_file:
                manifest_file.write(_manifest())

            first_data = utils.get_app_data_path(APP_ID, first_home)
            second_data = utils.get_app_data_path(APP_ID, second_home)
            wrong_global_data = os.path.join(
                custom_share, 'swai', 'app_data', APP_ID
            )
            os.makedirs(first_data)
            os.makedirs(second_data)
            os.makedirs(wrong_global_data)

            local_manifest = utils.get_local_app_path(APP_ID, second_home)
            os.makedirs(os.path.dirname(local_manifest))
            with open(local_manifest, 'w', encoding='utf-8') as manifest_file:
                manifest_file.write(_manifest())

            with mock.patch.object(
                utils,
                'get_user_home_directories',
                return_value=[first_home, second_home],
            ):
                installer.remove_from_file(
                    manifest_path, local_install=False, remove_data=True
                )

            self.assertFalse(os.path.exists(first_data))
            self.assertTrue(os.path.isdir(second_data))
            self.assertTrue(os.path.isdir(wrong_global_data))

    def test_extensionless_icon_does_not_skip_local_data_removal(self):
        with tempfile.TemporaryDirectory() as temporary_directory:
            home_directory = os.path.join(temporary_directory, 'home')
            with mock.patch.dict(os.environ, {'HOME': home_directory}):
                swai_dir, _, icon_dir = utils.get_install_paths(
                    local_install=True
                )
                manifest_path = os.path.join(swai_dir, f'{APP_ID}.swai')
                with open(
                    manifest_path, 'w', encoding='utf-8'
                ) as manifest_file:
                    manifest_file.write(
                        _manifest('https://example.test/icon')
                    )

                os.makedirs(utils.get_app_data_path(APP_ID))
                icon_path = os.path.join(
                    icon_dir, f'io.stillhq.swai.{APP_ID}.png'
                )
                with open(icon_path, 'wb') as icon_file:
                    icon_file.write(b'icon')

                installer.remove_from_file(
                    manifest_path, local_install=True, remove_data=True
                )

                self.assertFalse(os.path.exists(icon_path))
                self.assertFalse(os.path.exists(utils.get_app_data_path(APP_ID)))

    def test_icon_cleanup_treats_app_id_as_a_literal_filename(self):
        with tempfile.TemporaryDirectory() as temporary_directory:
            home_directory = os.path.join(temporary_directory, 'home')
            with mock.patch.dict(os.environ, {'HOME': home_directory}):
                swai_dir, _, icon_dir = utils.get_install_paths(
                    local_install=True
                )
                manifest_path = os.path.join(
                    swai_dir, f'{WILDCARD_APP_ID}.swai'
                )
                with open(
                    manifest_path, 'w', encoding='utf-8'
                ) as manifest_file:
                    manifest_file.write(
                        _manifest_for_app(
                            WILDCARD_APP_ID,
                            'https://example.test/icon',
                        )
                    )

                target_icon = os.path.join(
                    icon_dir, f'io.stillhq.swai.{WILDCARD_APP_ID}.png'
                )
                neighboring_icon = os.path.join(
                    icon_dir, 'io.stillhq.swai.org.example.neighbor.png'
                )
                for icon_path in (target_icon, neighboring_icon):
                    with open(icon_path, 'wb') as icon_file:
                        icon_file.write(b'icon')

                installer.remove_from_file(
                    manifest_path, local_install=True, remove_data=False
                )

                self.assertFalse(os.path.exists(target_icon))
                self.assertTrue(os.path.exists(neighboring_icon))


if __name__ == '__main__':
    unittest.main()
