/**
 * MVGAL - Multi-Vendor GPU Aggregation Layer for Linux
 * Power Manager Implementation - Idle detection, power gating, DVFS
 * SPDX-License-Identifier: MIT
 */

#include "power_manager.hpp"
#include "device_registry.hpp"
#include "daemon.hpp"
#include <fstream>
#include <sstream>
#include <thread>
#include <cmath>
#include <algorithm>

namespace mvgal {

PowerManager::PowerManager(Daemon* daemon, DeviceRegistry* deviceRegistry)
    : m_daemon(daemon),
      m_deviceRegistry(deviceRegistry)
{
    /* Initialize default configuration */
    m_config.idleTimeoutMs = 5000;       /* 5 seconds */
    m_config.sustainedTimeoutMs = 10000;  /* 10 seconds */
    m_config.parkTimeoutMs = 30000;       /* 30 seconds */
    m_config.thermalThreshold = 85;       /* 85 C */
    m_config.criticalThreshold = 95;     /* 95 C */
    m_config.thermalPollIntervalMs = 1000; /* 1 second */
    m_config.powerLimitWatts = 0;         /* No limit */
    m_config.enablePowerCapping = false;
    m_config.enableDvfs = true;
    m_config.dvfsPollIntervalMs = 2000;   /* 2 seconds */
    m_config.enableAggressivePowerDown = false;
}

PowerManager::~PowerManager()
{
}

bool PowerManager::init()
{
    uint32_t gpuCount = m_deviceRegistry->gpuCount();
    
    m_gpuPowerInfo.resize(gpuCount);
    m_dvfsState.resize(gpuCount);
    m_fanCurves.resize(gpuCount);
    m_powerCurves.resize(gpuCount);
    
    for (uint32_t i = 0; i < gpuCount; i++) {
        auto ts = std::chrono::high_resolution_clock::now().time_since_epoch();
        
        m_gpuPowerInfo[i] = {
            PowerState::ACTIVE,      /* state */
            PowerState::ACTIVE,      /* targetState */
            {0, 0, 0, 0, 0, 0, 0},   /* stats */
            ts,                      /* lastActivityTime */
            ts,                      /* lastStateChange */
            ThermalState::NORMAL,    /* thermalState */
            0,                       /* lastTemperature */
            true,                    /* enabled */
            false,                    /* inThermalThrottle */
            {true, true, 0, 0, 0} /* fanControl (enabled, automatic) */
        };
        
        m_dvfsState[i] = {
            0,                       /* lastUtilization */
            0,                       /* targetClockMhz */
            0                        /* currentClockMhz */
        };
    }
    
    // Initialize default fan curves for all GPUs
    const FanCurvePoint defaultCurve[] = {
        {40, 20},  /* 40C -> 20% */
        {50, 30},  /* 50C -> 30% */
        {60, 45},  /* 60C -> 45% */
        {70, 60},  /* 70C -> 60% */
        {80, 75},  /* 80C -> 75% */
        {90, 90},  /* 90C -> 90% */
        {100, 100} /* 100C -> 100% */
    };
    const uint32_t numDefaultPoints = sizeof(defaultCurve) / sizeof(defaultCurve[0]);
    
    for (uint32_t i = 0; i < gpuCount; i++) {
        GpuFanCurve& curve = m_fanCurves[i];
        curve.numPoints = numDefaultPoints;
        for (uint32_t j = 0; j < numDefaultPoints; j++) {
            curve.points[j] = defaultCurve[j];
        }
    }

    const PowerCurvePoint defaultPowerCurve[] = {
        {0, 30}, {25, 50}, {50, 70}, {75, 90}, {100, 100},
    };
    const uint32_t numPowerPoints =
        static_cast<uint32_t>(sizeof(defaultPowerCurve) / sizeof(defaultPowerCurve[0]));

    for (uint32_t i = 0; i < gpuCount; i++) {
        GpuPowerCurve& pcurve = m_powerCurves[i];
        pcurve.numPoints = numPowerPoints;
        for (uint32_t j = 0; j < numPowerPoints; j++) {
            pcurve.points[j] = defaultPowerCurve[j];
        }
    }

    // Initialize default PSU config
    m_psuConfig.totalCapacityWatts = 0;      /* Unknown, will be probed */
    m_psuConfig.safetyMarginPercent = 20;     /* 20% safety margin */
    m_psuConfig.enableHeadroomTracking = false; /* Disabled by default */
    
    m_lastThermalUpdate = std::chrono::high_resolution_clock::now().time_since_epoch();
    m_lastDvfsUpdate = m_lastThermalUpdate;
    
    return true;
}

void PowerManager::fini()
{
    std::lock_guard<std::mutex> lock(m_mutex);
    m_gpuPowerInfo.clear();
    m_dvfsState.clear();
}

void PowerManager::processEvents()
{
    std::lock_guard<std::mutex> lock(m_mutex);
    
    auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
    
    /* Check idle timeouts */
    checkIdleTimeouts();
    
    /* Check thermal state */
    auto thermalElapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
        now - m_lastThermalUpdate).count();
    if (thermalElapsed >= static_cast<int64_t>(m_config.thermalPollIntervalMs)) {
        updateThermalData();
        checkThermalState();
        m_lastThermalUpdate = now;
    }
    
