// src/services/host-monitor.ts
// Copyright (C) 2025 DTP Technologies, LLC
// All Rights Reserved

import env from "../config/env.js";

import os from "node:os";
import v8 from "node:v8";
import { EventEmitter } from "node:events";

import { CronJob } from "cron";

import HostMonitor, { IHostMonitor } from "@/models/host-monitor.js";
import { DtpService } from "../lib/service.js";

export interface IHostMonitorStats {
  memoryUtilization: number;
  rss: number;
  heapTotal: number;
  heapUsed: number;
  heapExternal: number;
  osTotal: number;
  osFree: number;
  timestamp: Date;
}

class HostMonitorService extends DtpService {
  private cronJob: CronJob | undefined;
  private stats: IHostMonitor;
  private eventEmitter: EventEmitter;

  get name(): string {
    return "HostMonitorService";
  }
  get slug(): string {
    return "search";
  }

  constructor() {
    super();
    this.stats = this.createMonitor();
    this.stats.hostname = os.hostname();
    this.eventEmitter = new EventEmitter();
  }

  async start(): Promise<void> {
    const { heap_size_limit } = v8.getHeapStatistics();
    const limitMB = (heap_size_limit / 1024 / 1024).toFixed(2);

    this.log.info("starting host monitor cron job");
    this.cronJob = new CronJob(
      "*/15 * * * * *",
      this.onStoreStats.bind(this),
      null,
      true,
      env.timezone,
    );

    this.log.info("service started", { heapLimit: limitMB });
  }

  async stop(): Promise<void> {
    if (this.cronJob) {
      this.log.info("stopping host monitor cron job");
      this.cronJob.stop();
      delete this.cronJob;
    }

    this.log.info("service stopped");
  }

  subagent(bytes: number): void {
    this.stats.memory.ai.subagents.count += 1;
    this.stats.memory.ai.subagents.bytes += bytes;
  }

  fileOperation(bytes: number): void {
    this.stats.memory.ai.fileOperations.count += 1;
    this.stats.memory.ai.fileOperations.bytes += bytes;
  }

  toolCall(bytes: number): void {
    this.stats.memory.ai.toolCalls.count += 1;
    this.stats.memory.ai.toolCalls.bytes += bytes;
  }

  on(event: string, listener: (...args: any[]) => void): void {
    this.eventEmitter.on(event, listener);
  }

  off(event: string, listener: (...args: any[]) => void): void {
    this.eventEmitter.off(event, listener);
  }

  async onStoreStats(): Promise<void> {
    const NOW = new Date();
    this.stats.timestamp = NOW;

    const usage: NodeJS.MemoryUsage = process.memoryUsage();
    this.stats.memory.rss = usage.rss;
    this.stats.memory.v8.heapTotal = usage.heapTotal;
    this.stats.memory.v8.heapUsed = usage.heapUsed;
    this.stats.memory.v8.heapExternal = usage.external ?? 0;

    const osTotal = os.totalmem();
    const osFree = os.freemem();
    this.stats.memory.os.total = osTotal;
    this.stats.memory.os.free = osFree;

    // store stats to db
    await this.stats.save();

    // Calculate memory utilization percentage
    const memoryUsed = osTotal - osFree;
    const memoryUtilization = Math.round((memoryUsed / osTotal) * 100);

    // Emit stats event for UI updates
    const statsData: IHostMonitorStats = {
      memoryUtilization,
      rss: usage.rss,
      heapTotal: usage.heapTotal,
      heapUsed: usage.heapUsed,
      heapExternal: usage.external ?? 0,
      osTotal,
      osFree,
      timestamp: NOW,
    };
    this.eventEmitter.emit("stats", statsData);

    this.stats = this.createMonitor();
  }

  createMonitor(): IHostMonitor {
    const monitor = new HostMonitor();
    monitor.hostname = os.hostname();

    const usage = process.memoryUsage();
    monitor.memory = {
      rss: usage.rss,
      os: {
        total: os.totalmem(),
        free: os.freemem(),
      },
      v8: {
        heapTotal: usage.heapTotal,
        heapUsed: usage.heapUsed,
        heapExternal: usage.external ?? 0,
      },
      logs: {
        count: 0,
        bytes: 0,
      },
      ai: {
        fileOperations: {
          count: 0,
          bytes: 0,
        },
        subagents: {
          count: 0,
          bytes: 0,
        },
        toolCalls: {
          count: 0,
          bytes: 0,
        },
      },
    };

    return monitor;
  }
}

export default new HostMonitorService();
