-- Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
--
-- this file licensed under the 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.

local class = require 'mc.class'
local client = require 'observability.client'
local service = require 'observability.service'
local skynet = require 'skynet'
local mc_admin = require 'mc.mc_admin'
local utils = require 'mc.utils'
local context = require 'mc.context'
local utils_core = require 'utils.core'
local server_mdb = require 'mdb_server.server'
local receiver_mdb = require 'mdb_server.receiver'
local log = require 'mc.logging'
local vos = require 'utils.vos'
local policy_config = require 'mdb_server.policy_config'
local m_test = require 'mdb_server.test.submit_test'
local m_control = require 'public.control'
local defs = require 'public.defs'

local observability = class(service)

function observability:check_dependencies()
    if skynet.getenv('TEST_DATA_DIR') ~= nil then
        return
    end

    local admin = mc_admin.new()
    admin:parse_dependency(utils_core.getcwd() .. '/mds/service.json')
    admin:check_dependency(self.bus)
end

function observability:method_register()
    service:ImplReceiversReceiverSubmitTestTelemetryData(function(object, ctx)
        self.m_test:test_submit_data(object, ctx)
    end)

    service:ImplTracesObservabilityServiceTracesGetConfigurations(function(object, ctx)
        return policy_config:get_traces_config()
    end)

    service:ImplTracesObservabilityServiceTracesSetConfigurations(function(object, ctx, config)
        policy_config:set_traces_config(config)
    end)

    service:ImplMetricsObservabilityServiceMetricsGetConfigurations(function(object, ctx)
        return policy_config:get_metrics_config()
    end)

    service:ImplMetricsObservabilityServiceMetricsSetConfigurations(function(object, ctx, config)
        policy_config:set_metrics_config(config)
    end)

    service:ImplLogsObservabilityServiceLogsGetConfigurations(function(object, ctx)
        return policy_config:get_logs_config()
    end)

    service:ImplLogsObservabilityServiceLogsSetConfigurations(function(object, ctx, config)
        policy_config:set_logs_config(config)
    end)
end

function observability:remote_monitor()
    --监听CA证书属性变更
    client:OnCertificatePropertiesChanged(function (values, path, intf)
        local expect_path = string.match(path, '^/bmc/kepler/Managers/.+/Certificates/.*$')
        if not expect_path or intf ~= defs.CERTIFICATE_INTERFACE then
            return
        end

        if values['FilePath'] ~= nil then
            self.control:set_state_flag()
        end
    end)

    --监听CA证书接口变更
    client:OnCertificateInterfacesRemoved(function (sender, path)
        local expect_path = string.match(path, '^/bmc/kepler/Managers/.+/Certificates/.*$')
        if not expect_path then
            return
        end

        self.control:set_state_flag()
    end)

    --监听SSL证书属性变更
    client:OnCertificatePropertiesChanged(function (values, path, intf)
        local expect_path = string.match(path, '^/bmc/kepler/Managers/.+/NetworkProtocol/HTTPS/Certificates/1.*$')
        if not expect_path or intf ~= defs.CERTIFICATE_INTERFACE then
            return
        end

        if values['FilePath'] ~= nil then
            self.control:set_state_flag()
        end
    end)
end

local function get_frudata_path()
    -- 重试次数为300次
    local retry_times = 300
    local frudata_objs
    local overview_objs
    while retry_times > 0 do
        frudata_objs = client:GetProductObjects()
        overview_objs = client:GetOverviewObjects()
        for path, _ in pairs(frudata_objs) do
            if overview_objs[path].FruId and overview_objs[path].FruId == defs.FRU_ID then
                return path
            end
        end
        retry_times = retry_times - 1
        skynet.sleep(200)
    end

    return nil
end

function observability:server_identity_remote_monitor()
    -- 获取主板资源树路径
    local frudata_path = get_frudata_path()
    if frudata_path == nil then
        log:error('Get frudata objects failed')
        return
    end

    -- 监听BoardSerialNumber属性变更
    client:OnBoardPropertiesChanged(function(values, path, intf)
        if path ~= frudata_path or intf ~= defs.FRUDATA_BOARD_INTF then
            return
        end

        local observability_obj = self.db:select(self.db.ObservabilityService):first()
        if values['BoardSerialNumber'] ~= nil and observability_obj.ServerIdentity == 'BoardSN' then
            self.control:set_state_flag()
        end
    end)

    -- 监听AssetTag属性变更
    client:OnProductPropertiesChanged(function(values, path, intf)
        if path ~= frudata_path or intf ~= defs.FRUDATA_PRODUCT_INTF then
            return
        end

        local observability_obj = self.db:select(self.db.ObservabilityService):first()
        if values['AssetTag'] ~= nil and observability_obj.ServerIdentity == 'ProductAssetTag' then
            self.control:set_state_flag()
        end
    end)

    -- 监听HostName属性变更
    client:OnAssemblyPropertiesChanged(function(values, path, intf)
        local expect_path = string.match(path, '^/bmc/kepler/Managers/.+/Assembly.*$')
        if not expect_path or intf ~= defs.ASSEMBLY_INTF then
            return
        end

        local observability_obj = self.db:select(self.db.ObservabilityService):first()
        if values['HostName'] ~= nil  and observability_obj.ServerIdentity == 'HostName' then
            self.control:set_state_flag()
        end
    end)
end

function observability:init()
    log:notice('observability init start')
    self.super.init(self)
    -- 依赖检查
    self:check_dependencies()

    self.module_name = 'observability'

    self.control = m_control.new(self.db)

    -- 服务端属性初始化
    server_mdb:init(self.db, self.bus)

    -- 接收端属性初始化
    receiver_mdb:init(self.db, self.bus)

    -- 策略配置初始化
    policy_config:init(self.db, self.bus)

    self.m_test = m_test.new(self.db, self.module_name)

    -- 注册方法
    self:method_register()

    -- 注册监听资源树属性函数
    skynet.fork_once(observability.remote_monitor, self)
    skynet.fork_once(observability.server_identity_remote_monitor, self)

    -- 生成fluent-bit pid文件，属主为comm_user
    if not vos.get_file_accessible(defs.TMP_OBSERVABILITY_DIR) then
        utils.mkdir(defs.TMP_OBSERVABILITY_DIR, utils.S_IRUSR | utils.S_IWUSR)
        utils_core.chown_s(defs.TMP_OBSERVABILITY_DIR, defs.COMM_USER_UID, defs.COMM_USER_GID)
    end

    if not vos.get_file_accessible(defs.SSL_KEY_PATH) then
        utils.mkdir(defs.SSL_KEY_PATH, utils.S_IRUSR | utils.S_IWUSR)
        utils_core.chown_s(defs.SSL_KEY_PATH, defs.COMM_USER_UID, defs.COMM_USER_GID)
    end

    if not utils_core.is_file(defs.FLUENT_BIT_PID_FILE_PATH) then
        vos.system_s('/bin/sh', '-c', 'touch ' .. defs.FLUENT_BIT_PID_FILE_PATH)
        utils_core.chown_s(defs.FLUENT_BIT_PID_FILE_PATH, defs.COMM_USER_UID, defs.COMM_USER_GID)
    end

    log:notice('observability init success')
end

return observability