    /* Apply DVFS and power curves */
    if (m_config.enableDvfs) {
        auto dvfsElapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
            now - m_lastDvfsUpdate).count();
        if (dvfsElapsed >= static_cast<int64_t>(m_config.dvfsPollIntervalMs)) {
            applyDvfs();
            applyPowerCurves();
            m_lastDvfsUpdate = now;
        }
    }

    if (m_psuConfig.enableHeadroomTracking) {
        updatePsuHeadroom();
    }

    /* Apply fan control */
    applyFanControl();
}

bool PowerManager::setPowerState(uint32_t gpuIndex, PowerState state)
{
    std::lock_guard<std::mutex> lock(m_mutex);
    
    if (gpuIndex >= m_gpuPowerInfo.size()) {
        return false;
    }
    
    transitionGpuPower(gpuIndex, state);
    return true;
}

PowerState PowerManager::getPowerState(uint32_t gpuIndex) const
{
    std::lock_guard<std::mutex> lock(m_mutex);
    
    if (gpuIndex >= m_gpuPowerInfo.size()) {
        return PowerState::ACTIVE;
    }
    
    return m_gpuPowerInfo[gpuIndex].state;
}

GpuPowerStats PowerManager::getPowerStats(uint32_t gpuIndex) const
{
    std::lock_guard<std::mutex> lock(m_mutex);
    
    if (gpuIndex >= m_gpuPowerInfo.size()) {
        return {0, 0, 0, 0, 0, 0, 0};
    }
    
    return m_gpuPowerInfo[gpuIndex].stats;
}

void PowerManager::setThermalThreshold(uint32_t /*gpuIndex*/, uint32_t thresholdCelsius)
{
    std::lock_guard<std::mutex> lock(m_mutex);
    /* Not per-GPU yet */
    m_config.thermalThreshold = thresholdCelsius;
}

ThermalState PowerManager::getThermalState(uint32_t gpuIndex) const
{
    std::lock_guard<std::mutex> lock(m_mutex);
    
    if (gpuIndex >= m_gpuPowerInfo.size()) {
        return ThermalState::NORMAL;
    }
    
    return m_gpuPowerInfo[gpuIndex].thermalState;
}

void PowerManager::setConfig(const PowerConfig& config)
{
    std::lock_guard<std::mutex> lock(m_mutex);
    m_config = config;
}

