// Copyright (C) 2026 Kiyotsugu Arai
// SPDX-License-Identifier: LGPL-3.0-or-later

// error.hpp
//
// Error handling for the state-management system
//
// This file provides additional interfaces for error handling related to the numeric-state-management system.
// It implements more specialized error handling using the exception classes and state-error functionality defined in common.hpp.
//
// Main features:
// - Specialized error-detection utilities
// - Additional error-handling policies

#ifndef SANGI_ERROR_HPP
#define SANGI_ERROR_HPP

#include <math/core/numeric_state.hpp>
#include <math/core/common.hpp>
#include <string>
#include <stdexcept>
#include <concepts>

namespace sangi {

    // Error-handling utilities

    // Division-by-zero check
    template<typename T>
    inline void check_division_by_zero(T divisor, const std::string& message = "Division by zero") {
        if (divisor == T(0)) {
            const auto& options = global_state_error_options();
            switch (options.divide_by_zero_error_mode) {
            case StateErrorMode::Exception:
                throw DivideByZeroError(message);
            case StateErrorMode::ReturnSpecial:
            case StateErrorMode::Silent:
                // Special values are handled by the caller
                break;
            }
        }
    }

    // NaN check
    template<std::floating_point T>
    inline void check_nan(const T& value, const std::string& message = "Operation resulted in NaN") {
        if (std::isnan(value)) {
            const auto& options = global_state_error_options();
            switch (options.nan_error_mode) {
            case StateErrorMode::Exception:
                throw NaNError(message);
            case StateErrorMode::ReturnSpecial:
            case StateErrorMode::Silent:
                // Special values are handled by the caller
                break;
            }
        }
    }

    // Infinity check
    template<std::floating_point T>
    inline void check_infinity(const T& value, const std::string& message = "Operation resulted in infinity") {
        if (std::isinf(value)) {
            const auto& options = global_state_error_options();
            switch (options.infinity_error_mode) {
            case StateErrorMode::Exception:
                throw InfinityError(message, value > 0);
            case StateErrorMode::ReturnSpecial:
            case StateErrorMode::Silent:
                // Special values are handled by the caller
                break;
            }
        }
    }

    // Overflow check
    inline void check_overflow(bool overflow_condition, const std::string& message = "Numeric overflow") {
        if (overflow_condition) {
            const auto& options = global_state_error_options();
            switch (options.overflow_error_mode) {
            case StateErrorMode::Exception:
                throw OverflowError(message);
            case StateErrorMode::ReturnSpecial:
            case StateErrorMode::Silent:
                // Special values are handled by the caller
                break;
            }
        }
    }

    // Underflow check
    inline void check_underflow(bool underflow_condition, const std::string& message = "Numeric underflow") {
        if (underflow_condition) {
            const auto& options = global_state_error_options();
            switch (options.underflow_error_mode) {
            case StateErrorMode::Exception:
                throw UnderflowError(message);
            case StateErrorMode::ReturnSpecial:
            case StateErrorMode::Silent:
                // Special values are handled by the caller
                break;
            }
        }
    }

    // Divergence check
    inline void check_divergence(bool divergence_condition,
        DivergenceDetail detail = DivergenceDetail::Diverging,
        const std::string& message = "Algorithm diverged") {
        if (divergence_condition) {
            const auto& options = global_state_error_options();
            switch (options.divergence_error_mode) {
            case StateErrorMode::Exception:
                throw DivergenceStateError(message, detail);
            case StateErrorMode::ReturnSpecial:
            case StateErrorMode::Silent:
                // Special values are handled by the caller
                break;
            }
        }
    }

    // Combined error-cause + state check
    inline void check_error_and_state(NumericError error, NumericState state,
        const std::string& message = "Numeric error occurred") {
        const auto& options = global_state_error_options();

        // Checks based on error cause
        if (error != NumericError::None) {
            switch (error) {
            case NumericError::DivideByZero:
                if (options.divide_by_zero_error_mode == StateErrorMode::Exception) {
                    throw DivideByZeroError(message);
                }
                break;
            case NumericError::NaNPropagation:
            case NumericError::ExplicitNaN:
                if (options.nan_error_mode == StateErrorMode::Exception) {
                    throw NaNError(message);
                }
                break;
            case NumericError::ConversionError:
            case NumericError::OutOfRangeInput:
                if (options.overflow_error_mode == StateErrorMode::Exception) {
                    throw OverflowError(message);
                }
                break;
            case NumericError::DivergenceError:
                if (options.divergence_error_mode == StateErrorMode::Exception) {
                    throw DivergenceStateError(message);
                }
                break;
            default:
                // Other errors are not specifically handled
                break;
            }
        }

        // Checks based on state
        if (NumericStateTraits::isNaN(state) && options.nan_error_mode == StateErrorMode::Exception) {
            throw NaNError(message);
        }
        if (NumericStateTraits::isInfinite(state)) {
            if (options.infinity_error_mode == StateErrorMode::Exception) {
                bool is_positive = (state == NumericState::PositiveInfinity);
                throw InfinityError(message, is_positive);
            }
        }
        if (state == NumericState::Overflow && options.overflow_error_mode == StateErrorMode::Exception) {
            throw OverflowError(message);
        }
        if (state == NumericState::Underflow && options.underflow_error_mode == StateErrorMode::Exception) {
            throw UnderflowError(message);
        }
        if (NumericStateTraits::isDivergent(state) && options.divergence_error_mode == StateErrorMode::Exception) {
            throw DivergenceStateError(message);
        }
    }

    // Additional: utility to verify consistency between state and error
    inline void validate_state_and_error(NumericState& state, NumericError& error) {
        // If an error exists while the state is normal, set the state matching the error
        if (error != NumericError::None && state == NumericState::Normal) {
            state = NumericStateTraits::errorToState(error);
        }

        // If the state is special but no error is set, set the default error matching the state
        if (NumericStateTraits::requiresErrorCode(state) && error == NumericError::None) {
            error = NumericStateTraits::stateToDefaultError(state);
        }
    }

    // Additional: helper to set state and error consistently
    inline void set_state_and_error(NumericState& state_dest, NumericError& error_dest,
        NumericState state_src, NumericError error_src) {
        state_dest = state_src;
        error_dest = error_src;
        validate_state_and_error(state_dest, error_dest);
    }

} // namespace sangi

#endif // SANGI_ERROR_HPP