# 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 unittest import mock

from parameterized import parameterized

from bfh_science_ssp.core.user import User

from . import WebCase


def _mock_subprocess(output=""):
    mock_process = mock.MagicMock()
    mock_process.returncode = 0
    mock_process.stdout = output
    mock_process.stderr = ""
    return mock_process


class AccessHandlerTest(WebCase):

    login = True

    default_config = {'replace-ssh-key-cmd': '/bin/true'}

    @parameterized.expand(
        [
            (
                'ed25519_pub',
                {'ssh_key': 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN5Ml66VFCQpe/Css9r2JVb4Jd6694kFH3bGaYw7aoNg'},
                True,
            ),
            (
                'putty',
                {
                    'ssh_key': '---- BEGIN SSH2 PUBLIC KEY ----\nComment: "eddsa-key-20250604"\nAAAAC3NzaC1lZDI1NTE5AAAAIJP+MVYx3I/DE5APFt18FshgwpVA2HwUx4Ji07Cv\nSxV3\n---- END SSH2 PUBLIC KEY ----'
                },
                True,
            ),
            (
                'invalid-bytes',
                {'ssh_key': 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5Asomeinvalidkey/Css9r2JVb4Jd6694kFH3bGaYw7aoNg'},
                False,
            ),
            (
                'invalid-type',
                {
                    'ssh_key': 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCpYtoafAmPSg+1Qunl8UEO9HZ2yydqJDBshd3YbeURu6NyFrGvMIwUn/GBsVVAm6sdfgOc7vDV3ZoYkhbt9EgYh2/jsIX13Cd/XKWWDEPZ6hnsUIpPflq5NUB7KwwBgLO6+Mdvl69MZWDqYIDHIebfTe3pkvbwh6hjS2/3p+Qk2A+pmhFMGbwpWpRDpGrqYE1hrZXftsxLtsvl208jKYspDoZwxtKEg+o8EYsSj+jlD3EoC0sVEb9ikhYNG1TQsSKc5fktCEd5AkCSKcI3x91Z3d6LxyVaMHWmEIaNEJ6oNW24bJmu14tjFKS1sHN2TotrpxLTYRVNNPPuicKk643N'
                },
                False,
            ),
        ]
    )
    @mock.patch('subprocess.run', return_value=_mock_subprocess())
    def test_replace_ssh_key(self, name, data, expect_success, mock_exec_requests):
        # Given a user
        # When user replace SSH Key
        self.getPage("/dashboard/replace-ssh-key", body=data, method='POST')
        # Then user redirect to dashboard
        self.assertStatus(303)
        self.assertHeaderItemValue('Location', f'{self.baseurl}/dashboard')
        # Then new page show success
        if expect_success:
            self.getPage("/dashboard/")
            self.assertStatus(200)
            self.assertInBody("Request executed successfully.")
            # Then storage command get executed.
            mock_exec_requests.assert_called_once_with(
                '/bin/true',
                env=mock.ANY,
                shell=True,
                check=True,
                stdin=-3,
                stdout=-1,
                stderr=-2,
                text=True,
            )
        else:
            self.getPage("/dashboard/")
            self.assertStatus(200)
            self.assertNotInBody("Request executed successfully.")
            # Then storage command get executed.
            mock_exec_requests.assert_not_called()

    @parameterized.expand(
        [
            # Staff has access to everything
            ('user_access0', User.TYPE_STAFF, '', True, User.VPN_ENABLED, True, User.ROLE_USERS),
            # Stud has access to everything
            ('user_access1', User.TYPE_STUD, '', True, User.VPN_ENABLED, True, User.ROLE_USERS),
            # Ext has access to vpn, wlan
            ('user_access2', User.TYPE_EXT, '', False, User.VPN_DISABLED, False, User.ROLE_GUEST),
            # Guest has not access
            ('user_access3', User.TYPE_GUEST, '', False, User.VPN_DISABLED, False, User.ROLE_GUEST),
            # Guest member of group
            (
                'user_access4',
                User.TYPE_GUEST,
                'cn=IDM.perm.compute.nextcloud.access,ou=Security,ou=Groups,dc=bfh',
                True,
                User.VPN_DISABLED,
                False,
                User.ROLE_GUEST,
            ),
            (
                'user_access5',
                User.TYPE_GUEST,
                'cn=IDM.perm.infrastructure.vpn.access,ou=Security,ou=Groups,dc=bfh',
                False,
                User.VPN_ENABLED,
                False,
                User.ROLE_GUEST,
            ),
            (
                'user_access6',
                User.TYPE_GUEST,
                'cn=IDM.perm.network.wlan.access,ou=Security,ou=Groups,dc=bfh',
                False,
                User.VPN_DISABLED,
                True,
                User.ROLE_GUEST,
            ),
            # Admin role
            (
                'user_access7',
                User.TYPE_STAFF,
                'cn=IDM.perm.infrastructure.ssp.admin,ou=Security,ou=Groups,dc=bfh',
                True,
                User.VPN_ENABLED,
                True,
                User.ROLE_ADMIN,
            ),
            (
                'user_access7',
                User.TYPE_STAFF,
                'cn=IDM.ser-its.pers-vma,ou=Security,ou=Groups,dc=bfh',
                True,
                User.VPN_ENABLED,
                True,
                User.ROLE_ADMIN,
            ),
        ]
    )
    def test_user_access(self, username, account_type, member_of, nextcloud_enabled, vpn_status, wlan_enabled, role):
        self.conn.strategy.add_entry(
            f'cn={username},ou=Staff,ou=Accounts,dc=bfh',
            {
                'userPassword': self.password,
                'uid': [username],
                'objectClass': ['posixAccount'],
                'bfhAccountType': account_type,
                'memberOf': [member_of],
            },
        )
        user = User.from_username(username)
        # Mock mfa status
        user.mfa_status = User.MFA_ENABLED
        self.assertEqual(nextcloud_enabled, user.nextcloud_enabled)
        self.assertEqual(vpn_status, user.vpn_status)
        self.assertEqual(wlan_enabled, user.wlan_enabled)
        self.assertEqual(role, user.role)