void PowerManager::notifyActivity(uint32_t gpuIndex)
{
    std::lock_guard<std::mutex> lock(m_mutex);
    
    if (gpuIndex >= m_gpuPowerInfo.size()) {
        return;
    }
    
    auto& info = m_gpuPowerInfo[gpuIndex];
    info.lastActivityTime = std::chrono::high_resolution_clock::now().time_since_epoch();
    
    /* If GPU was in a low-power state, wake it up */
    if (info.state != PowerState::ACTIVE && !info.inThermalThrottle) {
        transitionGpuPower(gpuIndex, PowerState::ACTIVE);
    }
    
    /* Update stats */
    auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
        now - info.lastStateChange).count();
    
    if (info.state == PowerState::ACTIVE) {
        info.stats.activeTimeUs += static_cast<uint64_t>(duration);
    } else {
        info.stats.idleTimeUs += static_cast<uint64_t>(duration);
    }
    
    info.lastStateChange = now;
}

void PowerManager::setEnabled(uint32_t gpuIndex, bool enabled)
{
    std::lock_guard<std::mutex> lock(m_mutex);
    
    if (gpuIndex < m_gpuPowerInfo.size()) {
        m_gpuPowerInfo[gpuIndex].enabled = enabled;
    }
}

void PowerManager::updateThermalData()
{
    for (uint32_t i = 0; i < m_gpuPowerInfo.size(); i++) {
        int32_t temp;
        if (readTemperature(i, &temp)) {
            m_gpuPowerInfo[i].lastTemperature = temp;
        }
        
        uint32_t milliwatts;
        if (readPowerDraw(i, &milliwatts)) {
            m_gpuPowerInfo[i].stats.powerDrawMilliwatts = milliwatts;
        }
        
        // Update max temperature
        if (temp > m_gpuPowerInfo[i].stats.maxTemperature) {
            m_gpuPowerInfo[i].stats.maxTemperature = temp;
        }
    }
}

void PowerManager::applyDvfs()
{
    if (!m_config.enableDvfs) {
        return;
    }
    
    for (uint32_t i = 0; i < m_dvfsState.size(); i++) {
        const auto* gpu = m_deviceRegistry->getGpu(i);
        if (!gpu) {
            continue;
        }
        
        uint32_t utilization = gpu->utilization();
        m_dvfsState[i].lastUtilization = utilization;
        
        /* Simple DVFS: scale frequency with utilization */
        /* Get max clock from capabilities (not yet available) */
        uint32_t max_clock = 2000; /* Assume 2 GHz max */
        uint32_t min_clock = 500;  /* Assume 500 MHz min */
        
        /* Target clock = min + (max - min) * utilization/100 */
        uint32_t target_clock = min_clock + 
            static_cast<uint32_t>((max_clock - min_clock) * (utilization / 100.0));
        
        m_dvfsState[i].targetClockMhz = target_clock;
        
        /* Apply to GPU if different */
        if (m_dvfsState[i].currentClockMhz != target_clock) {
            if (writeFrequency(i, target_clock)) {
                m_dvfsState[i].currentClockMhz = target_clock;
            }
        }
    }
}

void PowerManager::checkIdleTimeouts()
{
    auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
    
    for (uint32_t i = 0; i < m_gpuPowerInfo.size(); i++) {
        auto& info = m_gpuPowerInfo[i];
        
        if (!info.enabled || info.inThermalThrottle) {
            continue;
        }
        
        auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
            now - info.lastActivityTime).count();
        
        PowerState newState = info.state;
        
        switch (info.state) {
        case PowerState::ACTIVE:
            if (elapsed >= static_cast<int64_t>(m_config.idleTimeoutMs)) {
                newState = PowerState::SUSTAINED;
            }
            break;
        case PowerState::SUSTAINED:
            if (elapsed >= static_cast<int64_t>(m_config.idleTimeoutMs + m_config.sustainedTimeoutMs)) {
                newState = PowerState::IDLE;
            } else if (elapsed < static_cast<int64_t>(m_config.idleTimeoutMs)) {
                newState = PowerState::ACTIVE;
            }
            break;
        case PowerState::IDLE:
            if (elapsed >= static_cast<int64_t>(m_config.idleTimeoutMs + 
                                                   m_config.sustainedTimeoutMs + 
                                                   m_config.parkTimeoutMs)) {
                if (m_config.enableAggressivePowerDown) {
                    newState = PowerState::PARK;
                }
            } else if (elapsed < static_cast<int64_t>(m_config.idleTimeoutMs + m_config.sustainedTimeoutMs)) {
                newState = PowerState::ACTIVE;
            }
            break;
        case PowerState::PARK:
            if (elapsed < static_cast<int64_t>(m_config.idleTimeoutMs + 
                                                   m_config.sustainedTimeoutMs + 
                                                   m_config.parkTimeoutMs)) {
                newState = PowerState::ACTIVE;
            }
            break;
        }
        
        if (newState != info.state) {
            transitionGpuPower(i, newState);
        }
    }
}

