// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
//
// 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.

use nv_redfish_core::Bmc;
use serde_json::Error as JsonError;
use std::error::Error as StdError;
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FmtResult;

/// Redfish Errors.
pub enum Error<B: Bmc> {
    /// Errors generated by BMC access.
    Bmc(B::Error),
    /// Account service is not supported by the BMC.
    #[cfg(feature = "accounts")]
    AccountServiceNotSupported,
    /// No available account slot found. This error happens for
    /// `slot_defined_user_accounts` feature.
    #[cfg(feature = "accounts")]
    AccountSlotNotAvailable,
    /// Assembly is not available on BMC.
    #[cfg(feature = "assembly")]
    AssemblyNotAvailable,
    /// Chassis not supported by BMC
    #[cfg(feature = "chassis")]
    ChassisNotSupported,
    /// Computer system not supported by BMC
    #[cfg(feature = "computer-systems")]
    SystemNotSupported,
    /// Processors not available for this system
    #[cfg(feature = "computer-systems")]
    ProcessorsNotAvailable,
    /// Storage not available for this system
    #[cfg(feature = "computer-systems")]
    StorageNotAvailable,
    /// Memory not available for this system
    #[cfg(feature = "computer-systems")]
    MemoryNotAvailable,
    /// Metrics not available for this entity
    MetricsNotAvailable,
    /// Update service not supported by BMC
    #[cfg(feature = "update-service")]
    UpdateServiceNotSupported,
    /// Firmware inventory not available
    #[cfg(feature = "update-service")]
    FirmwareInventoryNotAvailable,
    /// Software inventory not available
    #[cfg(feature = "update-service")]
    SoftwareInventoryNotAvailable,
    /// Action not available for this resource
    ActionNotAvailable,
    /// Sensors not available for this resource
    SensorsNotAvailable,
    /// Log service not available for this resource
    #[cfg(feature = "log-services")]
    LogServiceNotAvailable,
    /// Log entries not available
    #[cfg(feature = "log-services")]
    LogEntriesNotAvailable,
    /// Manager not supported by BMC
    #[cfg(feature = "managers")]
    ManagerNotSupported,
    /// Ethernet interfaces not available for this resource
    #[cfg(feature = "ethernet-interfaces")]
    EthernetInterfacesNotAvailable,
    /// Network adapters not available for this resource
    #[cfg(feature = "network-adapters")]
    NetworkAdaptersNotAvailable,
    /// Boot options not available for this resource
    #[cfg(feature = "boot-options")]
    BootOptionsNotAvailable,
    /// `PCIe` devices not available for this resource
    #[cfg(feature = "pcie-devices")]
    PcieDevicesNotAvailable,
    /// BIOS settings not available for the computer system.
    #[cfg(feature = "bios")]
    BiosNotAvailable,
    /// NVIDIA OEM extension is not availvle for the computer system.
    #[cfg(feature = "oem-nvidia-bluefield")]
    NvidiaComputerSystemNotAvailable,
    /// Secure boot is not available for this system
    #[cfg(feature = "secure-boot")]
    SecureBootNotAvailable,
    /// JSON parse error.
    Json(JsonError),
}

impl<B: Bmc> Display for Error<B> {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        match self {
            Self::Bmc(err) => write!(f, "BMC error: {err}"),
            Self::Json(err) => write!(f, "JSON error: {err}"),
            #[cfg(feature = "accounts")]
            Self::AccountServiceNotSupported => {
                write!(f, "Account service is not supported by system")
            }
            #[cfg(feature = "accounts")]
            Self::AccountSlotNotAvailable => {
                write!(f, "Free account slot is not found")
            }
            #[cfg(feature = "assembly")]
            Self::AssemblyNotAvailable => {
                write!(f, "Assembly is not available")
            }
            #[cfg(feature = "chassis")]
            Self::ChassisNotSupported => {
                write!(f, "Chassis is not supported by system")
            }
            #[cfg(feature = "computer-systems")]
            Self::SystemNotSupported => {
                write!(f, "Computer system is not supported by system")
            }
            #[cfg(feature = "computer-systems")]
            Self::ProcessorsNotAvailable => {
                write!(f, "Processors are not available for this system")
            }
            #[cfg(feature = "computer-systems")]
            Self::StorageNotAvailable => {
                write!(f, "Storage is not available for this system")
            }
            #[cfg(feature = "computer-systems")]
            Self::MemoryNotAvailable => {
                write!(f, "Memory is not available for this system")
            }
            Self::MetricsNotAvailable => {
                write!(f, "Metrics are not available for this entity")
            }
            #[cfg(feature = "update-service")]
            Self::UpdateServiceNotSupported => {
                write!(f, "Update service is not supported by system")
            }
            #[cfg(feature = "update-service")]
            Self::FirmwareInventoryNotAvailable => {
                write!(f, "Firmware inventory is not available")
            }
            #[cfg(feature = "update-service")]
            Self::SoftwareInventoryNotAvailable => {
                write!(f, "Software inventory is not available")
            }
            Self::ActionNotAvailable => {
                write!(f, "Action is not available for this resource")
            }
            Self::SensorsNotAvailable => {
                write!(f, "Sensors is not available for this resource")
            }
            #[cfg(feature = "log-services")]
            Self::LogServiceNotAvailable => {
                write!(f, "Log service is not available for this resource")
            }
            #[cfg(feature = "log-services")]
            Self::LogEntriesNotAvailable => {
                write!(f, "Log entries are not available")
            }
            #[cfg(feature = "ethernet-interfaces")]
            Self::EthernetInterfacesNotAvailable => {
                write!(f, "Ethernet interfaces are not available")
            }
            #[cfg(feature = "managers")]
            Self::ManagerNotSupported => {
                write!(f, "Manager is not supported by system")
            }
            #[cfg(feature = "network-adapters")]
            Self::NetworkAdaptersNotAvailable => {
                write!(f, "Network adpaters are not available")
            }
            #[cfg(feature = "boot-options")]
            Self::BootOptionsNotAvailable => {
                write!(f, "Boot options are not available")
            }
            #[cfg(feature = "pcie-devices")]
            Self::PcieDevicesNotAvailable => {
                write!(f, "PCIe devices are not available")
            }
            #[cfg(feature = "bios")]
            Self::BiosNotAvailable => {
                write!(f, "Bios settings are not available")
            }
            #[cfg(feature = "oem-nvidia-bluefield")]
            Self::NvidiaComputerSystemNotAvailable => {
                write!(
                    f,
                    "NVIDIA OEM extension for computer system is not available"
                )
            }
            #[cfg(feature = "secure-boot")]
            Self::SecureBootNotAvailable => {
                write!(f, "Secure boot is not available")
            }
        }
    }
}

impl<B: Bmc> Debug for Error<B> {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        Display::fmt(self, f)
    }
}

impl<B: Bmc> StdError for Error<B> {}
