#  Copyright (c) 2016 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#

from __future__ import annotations

import copy

from osc_lib.tests import utils as oscutils

from ironicclient.osc.v1 import baremetal_driver
from ironicclient.tests.unit.osc.v1 import fakes as baremetal_fakes


class TestBaremetalDriver(baremetal_fakes.TestBaremetal):

    def setUp(self) -> None:
        super(TestBaremetalDriver, self).setUp()

        self.baremetal_mock = self.app.client_manager.baremetal
        self.baremetal_mock.reset_mock()


class TestListBaremetalDriver(TestBaremetalDriver):

    def setUp(self) -> None:
        super(TestListBaremetalDriver, self).setUp()

        self.baremetal_mock.driver.list.return_value = [
            baremetal_fakes.FakeBaremetalResource(
                None,
                copy.deepcopy(baremetal_fakes.BAREMETAL_DRIVER),
                loaded=True)
        ]
        self.cmd = baremetal_driver.ListBaremetalDriver(self.app, None)

    def test_baremetal_driver_list(self) -> None:
        arglist = []
        verifylist = []

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
        columns, data = self.cmd.take_action(parsed_args)

        collist = (
            "name",
            "hosts")
        self.assertEqual(collist, tuple(columns))

        datalist = ((
            baremetal_fakes.baremetal_driver_name,
            ', '.join(baremetal_fakes.baremetal_driver_hosts)), )
        self.assertEqual(datalist, tuple(data))

    def test_baremetal_driver_list_with_type(self) -> None:
        arglist = ['--type', baremetal_fakes.baremetal_driver_type]
        verifylist = [('type', baremetal_fakes.baremetal_driver_type)]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
        columns, data = self.cmd.take_action(parsed_args)

        collist = (
            "name",
            "hosts")
        self.assertEqual(collist, tuple(columns))

        datalist = ((
            baremetal_fakes.baremetal_driver_name,
            ', '.join(baremetal_fakes.baremetal_driver_hosts)),)
        self.assertEqual(datalist, tuple(data))

    def test_baremetal_driver_list_with_detail(self) -> None:
        arglist = ['--long']
        verifylist = [('long', True)]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
        columns, data = self.cmd.take_action(parsed_args)

        collist = (
            "name",
            "type",
            "hosts",
            'default_bios_interface',
            'default_boot_interface',
            'default_console_interface',
            'default_deploy_interface',
            'default_firmware_interface',
            'default_inspect_interface',
            'default_management_interface',
            'default_network_interface',
            'default_power_interface',
            'default_raid_interface',
            'default_rescue_interface',
            'default_storage_interface',
            'default_vendor_interface',
            'enabled_bios_interfaces',
            'enabled_boot_interfaces',
            'enabled_console_interfaces',
            'enabled_deploy_interfaces',
            'enabled_firmware_interfaces',
            'enabled_inspect_interfaces',
            'enabled_management_interfaces',
            'enabled_network_interfaces',
            'enabled_power_interfaces',
            'enabled_raid_interfaces',
            'enabled_rescue_interfaces',
            'enabled_storage_interfaces',
            'enabled_vendor_interfaces'
        )
        self.assertEqual(collist, tuple(columns))

        datalist = ((
            baremetal_fakes.baremetal_driver_name,
            baremetal_fakes.baremetal_driver_type,
            ', '.join(baremetal_fakes.baremetal_driver_hosts),
            baremetal_fakes.baremetal_driver_default_bios_if,
            baremetal_fakes.baremetal_driver_default_boot_if,
            baremetal_fakes.baremetal_driver_default_console_if,
            baremetal_fakes.baremetal_driver_default_deploy_if,
            baremetal_fakes.baremetal_driver_default_firmware_if,
            baremetal_fakes.baremetal_driver_default_inspect_if,
            baremetal_fakes.baremetal_driver_default_management_if,
            baremetal_fakes.baremetal_driver_default_network_if,
            baremetal_fakes.baremetal_driver_default_power_if,
            baremetal_fakes.baremetal_driver_default_raid_if,
            baremetal_fakes.baremetal_driver_default_rescue_if,
            baremetal_fakes.baremetal_driver_default_storage_if,
            baremetal_fakes.baremetal_driver_default_vendor_if,
            ', '.join(baremetal_fakes.baremetal_driver_enabled_bios_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_boot_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_console_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_deploy_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_firmware_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_inspect_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_management_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_network_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_power_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_raid_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_rescue_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_storage_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_vendor_ifs),
        ),)
        self.assertEqual(datalist, tuple(data))

    def test_baremetal_driver_list_fields(self) -> None:
        arglist = [
            '--fields', 'name', 'hosts'
        ]
        verifylist = [
            ('fields', [['name', 'hosts']])
        ]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
        self.cmd.take_action(parsed_args)

        kwargs = {
            'driver_type': None,
            'detail': None,
            'fields': ('name', 'hosts')
        }

        self.baremetal_mock.driver.list.assert_called_with(**kwargs)

    def test_baremetal_driver_list_fields_multiple(self) -> None:
        arglist = [
            '--fields', 'name',
            '--fields', 'hosts', 'type'
        ]
        verifylist = [
            ('fields', [['name'], ['hosts', 'type']])
        ]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
        self.cmd.take_action(parsed_args)

        kwargs = {
            'driver_type': None,
            'detail': None,
            'fields': ('name', 'hosts', 'type')
        }

        self.baremetal_mock.driver.list.assert_called_with(**kwargs)

    def test_baremetal_driver_list_invalid_fields(self) -> None:
        arglist = [
            '--fields', 'name', 'invalid'
        ]
        verifylist = [
            ('fields', [['name', 'invalid']])
        ]
        self.assertRaises(oscutils.ParserException,
                          self.check_parser,
                          self.cmd, arglist, verifylist)

    def test_baremetal_driver_list_fields_with_long(self) -> None:
        arglist = [
            '--fields', 'name', 'hosts',
            '--long'
        ]
        verifylist = [
            ('fields', [['name', 'invalid']]),
            ('long', True)
        ]
        self.assertRaises(oscutils.ParserException,
                          self.check_parser,
                          self.cmd, arglist, verifylist)