void PowerManager::checkThermalState()
{
    for (uint32_t i = 0; i < m_gpuPowerInfo.size(); i++) {
        auto& info = m_gpuPowerInfo[i];
        int32_t temp = info.lastTemperature;
        
        /* Update thermal state */
        if (temp >= static_cast<int32_t>(m_config.criticalThreshold)) {
            info.thermalState = ThermalState::CRITICAL;
            info.inThermalThrottle = true;
        } else if (temp >= static_cast<int32_t>(m_config.thermalThreshold)) {
            info.thermalState = ThermalState::THROTTLING;
            info.inThermalThrottle = true;
        } else if (temp >= static_cast<int32_t>(m_config.thermalThreshold - 5)) {
            info.thermalState = ThermalState::HOT;
            info.inThermalThrottle = false;
        } else if (temp >= static_cast<int32_t>(m_config.thermalThreshold - 15)) {
            info.thermalState = ThermalState::WARN;
            info.inThermalThrottle = false;
        } else {
            info.thermalState = ThermalState::NORMAL;
            info.inThermalThrottle = false;
        }
        
        /* If in thermal throttle, force to active state to allow throttling */
        if (info.inThermalThrottle && info.state != PowerState::ACTIVE) {
            transitionGpuPower(i, PowerState::ACTIVE);
        }
    }
}

void PowerManager::transitionGpuPower(uint32_t gpuIndex, PowerState newState)
{
    auto& info = m_gpuPowerInfo[gpuIndex];
    
    if (info.state == newState) {
        return;
    }
    
    info.state = newState;
    info.targetState = newState;
    info.lastStateChange = std::chrono::high_resolution_clock::now().time_since_epoch();
    info.stats.transitions++;
    
    /* Call vendor-specific power state function */
    const auto* gpu = m_deviceRegistry->getGpu(gpuIndex);
    if (!gpu) return;

    if (gpu->vendor() == VendorId::NVIDIA && gpu->nvmlDevice()) {
        /* NVML doesn't have a direct "power state" enum like ours, 
         * but we can adjust power limits or persistence mode. */
        unsigned int limit = 0;
        switch (newState) {
        case PowerState::ACTIVE:
            limit = static_cast<unsigned int>(gpu->capabilities().vramSize > 0 ? 250000 : 0); /* Example: 250W */
            break;
        case PowerState::SUSTAINED:
            limit = 150000; /* 150W */
            break;
        case PowerState::IDLE:
            limit = 50000;  /* 50W */
            break;
        case PowerState::PARK:
            limit = 20000;  /* 20W */
            break;
        }
        
        if (limit > 0) {
            NvmlLoader::instance().deviceSetPowerManagementLimit(gpu->nvmlDevice(), limit);
        }
    }
}

bool PowerManager::readPowerDraw(uint32_t gpuIndex, uint32_t* milliwatts)
{
    const auto* gpu = m_deviceRegistry->getGpu(gpuIndex);
    if (!gpu) {
        return false;
    }
    
    if (gpu->vendor() == VendorId::NVIDIA && gpu->nvmlDevice()) {
        unsigned int power;
        if (NvmlLoader::instance().deviceGetPowerUsage(gpu->nvmlDevice(), &power) == NVML_SUCCESS) {
            *milliwatts = power;
            return true;
        }
    }

    /* Try to read power from sysfs */
    *milliwatts = 100 * 1000; /* 100W in mW default fallback */
    return true;
}

