#include "ANSCUSTOMPY.h"
#include <filesystem>
#include <json.hpp>
using json = nlohmann::json;
namespace fs = std::filesystem;
namespace py = pybind11;

namespace {
    using ThreadLocalModelMap = std::unordered_map<int, std::shared_ptr<ANSCENTER::ModelWrapper>>;
    ThreadLocalModelMap& GetThreadLocalState() {
        thread_local ThreadLocalModelMap threadState;
        return threadState;
    }
}
std::string CustomParametersToJson(const ANSCENTER::Params& params) {
    json root;
    // ROI_Config
    for (const auto& cfg : params.ROI_Config) {
        json j;
        j["Rectangle"] = cfg.Rectangle;
        j["Polygon"] = cfg.Polygon;
        j["Line"] = cfg.Line;
        j["MinItems"] = cfg.MinItems;
        j["MaxItems"] = cfg.MaxItems;
        j["Name"] = cfg.Name;
        j["ROI-Match"] = cfg.ROIMatch;
        root["ROI_Config"].push_back(j);
    }

    // ROI_Options
    for (const auto& opt : params.ROI_Options) {
        root["ROI_Options"].push_back(opt);
    }

    // Parameters
    for (const auto& param : params.Parameters) {
        json j;
        j["Name"] = param.Name;
        j["DataType"] = param.DataType;
        j["NoOfdecimals"] = param.NoOfDecimals;
        j["MaxValue"] = param.MaxValue;
        j["MinValue"] = param.MinValue;
        j["StartValue"] = param.StartValue;
        j["ListItems"] = param.ListItems;
        j["DefaultValue"] = param.DefaultValue;
        j["Value"] = param.Value;
        root["Parameters"].push_back(j);
    }

    // ROI_Values
    for (const auto& rv : params.ROI_Values) {
        json j;
        j["ROI-Match"] = rv.ROIMatch;

        for (const auto& pt : rv.ROIPoints) {
            j["ROIPoints"].push_back({ {"x", pt.x}, {"y", pt.y} });
        }

        j["Option"] = rv.Option;
        j["Name"] = rv.Name;
        j["OriginalImageSize"] = rv.OriginalImageSize;
        root["ROI_Values"].push_back(j);
    }

    // Convert to compact JSON string (no whitespace)
    std::string jsonString = root.dump();  // dump() without indent = compact

    return jsonString;
}
namespace ANSCENTER
{
    std::once_flag PythonRuntime::initFlag;
    std::unique_ptr<PythonRuntime> PythonRuntime::instance = nullptr;
    std::atomic<bool> PythonRuntime::_isInitialized{ false };
    std::atomic<bool> PythonRuntime::shuttingDown{ false };
    std::mutex PythonRuntime::_instanceMutex;

    std::mutex ANSCUSTOMPY::_globalInstancesMutex;
    std::unordered_set<ANSCUSTOMPY*> ANSCUSTOMPY::_globalInstances;
    std::atomic<int> ANSCUSTOMPY::globalInstanceCounter{ 1 };

    // Declare GetPythonRuntime as a friend function of PythonRuntime
    PythonRuntime& GetPythonRuntime(const std::wstring& pythonHome);

    PythonRuntime& GetPythonRuntime(const std::wstring& pythonHome) {
        std::call_once(PythonRuntime::initFlag, [&]() {
            std::lock_guard<std::mutex> lock(PythonRuntime::_instanceMutex);
            PythonRuntime::instance = std::unique_ptr<PythonRuntime>(new PythonRuntime(pythonHome));
            });
        return *PythonRuntime::instance;
    }

    PythonRuntime::PythonRuntime(const std::wstring& home) : pythonHome(home) {
        InitializeInterpreter();
    }

    bool PythonRuntime::IsInitialized() {
        return _isInitialized.load(std::memory_order_acquire) && Py_IsInitialized();
    }

    void PythonRuntime::InitializeInterpreter() {
        if (!pythonHome.empty()) {
#pragma warning(push)
#pragma warning(disable : 4996)
            Py_SetPythonHome(pythonHome.c_str());
#pragma warning(pop)
        }
        py::initialize_interpreter();
        _isInitialized.store(true, std::memory_order_release);
        PatchPythonStreamsSafe();
    }