class TestListBaremetalDriverProperty(TestBaremetalDriver):

    def setUp(self) -> None:
        super(TestListBaremetalDriverProperty, self).setUp()

        self.baremetal_mock.driver.properties.return_value = {
            'property1': 'description1', 'property2': 'description2'}
        self.cmd = baremetal_driver.ListBaremetalDriverProperty(
            self.app, None)

    def test_baremetal_driver_property_list(self) -> None:
        arglist = ['fakedrivername']
        verifylist = []

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
        columns, data = self.cmd.take_action(parsed_args)
        self.baremetal_mock.driver.properties.assert_called_with(*arglist)

        collist = ['property', 'description']
        self.assertEqual(collist, columns)
        expected_data = [('property1', 'description1'),
                         ('property2', 'description2')]
        self.assertEqual(expected_data, data)

    def test_baremetal_driver_list_no_arg(self) -> None:
        arglist = []
        verifylist = []

        self.assertRaises(oscutils.ParserException,
                          self.check_parser,
                          self.cmd, arglist, verifylist)


class TestListBaremetalDriverRaidProperty(TestBaremetalDriver):

    def setUp(self) -> None:
        super(TestListBaremetalDriverRaidProperty, self).setUp()

        (self.baremetal_mock.driver.
         raid_logical_disk_properties.return_value) = {
            'RAIDProperty1': 'driver_raid_property1',
            'RAIDProperty2': 'driver_raid_property2',
        }

        self.cmd = (
            baremetal_driver.ListBaremetalDriverRaidProperty(
                self.app, None))

    def test_baremetal_driver_raid_property_list(self) -> None:
        arglist = ['fakedrivername']
        verifylist = []

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
        columns, data = self.cmd.take_action(parsed_args)

        (self.baremetal_mock.driver.
         raid_logical_disk_properties.assert_called_with(*arglist))

        collist = ('property', 'description')
        self.assertEqual(collist, tuple(columns))

        expected_data = [('RAIDProperty1', 'driver_raid_property1'),
                         ('RAIDProperty2', 'driver_raid_property2')]
        self.assertEqual(expected_data, data)

    def test_baremetal_driver_raid_property_list_no_arg(self) -> None:
        arglist = []
        verifylist = []

        self.assertRaises(oscutils.ParserException,
                          self.check_parser,
                          self.cmd, arglist, verifylist)