bool PowerManager::readTemperature(uint32_t gpuIndex, int32_t* temperatureCelsius)
{
    const auto* gpu = m_deviceRegistry->getGpu(gpuIndex);
    if (!gpu) {
        return false;
    }
    
    /* Try to read temperature from sysfs
     * Example path: /sys/class/drm/cardX/device/hwmon/<hwmonN>/temp1_input
     * The file contains temperature in millidegrees Celsius.
     */
    
    /* For now, use the GPU's spot field if available */
    // *temperatureCelsius = gpu->state().temperature;
    
    /* Simplified: return a default temperature */
    *temperatureCelsius = 50; /* 50 C */
    return true;
}

bool PowerManager::writeFrequency(uint32_t gpuIndex, uint32_t /*mhz*/)
{
    const auto* gpu = m_deviceRegistry->getGpu(gpuIndex);
    if (!gpu) {
        return false;
    }
    
    /* In a full implementation:
     * 1. For AMD: modify /sys/class/drm/cardX/device/pp_od_clk_voltage
     * 2. For NVIDIA: Use nvidia-smi or NVML
     * 3. For Intel: Use /sys/class/drm/cardX/gt/gt0/rps_boost_freq_mhz
     */
    
    return true; /* Assume success for now */
}

/* ======================== Fan Curve Methods ======================== */

void PowerManager::setFanCurve(uint32_t gpuIndex, const GpuFanCurve& curve)
{
    std::lock_guard<std::mutex> lock(m_mutex);
    
    if (gpuIndex >= m_fanCurves.size()) {
        return;
    }
    
    m_fanCurves[gpuIndex] = curve;
}

GpuFanCurve PowerManager::getFanCurve(uint32_t gpuIndex) const
{
    std::lock_guard<std::mutex> lock(m_mutex);
    
    if (gpuIndex >= m_fanCurves.size()) {
        GpuFanCurve empty = {{}, 0};
        return empty;
    }
    
    return m_fanCurves[gpuIndex];
}

void PowerManager::setFanSpeed(uint32_t gpuIndex, uint8_t speedPercent)
{
    std::lock_guard<std::mutex> lock(m_mutex);
    
    if (gpuIndex >= m_gpuPowerInfo.size()) {
        return;
    }
    
    auto& fan = m_gpuPowerInfo[gpuIndex].fanControl;
    fan.automatic = false;
    fan.manualSpeedPercent = std::min<uint8_t>(speedPercent, 100);
    
    writeFanSpeed(gpuIndex, fan.manualSpeedPercent);
    fan.currentSpeedPercent = fan.manualSpeedPercent;
}

void PowerManager::setFanAutomatic(uint32_t gpuIndex, bool automatic)
{
    std::lock_guard<std::mutex> lock(m_mutex);
    
    if (gpuIndex >= m_gpuPowerInfo.size()) {
        return;
    }
    
    m_gpuPowerInfo[gpuIndex].fanControl.automatic = automatic;
}

void PowerManager::applyFanControl()
{
    for (uint32_t i = 0; i < m_gpuPowerInfo.size(); i++) {
        auto& info = m_gpuPowerInfo[i];
        auto& fan = info.fanControl;
        
        if (!fan.enabled) {
            continue;
        }
        
        uint8_t targetSpeed = 0;
        
        if (fan.automatic && i < m_fanCurves.size() && m_fanCurves[i].numPoints > 0) {
            /* Use interpolated fan curve based on temperature */
            targetSpeed = interpolateFanSpeed(m_fanCurves[i], info.lastTemperature);
        } else if (!fan.automatic) {
            /* Manual override */
            targetSpeed = fan.manualSpeedPercent;
        }
        
        /* Only write if changed significantly (hysteresis: >5% difference) */
        uint8_t current = fan.currentSpeedPercent;
        if (targetSpeed > current + 5 || targetSpeed < current - 5) {
            if (writeFanSpeed(i, targetSpeed)) {
                fan.currentSpeedPercent = targetSpeed;
            }
        }
        
        /* Read back actual fan RPM */
        uint32_t rpm = 0;
        if (readFanSpeed(i, &rpm)) {
            fan.currentRpm = rpm;
        }
    }
}

