#include "fgc/HostMetrics.h"

#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <string>

#include <sys/statvfs.h>
#include <unistd.h>

namespace fgc::hostmetrics {

namespace fs = std::filesystem;

namespace {

std::string readFile(const fs::path& p) {
    std::ifstream f(p);
    if (!f) return {};
    std::ostringstream ss;
    ss << f.rdbuf();
    return ss.str();
}

}  // namespace

double parseMilliCelsius(const std::string& s) {
    try {
        return std::stod(s) / 1000.0;   // sysfs reports milli-degrees C
    } catch (...) {
        return 0.0;
    }
}

MemInfo parseMeminfo(const std::string& text) {
    MemInfo m;
    std::istringstream in(text);
    std::string line;
    double mem_total_kb = 0, mem_avail_kb = 0, swap_total_kb = 0, swap_free_kb = 0;
    bool have_total = false, have_avail = false;
    while (std::getline(in, line)) {
        std::istringstream ls(line);
        std::string key, val, unit;
        if (!(ls >> key >> val)) continue;
        double kb = 0;
        try { kb = std::stod(val); } catch (...) { continue; }
        if (key == "MemTotal:")         { mem_total_kb = kb; have_total = true; }
        else if (key == "MemAvailable:") { mem_avail_kb = kb; have_avail = true; }
        else if (key == "SwapTotal:")    swap_total_kb = kb;
        else if (key == "SwapFree:")     swap_free_kb = kb;
    }
    m.ok = have_total && have_avail;
    m.total_mb = mem_total_kb / 1024.0;
    m.avail_mb = mem_avail_kb / 1024.0;
    m.swap_used_mb = (swap_total_kb - swap_free_kb) / 1024.0;
    return m;
}

ThermalInfo readThermal() {
    ThermalInfo t;
    const fs::path root = "/sys/class/thermal";
    std::error_code ec;
    if (!fs::exists(root, ec)) return t;
    for (auto& e : fs::directory_iterator(root, ec)) {
        const auto name = e.path().filename().string();
        if (name.rfind("thermal_zone", 0) != 0) continue;
        std::string temp_s = readFile(e.path() / "temp");
        if (temp_s.empty()) continue;
        double c = parseMilliCelsius(temp_s);
        if (!t.ok || c > t.max_temp_c) {
            t.ok = true;
            t.max_temp_c = c;
            std::string type = readFile(e.path() / "type");
            // strip trailing newline
            while (!type.empty() && (type.back() == '\n' || type.back() == '\r')) type.pop_back();
            t.hottest_zone = type.empty() ? name : type;
        }
    }
    return t;
}

DiskInfo readDisk(const std::string& path) {
    DiskInfo d;
    d.path = path;
    struct statvfs vfs {};
    if (statvfs(path.c_str(), &vfs) != 0) return d;
    const double frsize = static_cast<double>(vfs.f_frsize);
    const double total = static_cast<double>(vfs.f_blocks) * frsize;
    const double avail = static_cast<double>(vfs.f_bavail) * frsize;
    const double gb = 1024.0 * 1024.0 * 1024.0;
    d.ok = true;
    d.total_gb = total / gb;
    d.free_gb = avail / gb;
    d.used_pct = total > 0 ? 100.0 * (total - avail) / total : 0.0;
    return d;
}

MemInfo readMeminfo() {
    return parseMeminfo(readFile("/proc/meminfo"));
}

LoadInfo readLoad() {
    LoadInfo l;
    double avg[3] = {0, 0, 0};
    if (getloadavg(avg, 3) == 3) {
        l.ok = true;
        l.load1 = avg[0];
        l.load5 = avg[1];
        l.load15 = avg[2];
    }
    long n = sysconf(_SC_NPROCESSORS_ONLN);
    l.cpus = (n > 0) ? static_cast<int>(n) : 0;
    return l;
}

}  // namespace fgc::hostmetrics