class TestPassthruCallBaremetalDriver(TestBaremetalDriver):

    def setUp(self) -> None:
        super(TestPassthruCallBaremetalDriver, self).setUp()

        self.baremetal_mock.driver.vendor_passthru.return_value = (
            baremetal_fakes.BAREMETAL_DRIVER_PASSTHRU
        )

        self.cmd = baremetal_driver.PassthruCallBaremetalDriver(self.app, None)

    def test_baremetal_driver_passthru_call_with_min_args(self) -> None:

        arglist = [
            baremetal_fakes.baremetal_driver_name,
            baremetal_fakes.baremetal_driver_passthru_method,
        ]

        verifylist = [
            ('driver', baremetal_fakes.baremetal_driver_name),
            ('method', baremetal_fakes.baremetal_driver_passthru_method),
        ]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
        self.cmd.take_action(parsed_args)

        # Set expected values
        args = [
            baremetal_fakes.baremetal_driver_name,
            baremetal_fakes.baremetal_driver_passthru_method,
        ]
        kwargs = {
            'http_method': 'POST',
            'args': {}
        }
        (self.baremetal_mock.driver.vendor_passthru.
            assert_called_once_with(*args, **kwargs))

    def test_baremetal_driver_passthru_call_with_all_args(self) -> None:

        arglist = [
            baremetal_fakes.baremetal_driver_name,
            baremetal_fakes.baremetal_driver_passthru_method,
            '--arg', 'arg1=val1', '--arg', 'arg2=val2',
            '--http-method', 'POST'
        ]

        verifylist = [
            ('driver', baremetal_fakes.baremetal_driver_name),
            ('method', baremetal_fakes.baremetal_driver_passthru_method),
            ('arg', ['arg1=val1', 'arg2=val2']),
            ('http_method', 'POST')
        ]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
        self.cmd.take_action(parsed_args)

        # Set expected values
        args = [
            baremetal_fakes.baremetal_driver_name,
            baremetal_fakes.baremetal_driver_passthru_method,
        ]
        kwargs = {
            'http_method': 'POST',
            'args': {'arg1': 'val1', 'arg2': 'val2'}
        }
        (self.baremetal_mock.driver.vendor_passthru.
            assert_called_once_with(*args, **kwargs))

    def test_baremetal_driver_passthru_call_no_arg(self) -> None:
        arglist = []
        verifylist = []

        self.assertRaises(oscutils.ParserException,
                          self.check_parser,
                          self.cmd, arglist, verifylist)


class TestPassthruListBaremetalDriver(TestBaremetalDriver):

    def setUp(self) -> None:
        super(TestPassthruListBaremetalDriver, self).setUp()

        self.baremetal_mock.driver.get_vendor_passthru_methods.return_value = (
            baremetal_fakes.BAREMETAL_DRIVER_PASSTHRU
        )
        self.cmd = baremetal_driver.PassthruListBaremetalDriver(self.app, None)

    def test_baremetal_driver_passthru_list(self) -> None:
        arglist = ['fakedrivername']
        verifylist = []

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
        columns, data = self.cmd.take_action(parsed_args)

        args = ['fakedrivername']
        (self.baremetal_mock.driver.get_vendor_passthru_methods.
            assert_called_with(*args))

        collist = (
            "name",
            "http_methods",
            "async",
            "description",
            "attach",
        )
        self.assertEqual(collist, tuple(columns))

        datalist = (('lookup', 'POST', 'false', '', 'false'),)

        self.assertEqual(datalist, tuple(data))

    def test_baremetal_driver_passthru_list_no_arg(self) -> None:
        arglist = []
        verifylist = []

        self.assertRaises(oscutils.ParserException,
                          self.check_parser,
                          self.cmd, arglist, verifylist)