uint8_t PowerManager::interpolateFanSpeed(const GpuFanCurve& curve, int32_t temperature) const
{
    if (curve.numPoints == 0) {
        return 0;
    }
    
    /* Below first point — use first point value */
    if (temperature <= curve.points[0].temperatureCelsius) {
        return curve.points[0].fanSpeedPercent;
    }
    
    /* Above last point — use last point value */
    if (temperature >= curve.points[curve.numPoints - 1].temperatureCelsius) {
        return curve.points[curve.numPoints - 1].fanSpeedPercent;
    }
    
    /* Linear interpolation between surrounding points */
    for (uint32_t i = 0; i < curve.numPoints - 1; i++) {
        int32_t tLow = curve.points[i].temperatureCelsius;
        int32_t tHigh = curve.points[i + 1].temperatureCelsius;
        
        if (temperature >= tLow && temperature < tHigh) {
            uint8_t sLow = curve.points[i].fanSpeedPercent;
            uint8_t sHigh = curve.points[i + 1].fanSpeedPercent;
            
            if (tHigh == tLow) {
                return sLow;
            }
            
            float t = static_cast<float>(temperature - tLow) / static_cast<float>(tHigh - tLow);
            uint8_t result = static_cast<uint8_t>(sLow + t * (sHigh - sLow));
            return std::min<uint8_t>(result, 100);
        }
    }
    
    return 0;
}

bool PowerManager::readFanSpeed(uint32_t gpuIndex, uint32_t* rpm)
{
    (void)gpuIndex;
    (void)rpm;
    
    /* In a full implementation:
     * 1. For AMD: read /sys/class/drm/cardX/device/hwmon/<hwmonN>/fan1_input
     * 2. For NVIDIA: Use NVML nvmlDeviceGetFanSpeed()
     * 3. For Intel: Read /sys/class/drm/cardX/gt/gt0/rps_cur_freq
     */
    
    /* Simplified: return 0 RPM for now */
    *rpm = 0;
    return true;
}

bool PowerManager::writeFanSpeed(uint32_t gpuIndex, uint8_t speedPercent)
{
    (void)gpuIndex;
    (void)speedPercent;
    
    /* In a full implementation:
     * 1. For AMD: write to /sys/class/drm/cardX/device/hwmon/<hwmonN>/pwm1
     * 2. For NVIDIA: Use NVML nvmlDeviceSetFanSpeed()
     * 3. For Intel: Use /sys/class/drm/cardX/gt/gt0/rps_boost_freq_mhz
     */
    
    return true; /* Assume success for now */
}

/* ======================== PSU Headroom Methods ======================== */

void PowerManager::setPsuConfig(const PsuConfig& config)
{
    std::lock_guard<std::mutex> lock(m_mutex);
    m_psuConfig = config;
}

PsuHeadroom PowerManager::getPsuHeadroom() const
{
    std::lock_guard<std::mutex> lock(m_mutex);
    
    PsuHeadroom headroom = {};
    headroom.totalCapacityWatts = m_psuConfig.totalCapacityWatts;
    headroom.safetyMarginPercent = static_cast<float>(m_psuConfig.safetyMarginPercent);
    
    /* Sum up current draw from all GPUs */
    uint32_t totalDraw = 0;
    for (size_t i = 0; i < m_gpuPowerInfo.size(); i++) {
        totalDraw += m_gpuPowerInfo[i].stats.powerDrawMilliwatts / 1000;
    }
    headroom.currentDrawWatts = totalDraw;
    
    if (m_psuConfig.totalCapacityWatts > 0) {
        if (totalDraw >= m_psuConfig.totalCapacityWatts) {
            headroom.headroomWatts = 0;
        } else {
            headroom.headroomWatts = m_psuConfig.totalCapacityWatts - totalDraw;
        }
    } else {
        headroom.headroomWatts = 0;
    }
    
    return headroom;
}

void PowerManager::setPowerCurve(uint32_t gpuIndex, const GpuPowerCurve& curve)
{
    std::lock_guard<std::mutex> lock(m_mutex);
    if (gpuIndex < m_powerCurves.size()) {
        m_powerCurves[gpuIndex] = curve;
    }
}