    void PythonRuntime::PatchPythonStreamsSafe() {
        try {
            py::exec(R"(
import sys, io
class DummyStream(io.StringIO):
    def write(self, txt): pass
    def flush(self): pass
if sys.stdout is None or not hasattr(sys.stdout, "write"):
    sys.stdout = DummyStream()
if sys.stderr is None or not hasattr(sys.stderr, "write"):
    sys.stderr = DummyStream()
)");
        }
        catch (const py::error_already_set& e) {
            std::cerr << "[PatchPythonStreamsSafe] Python error: " << e.what() << std::endl;
        }
    }

    void PythonRuntime::AddToSysPath(const std::string& path) {
        if (!IsInitialized()) {
            std::cerr << "[PythonRuntime::AddToSysPath] Interpreter not initialized!\n";
            return;
        }
        py::gil_scoped_acquire gil;
        py::module_ sys = py::module_::import("sys");
        sys.attr("path").attr("insert")(0, path);
    }

    void PythonRuntime::Shutdown() {
        std::lock_guard<std::mutex> lock(_instanceMutex);
        if (shuttingDown.exchange(true)) return;
        if (_isInitialized.load(std::memory_order_acquire)) {
            try {
                py::gil_scoped_acquire gil;
                py::module::import("gc").attr("collect")();
            }
            catch (...) {
                std::cerr << "[PythonRuntime::Shutdown] GC failed (ignored)." << std::endl;
            }
            try {
                py::finalize_interpreter();
            }
            catch (...) {
                std::cerr << "[PythonRuntime::Shutdown] finalize_interpreter failed." << std::endl;
            }
            _isInitialized.store(false, std::memory_order_release);
        }
        instance.reset();
    }
    
  
    /// <summary>
	/// /// Initializes the ANSCUSTOMPY model with the given parameters.
    /// </summary>
    ANSCUSTOMPY::ANSCUSTOMPY() {
        _instanceId = globalInstanceCounter.fetch_add(1, std::memory_order_relaxed);
        _destroyed = false;
        _isInitialized = false;
    }
    ANSCUSTOMPY::~ANSCUSTOMPY() {
        try {
            if (PythonRuntime::IsInitialized())
                Destroy();
        }
        catch (...) {
            std::cerr << "[~ANSCUSTOMPY] Exception during destruction." << std::endl;
        }
    }
    void ANSCUSTOMPY::RegisterSelf() {
        {
            std::lock_guard<std::mutex> lock(_globalInstancesMutex);
            _globalInstances.insert(this);
        }
        auto& threadState = GetThreadLocalState();
        if (!_wrapper || !_wrapper->modelFactory) return;
        if (threadState.find(_instanceId) == threadState.end()) {
            if (_wrapper && _wrapper->modelFactory) {
                py::gil_scoped_acquire gil;
                auto newWrapper = std::make_shared<ModelWrapper>();
                newWrapper->modelFactory = _wrapper->modelFactory;
                newWrapper->map[0] = std::make_shared<py::object>(_wrapper->modelFactory());
                threadState[_instanceId] = newWrapper;
            }
        }
    }
    void ANSCUSTOMPY::UnregisterSelf() {
        std::lock_guard<std::mutex> lock(_globalInstancesMutex);
        _globalInstances.erase(this);
    }
    void ANSCUSTOMPY::ClearThreadState() {
        auto& threadState = GetThreadLocalState();
        auto it = threadState.find(_instanceId);
        if (it != threadState.end()) {
            if (PythonRuntime::IsInitialized()) {
                py::gil_scoped_acquire gil;
                it->second->map.clear();
            }
            else {
                it->second->map.clear();
            }
            threadState.erase(it);
        }
    }
    bool ANSCUSTOMPY::EnsureModelReady(const std::string& methodName) {
        if (!_isInitialized || _destroyed || !PythonRuntime::IsInitialized()) {
            return false;
        }
        //auto& threadState = GetThreadLocalState();
        //if (threadState.find(_instanceId) == threadState.end()) {
        //    if (_wrapper && _wrapper->modelFactory) {
        //        py::gil_scoped_acquire gil;
        //        auto newWrapper = std::make_shared<ModelWrapper>();
        //        newWrapper->modelFactory = _wrapper->modelFactory;
        //        newWrapper->map[0] = std::make_shared<py::object>(_wrapper->modelFactory());
        //        threadState[_instanceId] = newWrapper;
        //    }
        //    else {
        //        return false;
        //    }
        //}
        return true;
    }
    py::object ANSCUSTOMPY::GetThreadLocalModel() {
        auto& threadState = GetThreadLocalState();
        auto it = threadState.find(_instanceId);
        if (_destroyed || it == threadState.end() || !it->second) {
            return py::none();
        }
        auto& modelMap = it->second->map;
        auto modelIt = modelMap.find(0);
        if (modelIt == modelMap.end()) {
            py::gil_scoped_acquire gil;
            auto obj = it->second->modelFactory();
            if (obj.is_none()) return py::none();
            auto wrapped = std::make_shared<py::object>(obj);
            modelMap[0] = wrapped;
            return *wrapped;
        }
        return *modelIt->second;
    }
    bool ANSCUSTOMPY::Destroy() {
        //std::lock_guard<std::recursive_mutex> lock(_mutex);
        if (_destroyed) return true;
        _destroyed = true;
        ClearThreadState();
        UnregisterSelf();
        return true;
    }
    void ANSCUSTOMPY::SafeShutdownAll(bool waitForShutdown) {
        std::unordered_set<ANSCUSTOMPY*> instancesCopy;
        {
            std::lock_guard<std::mutex> lock(_globalInstancesMutex);
            instancesCopy = _globalInstances;
        }
        for (ANSCUSTOMPY* instance : instancesCopy) {
            if (instance) {
                try {
                    instance->ClearThreadState();
                    instance->Destroy();
                }
                catch (...) {
                    std::cerr << "[SafeShutdownAll] Destroy failed." << std::endl;
                }
            }
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(300));
        auto shutdownFunc = []() {
            try {
                std::this_thread::sleep_for(std::chrono::milliseconds(300));
                int retry = 0;
                while (!ANSCUSTOMPY::_globalInstances.empty() && retry++ < 100) {
                    std::this_thread::sleep_for(std::chrono::milliseconds(50));
                }
                if (PythonRuntime::IsInitialized()) {
                    py::gil_scoped_acquire gil;
                    py::module::import("gc").attr("collect")();
                    PythonRuntime::Shutdown();
                }
            }
            catch (...) {
                std::cerr << "[SafeShutdownAll] Shutdown failed." << std::endl;
            }
            };
        if (waitForShutdown) shutdownFunc();
        else std::thread(shutdownFunc).detach();
    }
    
    
    bool ANSCUSTOMPY::InitializePythonModel(const std::string& moduleName, const std::string& fullPath) {
        if (!PythonRuntime::IsInitialized()) {
            _logger.LogError("[InitializePythonModel] Python error:\n", "Python interpreter is not initialized.", __FILE__, __LINE__);
            return false;
        }
        py::gil_scoped_acquire gil;
        try {
            py::exec("import logging; logging.getLogger('ultralytics').setLevel(logging.CRITICAL)", py::globals());
            py::dict locals;
            std::ostringstream code;
            code << "import importlib.util\n"
                << "spec = importlib.util.spec_from_file_location('" << moduleName << "', r'" << fullPath << "')\n"
                << "mod = importlib.util.module_from_spec(spec)\n"
                << "spec.loader.exec_module(mod)\n";

            py::exec(code.str(), py::globals(), locals);
            py::object mod = locals["mod"];
            py::object modelClass = mod.attr("ANSModel");

            auto& threadState = GetThreadLocalState();
            auto& wrapper = threadState[_instanceId];  // ensure per-thread-per-instance
            if (!wrapper) wrapper = std::make_shared<ModelWrapper>();

            wrapper->modelFactory = [modelClass]() {
                    py::gil_scoped_acquire gil;
                    py::object instance = modelClass();
                    return instance;};
            _wrapper = wrapper;  // Store for main thread access too
            return true;
        }
        catch (const py::error_already_set& e) {
            PyErr_Print();
            _logger.LogFatal("InitializePythonModel", std::string("Python error: ") + e.what(), __FILE__, __LINE__);
            return false;
        }
    }
   
