/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025-2026. All rights reserved.
 * MindIE is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "pylog.h"

#include <Python.h>
#include <frameobject.h>

#include "system_log.h"

using namespace mindie_llm;

namespace FOUNDATION {
    PyDoc_STRVAR(LogModuleDoc, "C++ logging bindings based on system_log.");

    static void LogLineForPyObject(LogComponent comp, LogSeverity level, LogType type, const char* msg)
    {
        auto& mgr = LogManager::GetInstance();
        if (!mgr.IsPrintLog(comp, level)) {
            return;
        }

        const char* filename = "";
        size_t lineNum = 0;
        PyGILState_STATE gil = PyGILState_Ensure();
        PyFrameObject* frame = PyEval_GetFrame();
        PyFrameObject* caller = frame ? PyFrame_GetBack(frame) : nullptr;
        PyCodeObject* code = caller ? PyFrame_GetCode(caller) : nullptr;
        if (code) {
            PyObject* pyFile = code->co_filename;
            if (pyFile) filename = PyUnicode_AsUTF8(pyFile);
            lineNum = static_cast<size_t>(PyFrame_GetLineNumber(caller));
        }
        Py_XDECREF(code);
        Py_XDECREF(caller);
        PyGILState_Release(gil);

        LogLine line(comp, level, filename, lineNum);
        line.SetType(type) << msg;
    }

    static PyObject* PyLog(PyObject*, PyObject* args, LogSeverity level)
    {
        const char* msg;
        const char* compStr;
        const char* typeStr = "general";

        if (!PyArg_ParseTuple(args, "ss|s", &msg, &compStr, &typeStr)) {
            return nullptr;
        }
        LogComponent comp;
        if (!String2Component(compStr, comp)) {
            PyErr_Format(PyExc_ValueError, "invalid component: '%s'", compStr);
            return nullptr;
        }
        LogType type;
        if (!String2LogType(typeStr, type)) {
            PyErr_Format(PyExc_ValueError, "invalid log type: '%s'", typeStr);
            return nullptr;
        }
        LogLineForPyObject(comp, level, type, msg);
        Py_RETURN_NONE;
    }

    static PyObject* PyDebug(PyObject* s, PyObject* a)
    {
        return PyLog(s, a, LogSeverity::DEBUG);
    }

    static PyObject* PyInfo(PyObject* s, PyObject* a)
    {
        return PyLog(s, a, LogSeverity::INFO);
    }

    static PyObject* PyWarn(PyObject* s, PyObject* a)
    {
        return PyLog(s, a, LogSeverity::WARN);
    }

    static PyObject* PyError(PyObject* s, PyObject* a)
    {
        return PyLog(s, a, LogSeverity::ERROR);
    }

    static PyObject* PyCritical(PyObject* s, PyObject* a)
    {
        return PyLog(s, a, LogSeverity::CRITICAL);
    }

    static PyObject* PyAudit(PyObject* s, PyObject* a)
    {
        return PyLog(s, a, LogSeverity::AUDIT);
    }

    static PyObject* PySetLogLevel(PyObject*, PyObject* arg)
    {
        if (!PyUnicode_Check(arg)) {
            PyErr_SetString(PyExc_TypeError, "set_log_level(level: str), e.g. 'debug', 'llm:info;llmmodels:warn'"
            );
            return nullptr;
        }
        const char* levelStr = PyUnicode_AsUTF8(arg);
        if (levelStr == nullptr) {
            return nullptr;
        }
        const std::string level(levelStr);
        LogManager::GetInstance().LoadByComponentByString<LogSeverity>(level, GetAllLogSeverity(),
            [](const std::string& s) {
                LogSeverity lvl;
                if (!String2LogSeverity(s, lvl)) {
                    return LogSeverity::INFO;
                }
                return lvl;
            },
            [](ComponentConfig& c, LogSeverity v) {
                c.minLevel = v;
            }
        );
        Py_RETURN_NONE;
    }

    static PyMethodDef LogMethods[] = {
        {"debug",         PyDebug,       METH_VARARGS, "debug(msg, comp, 'general')"},
        {"info",          PyInfo,        METH_VARARGS, "info(msg, comp, 'general')"},
        {"warn",          PyWarn,        METH_VARARGS, "warn(msg, comp, 'general')"},
        {"error",         PyError,       METH_VARARGS, "error(msg, comp, 'general')"},
        {"critical",      PyCritical,    METH_VARARGS, "critical(msg, comp, 'general')"},
        {"audit",         PyAudit,       METH_VARARGS, "audit(msg, comp, 'general')"},
        {"set_log_level", PySetLogLevel, METH_O,       "set_log_level(level: str)"},
        {nullptr,         nullptr,       0,            nullptr}
    };

    static PyModuleDef g_LogModule = {
        PyModuleDef_HEAD_INIT,
        "foundation.log",   // m_name
        LogModuleDoc,       // m_doc
        -1,                 // m_size
        LogMethods,         // m_methods
        nullptr,            // m_slots
        nullptr,            // m_traverse
        nullptr,            // m_clear
        nullptr             // m_free
    };

    PyObject* GetLogModule()
    {
        return PyModule_Create(&g_LogModule);
    }

} // namespace FOUNDATION