# 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/>.

import json
from unittest import mock

from cherrypy_foundation.url import url_for
from parameterized import parameterized

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


MOCK_PROJECT_LIST = [
    {
        'id': '123',
        'name': 'test-project-name',
        'role_id': '89a9d472-76c7-465b-8cf7-0ca580b4ae27',
        'role_name': 'owner',
        'cpu': 12,
        'ram': 2048,
        'disk': 500,
    }
]
MOCK_SERVER_LIST = [
    {
        'id': '234',
        'name': 'server1',
        'cpu': 2,
        'ram': 512,
        'disk': 20,
    },
    {
        'id': '345',
        'name': 'server2',
        'cpu': 2,
        'ram': 512,
        'disk': 20,
    },
]


class CloudHandlerTest(WebCase):

    login = True

    default_config = {'enable-openstack-cmd': '/bin/true', 'openstack-auth-url': 'http://127.0.0.1'}

    @parameterized.expand(
        [
            (
                'valid_accept',
                {'accept': '1'},
                {
                    'SSP_USER_USERNAME': 'test-user',
                    'SSP_USER_DN': 'cn=test-user,ou=Staff,ou=Accounts,dc=bfh',
                    'SSP_USER_EMAIL': 'test@example.com',
                    'SSP_ACCEPT': 'True',
                },
            ),
        ]
    )
    @mock.patch('subprocess.run', return_value=_mock_subprocess())
    def test_enable_openstack(self, name, data, expected_env, mock_exec_requests):
        # Given a user
        # When user request cloud access
        self.getPage("/dashboard/enable-openstack", body=data, method='POST')
        # Then user redirect to dashboard
        self.assertStatus(303)
        self.assertHeaderItemValue('Location', f'{self.baseurl}/dashboard')
        # Then new page show success
        self.getPage("/dashboard/")
        self.assertStatus(200)
        self.assertInBody("Request executed successfully.")
        # Then cloud access 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,
        )
        self.assertEqual(mock_exec_requests.call_args.kwargs['env'], expected_env)

    def test_cloud_json_empty(self):
        # Given a user without data
        # When querying cloud list
        self.getJson("/dashboard/cloud.json")
        # Then the list is empty.
        self.assertBody('{"data": []}')

    @mock.patch('cherrypy.openstack.get_project_list', return_value=MOCK_PROJECT_LIST)
    @mock.patch('cherrypy.openstack.get_server_list', return_value=MOCK_SERVER_LIST)
    def test_cloud_json_with_data(self, mock_get_project_list, mock_get_server_list):
        # Given a user with cloud data
        # When querying cloud list
        self.getJson("/dashboard/cloud.json")
        self.assertStatus(200)
        # Then list of cloud is returned.
        self.assertEqual(
            json.loads(self.body),
            {
                'data': [
                    {
                        'id': '234',
                        'name': 'server1',
                        'cpu': 2,
                        'ram': 512,
                        'disk': 20,
                        'project': {
                            'id': '123',
                            'name': 'test-project-name',
                            'role_id': '89a9d472-76c7-465b-8cf7-0ca580b4ae27',
                            'role_name': 'owner',
                            'cpu': 12,
                            'ram': 2048,
                            'disk': 500,
                        },
                        'service': 'LNI Cloud',
                    },
                    {
                        'id': '345',
                        'name': 'server2',
                        'cpu': 2,
                        'ram': 512,
                        'disk': 20,
                        'project': {
                            'id': '123',
                            'name': 'test-project-name',
                            'role_id': '89a9d472-76c7-465b-8cf7-0ca580b4ae27',
                            'role_name': 'owner',
                            'cpu': 12,
                            'ram': 2048,
                            'disk': 500,
                        },
                        'service': 'LNI Cloud',
                    },
                ]
            },
        )

    @mock.patch('cherrypy.openstack.get_project_list', return_value=MOCK_PROJECT_LIST)
    @mock.patch('cherrypy.openstack.get_server_list', return_value=MOCK_SERVER_LIST)
    def test_cloud_with_selenium(self, mock_project_list, mock_server_list):
        # 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'))
            # Then page load without error
            print(driver.page_source)
            self.assertFalse(driver.get_log('browser'))
            # Then page contains our cloud table
            cloud_table = driver.find_element('css selector', "#cloud-table")
            # Then our table contains our project and servers
            cloud_table.find_element('xpath', "//*[contains(text(), 'test-project-name')]")
            cloud_table.find_element('xpath', "//*[contains(text(), 'server1')]")
            cloud_table.find_element('xpath', "//*[contains(text(), 'server2')]")
