# BFH Science Self-Service Portal
# Copyright (C) 2025-2026 IKUS Software
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

from cherrypy_foundation.url import url_for
from parameterized import parameterized

from . import WebCase


class DashboardPageTest(WebCase):

    login = True

    default_config = {
        "ldap-firstname-attribute": "givenName",
        "ldap-lastname-attribute": "sn",
        "ldap-email-attribute": "mail",
    }

    def test_get_dashboard_page(self):
        # Given user is authenticated
        # When querying the dashboard page
        self.getPage('/dashboard/')
        # Then the page is return
        self.assertStatus(200)
        self.assertInBody(self.username)

    def test_get_dashboard_page_with_wrong_permission(self):
        # Given a guest user
        # Given another user in LDAP
        self.conn.strategy.remove_entry('cn=other-user,ou=Staff,ou=Accounts,dc=bfh')
        self.conn.strategy.add_entry(
            'cn=other-user,ou=Staff,ou=Accounts,dc=bfh',
            {
                'userPassword': 'my-password',
                'uid': ['other-user'],
                'objectClass': ['person', 'organizationalPerson', 'inetOrgPerson', 'posixAccount'],
            },
        )
        # When querying the dashboard page for other users
        self.getPage('/dashboard/?u=other-user')
        # Then a 403 error get raised
        self.assertStatus(403)

    def test_get_dashboard_page_with_admin_role(self):
        # Given a power-user user
        self.conn.strategy.remove_entry(f'cn={self.username},ou=Staff,ou=Accounts,dc=bfh')
        self.conn.strategy.add_entry(
            f'cn={self.username},ou=Staff,ou=Accounts,dc=bfh',
            {
                'userPassword': self.password,
                'uid': [self.username],
                'objectClass': ['person', 'organizationalPerson', 'inetOrgPerson', 'posixAccount'],
                'memberOf': [
                    'cn=IDM.perm.infrastructure.ssp.admin,ou=Security,ou=Groups,dc=bfh',
                ],
            },
        )
        # Given another user in LDAP
        self.conn.strategy.remove_entry('cn=other-user,ou=Staff,ou=Accounts,dc=bfh')
        self.conn.strategy.add_entry(
            'cn=other-user,ou=Staff,ou=Accounts,dc=bfh',
            {
                'userPassword': 'my-password',
                'uid': ['other-user'],
                'objectClass': ['person', 'organizationalPerson', 'inetOrgPerson', 'posixAccount'],
                'givenName': ['Olivia'],
                'sn': ['Bennett'],
            },
        )
        # When querying the dashboard page for other users
        self.getPage('/dashboard/?u=other-user')
        # Then page return other-user data
        self.assertStatus(200)
        self.assertInBody('Olivia Bennett')
        self.assertInBody('other-user')
        # When querying an invalid user
        self.getPage('/dashboard/?u=invalid')
        # Then a 404 is returned
        self.assertStatus(404)
        self.assertInBody('No such user')

    def test_ssh_key_fingerprint(self):
        # Given user with a SSK Hey
        self.conn.strategy.remove_entry(f'cn={self.username},ou=Staff,ou=Accounts,dc=bfh')
        self.conn.strategy.add_entry(
            f'cn={self.username},ou=Staff,ou=Accounts,dc=bfh',
            {
                'userPassword': self.password,
                'uid': [self.username],
                'objectClass': ['person', 'organizationalPerson', 'inetOrgPerson', 'posixAccount'],
                'sshPublicKey': [
                    'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAwTL/0gImf5vs3xgE0xwvHSoLi1LXS/anAwpdPxN1iz',
                ],
            },
        )
        self._login(self.username, self.password)
        # When querying the dashboard page
        self.getPage('/dashboard/')
        # Then the page load and contains the SSH Fingerprint
        self.assertStatus(200)
        self.assertInBody(self.username)
        self.assertInBody('SHA256:Q3nFi5KOg9iwVOIMrDzZAMT0ttyzwOQCh06b9F+LKHo')

    def test_get_search_user(self):
        # Given power user is authenticated
        self.conn.strategy.remove_entry(f'cn={self.username},ou=Staff,ou=Accounts,dc=bfh')
        self.conn.strategy.add_entry(
            f'cn={self.username},ou=Staff,ou=Accounts,dc=bfh',
            {
                'userPassword': self.password,
                'uid': [self.username],
                'objectClass': ['person', 'organizationalPerson', 'inetOrgPerson', 'posixAccount'],
                'memberOf': [
                    'cn=IDM.infra.applications.self-service.users,ou=Security,ou=Groups,dc=bfh',
                ],
            },
        )
        # Given a user
        self.conn.strategy.remove_entry('cn=test-user,ou=Staff,ou=Accounts,dc=bfh')
        self.conn.strategy.add_entry(
            'cn=test-user,ou=Staff,ou=Accounts,dc=bfh',
            {
                'userPassword': self.password,
                'uid': ['test-user'],
                'givenName': ['Olivia'],
                'sn': ['Bennett'],
                'mail': ['olivia@example.com'],
                'objectClass': ['person', 'organizationalPerson', 'inetOrgPerson', 'posixAccount'],
                'memberOf': [
                    'cn=IDM.perm.storage.my-share-name.read,ou=Security,ou=Groups,dc=bfh',
                    'cn=IDM.perm.storage.second-share-name.write,ou=Security,ou=Groups,dc=bfh',
                ],
            },
        )
        # When searching list of users
        data = self.getJson('/dashboard/search-user.json?u=oli')

        # Then a list is returned
        self.assertEqual(
            data,
            {
                "status": 200,
                "data": [
                    {
                        "username": "test-user",
                        "name": "Olivia Bennett",
                        "email": "olivia@example.com",
                        "url": "/dashboard?u=test-user",
                    }
                ],
            },
        )

    def test_get_dashboard_page_with_selenium(self):
        # Given a user
        self.conn.strategy.remove_entry('cn=test-user,ou=Staff,ou=Accounts,dc=bfh')
        self.conn.strategy.add_entry(
            'cn=test-user,ou=Staff,ou=Accounts,dc=bfh',
            {
                'userPassword': self.password,
                'uid': ['test-user'],
                'givenName': ['Olivia'],
                'sn': ['Bennett'],
                'mail': ['olivia@example.com'],
                'objectClass': ['person', 'organizationalPerson', 'inetOrgPerson', 'posixAccount'],
                'memberOf': [
                    # Storage
                    'cn=IDM.perm.storage.my-share-name.read,ou=Security,ou=Groups,dc=bfh',
                    'cn=IDM.perm.storage.second-share-name.write,ou=Security,ou=Groups,dc=bfh',
                    # Compute
                    'cn=IDM.perm.compute.dep_some-compute-name.users,ou=Security,ou=Groups,dc=bfh',
                    # Infra
                    'cn=IDM.infra.infrastructure.some-infra-name.admin,ou=Security,ou=Groups,dc=bfh',
                ],
            },
        )
        self.conn.strategy.add_entry(
            'cn=IDM.perm.storage.my-share-name.read,ou=Security,ou=Groups,dc=bfh',
            {
                'objectClass': ['group'],
                'bfhCephDirectory': 'Services/My-Share-Name',
            },
        )
        self.conn.strategy.add_entry(
            'cn=IDM.perm.storage.second-share-name.write,ou=Security,ou=Groups,dc=bfh',
            {
                'objectClass': ['group'],
                'bfhCephDirectory': 'Second/Share-Name',
            },
        )
        self.conn.strategy.add_entry(
            'cn=IDM.perm.compute.dep_some-compute-name.users,ou=Security,ou=Groups,dc=bfh',
            {
                'objectClass': ['group'],
            },
        )
        self.conn.strategy.add_entry(
            'cn=IDM.infra.infrastructure.some-infra-name.admin,ou=Security,ou=Groups,dc=bfh',
            {
                'objectClass': ['group'],
            },
        )
        # Re-login
        self._login(self.username, self.password)
        # Given a repository
        with self.selenium() as driver:
            # When browsing repository
            driver.get(url_for('dashboard'))
            # Then page load without error
            self.assertFalse(driver.get_log('browser'))
            # Then storage are listed
            driver.find_element('xpath', "//*[contains(text(), 'LFE/Services/My-Share-Name')]")
            driver.find_element('xpath', "//*[contains(text(), 'LFE/Second/Share-Name')]")
            # Then compute are listed
            driver.find_element('xpath', "//*[contains(text(), 'some-infra-name')]")
            # Then Infra are listed
            driver.find_element('xpath', "//*[contains(text(), 'some-compute-name')]")

    @parameterized.expand(['dark', 'light', 'auto'])
    def test_theme_mode_with_selenium(self, mode):
        # Given we are login
        self._login(self.username, self.password)
        # Given the dashboard page is loaded
        with self.selenium() as driver:
            driver.get(url_for('dashboard'))
            # When user select a dark theme.
            menu_btn = driver.find_element('css selector', "#navbarDropdownMenuLink")
            menu_btn.click()
            dark_btn = driver.find_element('css selector', f"[data-bs-theme-value={mode}]")
            dark_btn.click()
            # Then body theme get updated
            body = driver.find_element('css selector', "html")
            theme = body.get_attribute('data-bs-theme')
            if mode != 'auto':
                self.assertEqual(theme, mode)
