-- Copyright (c) Huawei Technologies Co., Ltd. 2025-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 skynet = require 'skynet'
local log = require 'mc.logging'
local Singleton = require 'mc.singleton'
local mc_utils = require 'mc.utils'
local utils = require 'basic_cooling.cooling_utils'

local fan_board_data_keeping = require 'basic_cooling.data_keeping.fan_board_data_keeping'

local thermal_subsystem = class()
local thermal_subsystem_monitor = {
    total_power = 0,
    last_total_power = 0,
    power_calc_cnt = 0,
    last_tick = 0,
    this_tick = 0
}
function thermal_subsystem:ctor(db, base_service)
    self.obj = nil
    self.obj_pst = nil
    self.interval = 6000
    self.db = db
    self.base_service = base_service

    self.fan_board_data_keeping = fan_board_data_keeping.get_instance()
end

function thermal_subsystem:init()
    self:register()
    self:thermal_subsystem_metric_start()
end

function thermal_subsystem:init_thermal_subsystem_obj()
    self.obj_pst = self.db.ThermalSubsystem({Id = 1})
    if self.obj_pst['StandardPumpsResourceEnabled'] ~= nil then
        self.obj['StandardPumpsResourceEnabled'] = self.obj_pst['StandardPumpsResourceEnabled']
    end
    if self.obj_pst['EnergyConsumptionkWh'] ~= nil then
        self.obj['EnergyConsumptionkWh'] = self.obj_pst['EnergyConsumptionkWh']
    end
    if self.obj_pst['ResetTime'] ~= nil then
        self.obj['ResetTime'] = self.obj_pst['ResetTime']
    end
end

function thermal_subsystem:set_obj(object)
    self.obj = object
    self:init_thermal_subsystem_obj()
end

function thermal_subsystem:get_thermal_subsystem_total_power_watts()
    if not self.obj then
        return nil
    end
    return self.fan_board_data_keeping:get_total_power_watts()
end

function thermal_subsystem:clear_monitor_data()
    thermal_subsystem_monitor.last_total_power = 0
    thermal_subsystem_monitor.power_calc_cnt = 0
    thermal_subsystem_monitor.last_tick = thermal_subsystem_monitor.this_tick
end

function thermal_subsystem:set_standard_pumps_resource_enabled(enabled)
    if self.obj then
        self.obj_pst['StandardPumpsResourceEnabled'] = enabled
        self.obj_pst:save()
        self.obj['StandardPumpsResourceEnabled'] = enabled
        return true
    end
    return false
end

function thermal_subsystem:get_standard_pumps_resource_enabled()
    if self.obj then
        return self.obj['StandardPumpsResourceEnabled']
    end
    return nil
end

function thermal_subsystem:set_total_power_watts(power_watts)
    if self.obj and power_watts ~= self.obj['TotalPowerWatts'] then
        self.obj['TotalPowerWatts'] = power_watts
    end
end

function thermal_subsystem:thermal_subsystem_metric_start()
    local time_diff = 0
    skynet.fork_loop({ count = 0 }, function ()
        self.update_wait_time = 0
        self.save_wait_time = 0
        while true do
            skynet.sleep(self.interval)
            if thermal_subsystem_monitor.last_tick == 0 then
                thermal_subsystem_monitor.last_tick = mc_utils.time_ms()
                goto continue
            end
            local power_watts = self:get_thermal_subsystem_total_power_watts()
            if not power_watts then
                self:set_total_power_watts(0)
                goto continue
            end
            self:set_total_power_watts(power_watts)
            thermal_subsystem_monitor.this_tick = mc_utils.time_ms()
            thermal_subsystem_monitor.total_power = power_watts
            thermal_subsystem_monitor.power_calc_cnt = thermal_subsystem_monitor.power_calc_cnt + 1
            thermal_subsystem_monitor.last_total_power =
                thermal_subsystem_monitor.last_total_power + thermal_subsystem_monitor.total_power
            time_diff = thermal_subsystem_monitor.this_tick - thermal_subsystem_monitor.last_tick
            -- time_diff < 0 时间往前跳变时，更新上次时间为当前时间
            -- time_diff > 10000 防止ac掉电上电后第一次进入时last_tick是个极小正值，造成小段时间间隔为异常时间间隔
            if time_diff < 0 or time_diff > 10000 then
                self:clear_monitor_data()
                goto continue
            end
            self.update_wait_time = self.update_wait_time + self.interval
            self.save_wait_time = self.save_wait_time + self.interval
            -- 保存间隔达到10分钟，保存数据
            if self.save_wait_time >= 60000 then
                self:save_thermal_metric_data()
                self.save_wait_time = 0
            end
            -- 更新间隔未达到10s，不进行更新
            if self.update_wait_time < 1000 then
                goto continue
            end
            self.update_wait_time = 0
            local short_time_avg_power =
                thermal_subsystem_monitor.last_total_power / thermal_subsystem_monitor.power_calc_cnt
            local energy_added =
                (short_time_avg_power * (thermal_subsystem_monitor.this_tick - thermal_subsystem_monitor.last_tick)) /
                (1000 * 1000 * 60 * 60)
            self.obj['EnergyConsumptionkWh'] = self.obj['EnergyConsumptionkWh'] + energy_added
            self:clear_monitor_data()
            if self.obj['ResetTime'] == 0 then
                self.obj['ResetTime'] = os.time()
            end
            ::continue::
        end
    end)
end

function thermal_subsystem:save_thermal_metric_data()
    if not self.obj_pst then
        log:error('Save thermal metric data failed: obj_pst is nil')
        return
    end
    self.obj_pst['EnergyConsumptionkWh'] = self.obj['EnergyConsumptionkWh']
    self.obj_pst['ResetTime'] = self.obj['ResetTime']
    self.obj_pst:save()
end

function thermal_subsystem:reset_metrics(ctx)
    log:notice('Restart thermal subsystem metric statistics successfully')
    utils.op(ctx, 'Restart thermal subsystem metric statistics successfully')
    local current_time = os.time()
    self.obj['ResetTime'] = current_time
    self.obj['EnergyConsumptionkWh'] = 0
    self.obj_pst['EnergyConsumptionkWh'] = 0
    self.obj_pst['ResetTime'] = current_time
    self.obj_pst:save()
end

function thermal_subsystem:register()
    self.base_service:ImplThermalSubsystemMetricsResetMetrics(function(obj, ctx, ...)
        return self:reset_metrics(ctx)
    end)
end

return Singleton(thermal_subsystem)