class TestShowBaremetalDriver(TestBaremetalDriver):

    def setUp(self) -> None:
        super(TestShowBaremetalDriver, self).setUp()

        self.baremetal_mock.driver.get.return_value = (
            baremetal_fakes.FakeBaremetalResource(
                None,
                copy.deepcopy(baremetal_fakes.BAREMETAL_DRIVER),
                loaded=True))
        self.cmd = baremetal_driver.ShowBaremetalDriver(self.app, None)

    def test_baremetal_driver_show(self) -> None:
        arglist = ['fakedrivername']
        verifylist = []

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
        columns, data = self.cmd.take_action(parsed_args)

        args = ['fakedrivername']
        self.baremetal_mock.driver.get.assert_called_with(*args, fields=None)
        self.assertFalse(self.baremetal_mock.driver.properties.called)

        collist = ('default_bios_interface', 'default_boot_interface',
                   'default_console_interface', 'default_deploy_interface',
                   'default_firmware_interface', 'default_inspect_interface',
                   'default_management_interface',
                   'default_network_interface', 'default_power_interface',
                   'default_raid_interface', 'default_rescue_interface',
                   'default_storage_interface', 'default_vendor_interface',
                   'enabled_bios_interfaces', 'enabled_boot_interfaces',
                   'enabled_console_interfaces', 'enabled_deploy_interfaces',
                   'enabled_firmware_interfaces',
                   'enabled_inspect_interfaces',
                   'enabled_management_interfaces',
                   'enabled_network_interfaces', 'enabled_power_interfaces',
                   'enabled_raid_interfaces', 'enabled_rescue_interfaces',
                   'enabled_storage_interfaces', 'enabled_vendor_interfaces',
                   'hosts', 'name', 'type')
        self.assertEqual(collist, columns)

        datalist = (
            baremetal_fakes.baremetal_driver_default_bios_if,
            baremetal_fakes.baremetal_driver_default_boot_if,
            baremetal_fakes.baremetal_driver_default_console_if,
            baremetal_fakes.baremetal_driver_default_deploy_if,
            baremetal_fakes.baremetal_driver_default_firmware_if,
            baremetal_fakes.baremetal_driver_default_inspect_if,
            baremetal_fakes.baremetal_driver_default_management_if,
            baremetal_fakes.baremetal_driver_default_network_if,
            baremetal_fakes.baremetal_driver_default_power_if,
            baremetal_fakes.baremetal_driver_default_raid_if,
            baremetal_fakes.baremetal_driver_default_rescue_if,
            baremetal_fakes.baremetal_driver_default_storage_if,
            baremetal_fakes.baremetal_driver_default_vendor_if,
            ', '.join(baremetal_fakes.baremetal_driver_enabled_bios_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_boot_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_console_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_deploy_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_firmware_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_inspect_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_management_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_network_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_power_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_raid_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_rescue_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_storage_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_enabled_vendor_ifs),
            ', '.join(baremetal_fakes.baremetal_driver_hosts),
            baremetal_fakes.baremetal_driver_name,
            baremetal_fakes.baremetal_driver_type)

        self.assertEqual(datalist, tuple(data))

    def test_baremetal_driver_show_no_arg(self) -> None:
        arglist = []
        verifylist = []

        self.assertRaises(oscutils.ParserException,
                          self.check_parser,
                          self.cmd, arglist, verifylist)

    def test_baremetal_driver_show_fields(self) -> None:
        arglist = [
            'fakedrivername',
            '--fields', 'name', 'hosts'
        ]
        verifylist = [
            ('driver', 'fakedrivername'),
            ('fields', [['name', 'hosts']])
        ]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
        columns, data = self.cmd.take_action(parsed_args)

        args = ['fakedrivername']
        fields = ['name', 'hosts']
        self.baremetal_mock.driver.get.assert_called_with(*args, fields=fields)
        self.assertFalse(self.baremetal_mock.driver.properties.called)

    def test_baremetal_driver_show_fields_multiple(self) -> None:
        arglist = [
            'fakedrivername',
            '--fields', 'name',
            '--fields', 'hosts', 'type'
        ]
        verifylist = [
            ('driver', 'fakedrivername'),
            ('fields', [['name'], ['hosts', 'type']])
        ]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
        columns, data = self.cmd.take_action(parsed_args)

        args = ['fakedrivername']
        fields = ['name', 'hosts', 'type']
        self.baremetal_mock.driver.get.assert_called_with(*args, fields=fields)
        self.assertFalse(self.baremetal_mock.driver.properties.called)

    def test_baremetal_driver_show_invalid_fields(self) -> None:
        arglist = [
            'fakedrivername',
            '--fields', 'name', 'invalid'
        ]
        verifylist = [
            ('driver', 'fakedrivername'),
            ('fields', [['name', 'invalid']])
        ]

        self.assertRaises(oscutils.ParserException,
                          self.check_parser,
                          self.cmd, arglist, verifylist)
