# SPDX-License-Identifier: AGPL-3.0-or-later
"""
Functional, browser based tests for syncthing app.
"""

import time

import pytest
from plinth.tests import functional

pytestmark = [pytest.mark.apps, pytest.mark.syncthing, pytest.mark.sso]


class TestSyncthingApp(functional.BaseAppTests):
    app_name = 'syncthing'
    has_service = True
    has_web = True

    def test_notifications(self, session_browser):
        """Test that authentication and usage reporting notifications are not
        shown."""
        functional.app_enable(session_browser, self.app_name)
        functional.access_url(session_browser, self.app_name)
        _assert_usage_report_notification_not_shown(session_browser)
        _assert_authentication_notification_not_shown(session_browser)

    def test_add_remove_folder(self, session_browser):
        """Test adding and removing a folder."""
        functional.app_enable(session_browser, self.app_name)
        if _folder_is_present(session_browser, 'Test'):
            _remove_folder(session_browser, 'Test')

        _add_folder(session_browser, 'Test', '/tmp')
        assert _folder_is_present(session_browser, 'Test')

        _remove_folder(session_browser, 'Test')
        assert not _folder_is_present(session_browser, 'Test')

    @pytest.mark.backups
    def test_backup_restore(self, session_browser):
        """Test backup and restore of app data."""
        functional.app_enable(session_browser, self.app_name)
        if _folder_is_present(session_browser, 'Test'):
            _remove_folder(session_browser, 'Test')

        _add_folder(session_browser, 'Test', '/tmp')
        functional.backup_create(session_browser, self.app_name,
                                 'test_syncthing')

        _remove_folder(session_browser, 'Test')
        functional.backup_restore(session_browser, self.app_name,
                                  'test_syncthing')

        assert _folder_is_present(session_browser, 'Test')

    def test_user_group_access(self, session_browser):
        """Test that only users in syncthing-access group can access syncthing
        site."""
        functional.app_enable(session_browser, self.app_name)
        if not functional.user_exists(session_browser, 'syncthinguser'):
            functional.create_user(session_browser, 'syncthinguser',
                                   groups=['syncthing-access'])
        if not functional.user_exists(session_browser, 'nogroupuser'):
            functional.create_user(session_browser, 'nogroupuser')

        functional.login_with_account(session_browser, functional.base_url,
                                      'syncthinguser')
        assert functional.is_available(session_browser, self.app_name)

        functional.login_with_account(session_browser, functional.base_url,
                                      'nogroupuser')
        assert not functional.is_available(session_browser, self.app_name)

        functional.login(session_browser)


def _assert_usage_report_notification_not_shown(session_browser):
    _load_main_interface(session_browser)
    assert session_browser.find_by_id('ur').visible is False


def _assert_authentication_notification_not_shown(session_browser):
    _load_main_interface(session_browser)
    assert bool(session_browser.find_by_css(
        '#authenticationUserAndPassword *')) is False


def _load_main_interface(browser):
    """Close the dialog boxes that many popup after visiting the URL."""
    functional.access_url(browser, 'syncthing')

    def service_is_available():
        if browser.is_element_present_by_xpath(
                '//h1[text()="Service Unavailable"]'):
            functional.access_url(browser, 'syncthing')
            return False

        return True

    # After a backup restore, service may not be available immediately
    functional.eventually(service_is_available)

    # Wait for javascript loading process to complete
    functional.eventually(lambda: browser.evaluate_script(
        'angular.element("[ng-controller=SyncthingController]").scope()'
        '.thisDevice().name'))

    # Give browser additional time to setup site
    time.sleep(1)


def _folder_is_present(browser, folder_name):
    """Return whether a folder is present in Syncthing."""
    _load_main_interface(browser)
    folder_names = browser.find_by_css('#folders .panel-title-text span')
    folder_names = [folder_name.text for folder_name in folder_names]
    return folder_name in folder_names


def _add_folder(browser, folder_name, folder_path):
    """Add a new folder to Synthing."""
    _load_main_interface(browser)
    add_folder_xpath = '//button[contains(@ng-click, "addFolder")]'
    browser.find_by_xpath(add_folder_xpath).click()

    folder_dialog = browser.find_by_id('editFolder').first
    functional.eventually(lambda: folder_dialog.visible)
    browser.find_by_id('folderLabel').fill(folder_name)
    browser.find_by_id('folderPath').fill(folder_path)
    save_folder_xpath = './/button[contains(@ng-click, "saveFolder")]'
    folder_dialog.find_by_xpath(save_folder_xpath).first.click()
    functional.eventually(lambda: not folder_dialog.visible)


def _remove_folder(browser, folder_name):
    """Remove a folder from Synthing."""
    _load_main_interface(browser)

    # Find folder
    folder = None
    for current_folder in browser.find_by_css('#folders > .panel'):
        name = current_folder.find_by_css('.panel-title-text span').first.text
        if name == folder_name:
            folder = current_folder
            break

    # Edit folder button
    folder.find_by_css('button.panel-heading').first.click()
    functional.eventually(lambda: folder.find_by_css('div.collapse.in'))
    edit_folder_xpath = './/button[contains(@ng-click, "editFolder")]'
    edit_folder_button = folder.find_by_xpath(edit_folder_xpath).first
    edit_folder_button.scroll_to()
    edit_folder_button.click()

    # Edit folder dialog
    folder_dialog = browser.find_by_id('editFolder').first
    functional.eventually(lambda: folder_dialog.visible)
    remove_button_xpath = './/button[contains(@data-target, "remove-folder")]'
    folder_dialog.find_by_xpath(remove_button_xpath).first.click()

    # Remove confirmation dialog
    remove_folder_dialog = browser.find_by_id('remove-folder-confirmation')
    functional.eventually(lambda: remove_folder_dialog.visible)
    remove_button_xpath = './/button[contains(@ng-click, "deleteFolder")]'
    remove_folder_dialog.find_by_xpath(remove_button_xpath).first.click()

    functional.eventually(lambda: not folder_dialog.visible)