GpuPowerCurve PowerManager::getPowerCurve(uint32_t gpuIndex) const
{
    std::lock_guard<std::mutex> lock(m_mutex);
    if (gpuIndex < m_powerCurves.size()) {
        return m_powerCurves[gpuIndex];
    }
    return {};
}

uint8_t PowerManager::interpolatePowerLimit(const GpuPowerCurve& curve,
                                            uint8_t utilization) const
{
    if (curve.numPoints == 0) {
        return 100;
    }
    if (utilization <= curve.points[0].utilizationPercent) {
        return curve.points[0].powerLimitPercent;
    }
    for (uint32_t i = 1; i < curve.numPoints; i++) {
        const auto& lo = curve.points[i - 1];
        const auto& hi = curve.points[i];
        if (utilization <= hi.utilizationPercent) {
            const int span = hi.utilizationPercent - lo.utilizationPercent;
            if (span <= 0) {
                return hi.powerLimitPercent;
            }
            const int t = utilization - lo.utilizationPercent;
            return static_cast<uint8_t>(
                lo.powerLimitPercent +
                (hi.powerLimitPercent - lo.powerLimitPercent) * t / span);
        }
    }
    return curve.points[curve.numPoints - 1].powerLimitPercent;
}

void PowerManager::applyPowerCurves()
{
    if (!m_config.enablePowerCapping && m_psuConfig.totalCapacityWatts == 0) {
        return;
    }

    for (uint32_t i = 0; i < m_powerCurves.size(); i++) {
        const auto* gpu = m_deviceRegistry->getGpu(i);
        if (!gpu || i >= m_dvfsState.size()) {
            continue;
        }

        const uint8_t util = static_cast<uint8_t>(std::min<uint32_t>(100, gpu->utilization()));
        uint8_t limitPct = interpolatePowerLimit(m_powerCurves[i], util);

        if (m_config.powerLimitWatts > 0) {
            const uint32_t tdp = m_config.powerLimitWatts;
            const uint32_t capMw = tdp * 1000U * limitPct / 100U;
            if (m_gpuPowerInfo[i].stats.powerDrawMilliwatts > capMw && capMw > 0) {
                const uint32_t maxClock = m_dvfsState[i].currentClockMhz > 0
                    ? m_dvfsState[i].currentClockMhz
                    : 2000U;
                const uint32_t reduced = std::max<uint32_t>(500U, maxClock * 9U / 10U);
                m_dvfsState[i].targetClockMhz = reduced;
                writeFrequency(i, reduced);
            }
        }
    }
}

void PowerManager::updatePsuHeadroom()
{
    if (!m_psuConfig.enableHeadroomTracking || m_psuConfig.totalCapacityWatts == 0) {
        return;
    }
    
    PsuHeadroom hr = getPsuHeadroom();
    uint32_t safetyMargin = m_psuConfig.totalCapacityWatts * m_psuConfig.safetyMarginPercent / 100;
    
    /* If we're exceeding the safety margin, reduce power limits */
    if (hr.currentDrawWatts > m_psuConfig.totalCapacityWatts - safetyMargin) {
        /* Apply proportional power limiting */
        for (size_t i = 0; i < m_gpuPowerInfo.size(); i++) {
            if (!m_gpuPowerInfo[i].enabled) {
                continue;
            }
            
            /* Reduce DVFS target to lower power consumption */
            if (m_config.enableDvfs && i < m_dvfsState.size()) {
                uint32_t reduction = static_cast<uint32_t>(
                    (hr.currentDrawWatts - (m_psuConfig.totalCapacityWatts - safetyMargin)) * 10
                );
                if (m_dvfsState[i].targetClockMhz > reduction) {
                    m_dvfsState[i].targetClockMhz -= reduction;
                    writeFrequency(static_cast<uint32_t>(i), m_dvfsState[i].targetClockMhz);
                }
            }
        }
    }
}

} // namespace mvgal