    bool ANSCUSTOMPY::Initialize(std::string licenseKey, ModelConfig modelConfig,
        const std::string& modelZipFilePath, const std::string& modelZipPassword,
        std::string& labelMap) {
        //std::lock_guard<std::recursive_mutex> lock(_mutex);
        try {
            // Step 1: Base model init
            bool result = ANSODBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap);
            if (!result) return false;

            _modelConfig = modelConfig;
            _modelConfig.inpHeight = 640;
            _modelConfig.inpWidth = 640;
            if (_modelConfig.modelMNSThreshold < 0.2) _modelConfig.modelMNSThreshold = 0.5;
            if (_modelConfig.modelConfThreshold < 0.2) _modelConfig.modelConfThreshold = 0.5;

            // Step 2: Validate model folder
            if (_modelFolder.empty()) {
                _logger.LogError("ANSCUSTOMPY::Initialize", "_modelFolder is not set.", __FILE__, __LINE__);
                return false;
            }

            // Step 3: Set Python Home
            std::wstring pythonHome;
            const fs::path sharedPython = "C:/ProgramData/ANSCENTER/Python311";
            const fs::path sharedDll = sharedPython / "python311.dll";

            if (fs::exists(sharedDll)) {
                pythonHome = sharedPython.wstring();
            }
            else {
                pythonHome = (fs::path(_modelFolder) / "python_env").wstring();
            }

            // Step 4: Initialize Python and sys.path
            auto& runtime = GetPythonRuntime(pythonHome);
            runtime.AddToSysPath(_modelFolder);

            // Step 5: Load Python model class from unique module
            //_scriptPath = _modelFolder + "/ansinfer.py";
            _scriptPath = fs::path(_modelFolder).append("ansinfer.py").string();
            _moduleName = "ansinfer_" + std::to_string(_instanceId);

            if (!InitializePythonModel(_moduleName, _scriptPath)) {
                _logger.LogError("ANSCUSTOMPY::Initialize", "Failed to load Python model.", __FILE__, __LINE__);
                return false;
            }

            // Step 6: Load class names
            _classFilePath = CreateFilePath(_modelFolder, "classes.names");
            std::ifstream isValidFileName(_classFilePath);
            if (!isValidFileName) {
                _logger.LogDebug("ANSCUSTOMPY::LoadModel. Load classes from string", _classFilePath, __FILE__, __LINE__);
                LoadClassesFromString();
            }
            else {
                _logger.LogDebug("ANSCUSTOMPY::LoadModel. Load classes from file", _classFilePath, __FILE__, __LINE__);
                LoadClassesFromFile();
            }

            labelMap.clear();
            if (!_classes.empty())
                labelMap = VectorToCommaSeparatedString(_classes);

            _destroyed = false;
            _isInitialized = true;
            RegisterSelf();
            return true;

        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSCUSTOMPY::Initialize", e.what(), __FILE__, __LINE__);
            return false;
        }
    }

    bool ANSCUSTOMPY::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) {
        //std::lock_guard<std::recursive_mutex> lock(_mutex);
        try {
            // Step 0: Assign unique instance ID 
            if (!ANSODBase::LoadModel(modelZipFilePath, modelZipPassword))
                return false;

            if (_modelFolder.empty()) {
                _logger.LogError("ANSCUSTOMPY::LoadModel", "_modelFolder is not set.", __FILE__, __LINE__);
                return false;
            }

            // Step 3: Set Python home
            std::wstring pythonHome;
            const fs::path sharedPython = "C:/ProgramData/ANSCENTER/Python311";
            const fs::path sharedDll = sharedPython / "python311.dll";

            if (fs::exists(sharedDll)) {
                pythonHome = sharedPython.wstring();
            }
            else {
                pythonHome = (fs::path(_modelFolder) / "python_env").wstring();
            }

            // Step 4: Initialize Python and sys.path
            auto& runtime = GetPythonRuntime(pythonHome);
            runtime.AddToSysPath(_modelFolder);

            // Step 5: Load Python model class from unique module
            //_scriptPath = _modelFolder + "/ansinfer.py";
            _scriptPath = fs::path(_modelFolder).append("ansinfer.py").string();
            _moduleName = "ansinfer_" + std::to_string(_instanceId);

            if (!InitializePythonModel(_moduleName, _scriptPath)) {
                _logger.LogError("ANSCUSTOMPY::LoadModel", "Failed to load Python model.", __FILE__, __LINE__);
                return false;
            }
            _destroyed = false;
            _isInitialized = true;
            RegisterSelf();
            return true;
        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSCUSTOMPY::LoadModel", e.what(), __FILE__, __LINE__);
            return false;
        }
    }

    bool ANSCUSTOMPY::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig,
        std::string modelName, std::string className,
        const std::string& modelFolder,
        std::string& labelMap)
    {
        //std::lock_guard<std::recursive_mutex> lock(_mutex);
        try {
            // Step 0: Assign unique instance ID early
            if (!ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap))
                return false;
            _modelConfig = modelConfig;
            _modelConfig.inpHeight = 640;
            _modelConfig.inpWidth = 640;
            if (_modelConfig.modelMNSThreshold < 0.2) _modelConfig.modelMNSThreshold = 0.5;
            if (_modelConfig.modelConfThreshold < 0.2) _modelConfig.modelConfThreshold = 0.5;

            // Step 2: Validate model folder
            if (_modelFolder.empty()) {
                _logger.LogError("ANSCUSTOMPY::Initialize", "_modelFolder is not set.", __FILE__, __LINE__);
                return false;
            }

            // Step 3: Set Python home
            std::wstring pythonHome;
            const fs::path sharedPython = "C:/ProgramData/ANSCENTER/Python311";
            const fs::path sharedDll = sharedPython / "python311.dll";

            if (fs::exists(sharedDll)) {
                pythonHome = sharedPython.wstring();
            }
            else {
                pythonHome = (fs::path(_modelFolder) / "python_env").wstring();
            }

            // Step 4: Initialize Python and sys.path
            auto& runtime = GetPythonRuntime(pythonHome);
            runtime.AddToSysPath(_modelFolder);

            // Step 5: Load Python model class from unique module
            //_scriptPath = _modelFolder + "/ansinfer.py";
            _scriptPath = fs::path(_modelFolder).append("ansinfer.py").string();
            _moduleName = "ansinfer_" + std::to_string(_instanceId);

            if (!InitializePythonModel(_moduleName, _scriptPath)) {
                _logger.LogError("ANSCUSTOMPY::Initialize", "Failed to load Python model.", __FILE__, __LINE__);
                return false;
            }

            // Step 6: Load class labels
            _classFilePath = CreateFilePath(_modelFolder, "classes.names");
            std::ifstream isValidFileName(_classFilePath);
            if (!isValidFileName) {
                _logger.LogDebug("ANSCUSTOMPY::LoadModel", "Load classes from string", __FILE__, __LINE__);
                LoadClassesFromString();
            }
            else {
                _logger.LogDebug("ANSCUSTOMPY::LoadModel", "Load classes from file", __FILE__, __LINE__);
                LoadClassesFromFile();
            }

            labelMap.clear();
            if (!_classes.empty())
                labelMap = VectorToCommaSeparatedString(_classes);
            _destroyed = false;
            _isInitialized = true;
            RegisterSelf();
            return true;
        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSCUSTOMPY::LoadModelFromFolder", e.what(), __FILE__, __LINE__);
            return false;
        }
    }

    std::vector<Object> ANSCUSTOMPY::RunInference(const cv::Mat& input) {
        return RunInference(input, "CustomPyCam");
    }
    std::vector<Object> ANSCUSTOMPY::RunInference(const cv::Mat& input, const std::string& camera_id) {
        std::vector<Object> results;
        if (!_isInitialized) {
            _logger.LogError("ANSCUSTOMPY::RunInference", "Model is not initialized.", __FILE__, __LINE__);
            return results;
        }
        if (!_licenseValid) {
            _logger.LogError("ANSCUSTOMPY::RunInference", "Invalid License", __FILE__, __LINE__);
            return results;
        }
        if (input.empty()) {
            _logger.LogError("ANSCUSTOMPY::RunInference", "Input image is empty.", __FILE__, __LINE__);
            return results;
        }
        try {
            std::string jsonStr = RunPythonInferenceFromMat(input);
            if (!jsonStr.empty()) {
                results = ANSUtilityHelper::GetDetectionsFromString(jsonStr);
            }
            else {
                _logger.LogWarn("ANSCUSTOMPY::RunInference", "Empty JSON result from Python inference.", __FILE__, __LINE__);
            }
        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSCUSTOMPY::RunInference", e.what(), __FILE__, __LINE__);
        }

        return results;
    }
 
    std::string ANSCUSTOMPY::RunPythonInferenceFromMat(const cv::Mat& image) {
        if (!EnsureModelReady("RunPythonInferenceFromMat"))
            return "";
        if (image.empty())
            return "";

        try {
            py::object model = GetThreadLocalModel();
            if (model.is_none()) {
                _logger.LogError("ANSCUSTOMPY::RunPythonInferenceFromMat", "Model is None.", __FILE__, __LINE__);
                return "";
            }
            py::gil_scoped_acquire gil;
            if (!py::hasattr(model, "run_inference")) {
                _logger.LogError("ANSCUSTOMPY::RunPythonInferenceFromMat", "Model has no 'run_inference' method.", __FILE__, __LINE__);
                return "";
            }
            py::bytes image_bytes(reinterpret_cast<const char*>(image.data), image.total() * image.elemSize());
            py::object result = model.attr("run_inference")(image_bytes, image.cols, image.rows, image.channels());
            return result.cast<std::string>();
        }
        catch (const py::error_already_set& e) {
            PyErr_Print();
            _logger.LogFatal("RunPythonInferenceFromMat", e.what(), __FILE__, __LINE__);
            return "";
        }
        catch (const std::exception& e) {
            _logger.LogFatal("RunPythonInferenceFromMat", e.what(), __FILE__, __LINE__);
            return "";
        }
    }

    bool ANSCUSTOMPY::OptimizeModel(bool fp16, std::string& optimizedModelFolder) {
        if (!EnsureModelReady("OptimizeModel")) return false;
        try {
            py::object model = GetThreadLocalModel();
            if (model.is_none()) {
                _logger.LogError("ANSCUSTOMPY::OptimizeModel", "Model is None.", __FILE__, __LINE__);
                return false;
            }
            py::gil_scoped_acquire gil;
            if (!py::hasattr(model, "model_optimize")) {
                _logger.LogError("ANSCUSTOMPY::OptimizeModel", "Python model has no method 'model_optimize'.", __FILE__, __LINE__);
                return false;
            }
            py::object result;
            try {
                result = model.attr("model_optimize")(fp16);
            }
            catch (const py::error_already_set& e) {
                PyErr_Print();
                _logger.LogFatal("ANSCUSTOMPY::OptimizeModel", std::string("Exception in 'model_optimize': ") + e.what(), __FILE__, __LINE__);
                return false;
            }

            if (result.is_none()) {
                _logger.LogError("ANSCUSTOMPY::OptimizeModel", "Python 'model_optimize' returned None.", __FILE__, __LINE__);
                return false;
            }

            if (!py::isinstance<py::bool_>(result)) {
                _logger.LogError("ANSCUSTOMPY::OptimizeModel", "Unexpected return type from 'model_optimize'. Expected bool.", __FILE__, __LINE__);
                return false;
            }

            bool success = result.cast<bool>();
            if (success) {
                optimizedModelFolder = fs::path(_scriptPath).parent_path().string();
            }

            return success;
        }
        catch (const py::error_already_set& e) {
            PyErr_Print();
            _logger.LogFatal("ANSCUSTOMPY::OptimizeModel", e.what(), __FILE__, __LINE__);
            return false;
        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSCUSTOMPY::OptimizeModel", e.what(), __FILE__, __LINE__);
            return false;
        }
    }
    bool ANSCUSTOMPY::ConfigureParameters(Params& param) {
        if (!EnsureModelReady("ConfigureParamaters")) return false;

        try {
            py::object model = GetThreadLocalModel();
            if (model.is_none()) {
                _logger.LogError("ANSCUSTOMPY::ConfigureParamaters", "Model is None.", __FILE__, __LINE__);
                return false;
            }
            py::gil_scoped_acquire gil;
            if (!py::hasattr(model, "configureParameters")) {
                _logger.LogError("ANSCUSTOMPY::ConfigureParamaters", "Python model missing 'configureParameters' method.", __FILE__, __LINE__);
                return false;
            }
            py::object result;
            try {
                result = model.attr("configureParameters")();
            }
            catch (const py::error_already_set& e) {
                PyErr_Print();
                _logger.LogFatal("ANSCUSTOMPY::ConfigureParamaters", std::string("Exception in 'configureParameters': ") + e.what(), __FILE__, __LINE__);
                return false;
            }

            if (result.is_none()) {
                _logger.LogError("ANSCUSTOMPY::ConfigureParamaters", "Returned None from Python method.", __FILE__, __LINE__);
                return false;
            }

            if (!py::isinstance<py::str>(result)) {
                _logger.LogError("ANSCUSTOMPY::ConfigureParamaters", "Expected Python string return from 'configureParameters'.", __FILE__, __LINE__);
                return false;
            }

            std::string jsonStr = result.cast<std::string>();
            if (jsonStr.empty()) {
                _logger.LogError("ANSCUSTOMPY::ConfigureParamaters", "Empty JSON string returned from Python.", __FILE__, __LINE__);
                return false;
            }

            param = ANSUtilityHelper::ParseCustomParameters(jsonStr);
            return true;
        }
        catch (const py::error_already_set& e) {
            PyErr_Print();
            _logger.LogFatal("ANSCUSTOMPY::ConfigureParamaters", e.what(), __FILE__, __LINE__);
            return false;
        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSCUSTOMPY::ConfigureParamaters", e.what(), __FILE__, __LINE__);
            return false;
        }
    }

    bool ANSCUSTOMPY::SetParameters(const Params& param) {
        if (!EnsureModelReady("SetParamaters")) return false;
        try {
            std::string jsonStr = CustomParametersToJson(param);
            if (jsonStr.empty()) {
                _logger.LogError("ANSCUSTOMPY::SetParamaters", "Serialized parameter string is empty.", __FILE__, __LINE__);
                return false;
            }
            py::object model = GetThreadLocalModel();
            if (model.is_none()) {
                _logger.LogError("ANSCUSTOMPY::SetParamaters", "Model is None.", __FILE__, __LINE__);
                return false;
            }
            py::gil_scoped_acquire gil;
            if (!py::hasattr(model, "setParameters")) {
                _logger.LogError("ANSCUSTOMPY::SetParamaters", "Python model has no method 'setParameters'.", __FILE__, __LINE__);
                return false;
            }
            py::object result;
            try {
                result = model.attr("setParameters")(jsonStr);
            }
            catch (const py::error_already_set& e) {
                PyErr_Print();
                _logger.LogFatal("ANSCUSTOMPY::SetParamaters", std::string("Exception in 'setParameters': ") + e.what(), __FILE__, __LINE__);
                return false;
            }

            if (result.is_none()) {
                _logger.LogError("ANSCUSTOMPY::SetParamaters", "Python method returned None.", __FILE__, __LINE__);
                return false;
            }

            if (!py::isinstance<py::bool_>(result)) {
                _logger.LogError("ANSCUSTOMPY::SetParamaters", "Python method returned non-boolean type.", __FILE__, __LINE__);
                return false;
            }

            return result.cast<bool>();
        }
        catch (const py::error_already_set& e) {
            PyErr_Print();
            _logger.LogFatal("ANSCUSTOMPY::SetParamaters", std::string("Python error: ") + e.what(), __FILE__, __LINE__);
            return false;
        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSCUSTOMPY::SetParamaters", e.what(), __FILE__, __LINE__);
            return false;
        }
    }
}

