import { query } from '../db';
import { execSync } from 'child_process';
import { readdirSync, statSync } from 'fs';
import { listPm2Processes, summarizePm2Apps } from '../lib/pm2';

interface ProcessInfo {
  name: string;
  pm_id: number;
  status: string;
  memory: number;
  cpu: number;
  restarts: number;
}

interface FreshnessCheck {
  source: string;
  latest: Date | null;
  ageMinutes: number | null;
  maxAgeMinutes: number;
  status: 'fresh' | 'stale' | 'critical';
}

interface LockFileInfo {
  path: string;
  ageMinutes: number;
  pidAlive: boolean;
}

export class SystemMonitor {
  async getProcesses(): Promise<ProcessInfo[]> {
    try {
      const procs = await listPm2Processes();
      return procs.map(p => ({
        name: p.name,
        pm_id: p.pm_id,
        status: p.status,
        memory: p.memory,
        cpu: p.cpu,
        restarts: p.restarts,
      }));
    } catch {
      return [];
    }
  }

  async getDataFreshness(): Promise<FreshnessCheck[]> {
    const now = new Date();
    const checks = [
      { table: '"PlayerPropLine"', col: '"updatedAt"', source: 'PlayerPropLine', maxAge: 120 },
      { table: '"PlayerInjury"', col: '"updatedAt"', source: 'PlayerInjury', maxAge: 360 },
      { table: '"SportsGame"', col: '"updatedAt"', source: 'SportsGame', maxAge: 120 },
      { table: 'sc_blog_posts', col: 'published_at', source: 'BlogPosts', maxAge: 1440 },
    ];

    const results: FreshnessCheck[] = [];
    for (const check of checks) {
      try {
        const r = await query(`SELECT MAX(${check.col}) as latest FROM ${check.table}`);
        const latest = r.rows[0]?.latest ? new Date(r.rows[0].latest) : null;
        const ageMs = latest ? now.getTime() - latest.getTime() : Infinity;
        const ageMinutes = latest ? Math.round(ageMs / 60000) : null;
        results.push({
          source: check.source,
          latest,
          ageMinutes,
          maxAgeMinutes: check.maxAge,
          status: ageMs <= check.maxAge * 60000 ? 'fresh'
            : ageMs <= check.maxAge * 120000 ? 'stale' : 'critical',
        });
      } catch {
        results.push({
          source: check.source,
          latest: null,
          ageMinutes: null,
          maxAgeMinutes: check.maxAge,
          status: 'critical',
        });
      }
    }
    return results;
  }

  getLockFiles(): LockFileInfo[] {
    const locks: LockFileInfo[] = [];
    try {
      const files = readdirSync('/tmp').filter(f => f.startsWith('cron-') && f.endsWith('.lock'));
      const now = Date.now();
      for (const file of files) {
        try {
          const stat = statSync(`/tmp/${file}`);
          const ageMinutes = Math.round((now - stat.mtimeMs) / 60000);
          // Check if PID in lock file is alive
          let pidAlive = false;
          try {
            const stdout = execSync(`fuser /tmp/${file} 2>/dev/null`, { encoding: 'utf8' });
            pidAlive = stdout.trim().length > 0;
          } catch {
            pidAlive = false;
          }
          locks.push({ path: `/tmp/${file}`, ageMinutes, pidAlive });
        } catch {
          // skip unreadable files
        }
      }
    } catch {
      // /tmp not readable
    }
    return locks;
  }

  async computeHealthScores(): Promise<{
    infra: number;
    dataIntegrity: number;
  }> {
    // Infrastructure health should be scored by app, not by every stale PM2 slot.
    const appSummaries = summarizePm2Apps(await listPm2Processes());
    const totalApps = appSummaries.length || 1;
    const onlineApps = appSummaries.filter(app => app.onlineCount > 0).length;
    const unstableApps = appSummaries.filter(app => app.isRecentlyUnstable).length;
    let infraScore = (onlineApps / totalApps) * 100;
    infraScore -= Math.min(15, unstableApps * 3); // cap instability penalty at 15

    // Data integrity: freshness
    const freshness = await this.getDataFreshness();
    const totalChecks = freshness.length || 1;
    const freshCount = freshness.filter(f => f.status === 'fresh').length;
    const criticalCount = freshness.filter(f => f.status === 'critical').length;
    let dataScore = (freshCount / totalChecks) * 100;
    dataScore -= criticalCount * 15; // heavy penalty for critical staleness

    // Lock files penalty
    const locks = this.getLockFiles();
    const staleLocks = locks.filter(l => l.ageMinutes > 120 && !l.pidAlive);
    infraScore -= Math.min(20, staleLocks.length * 3); // cap lock penalty at 20

    return {
      infra: Math.max(0, Math.min(100, Math.round(infraScore * 100) / 100)),
      dataIntegrity: Math.max(0, Math.min(100, Math.round(dataScore * 100) / 100)),
    };
  }

  async updateGlobalState(): Promise<void> {
    const scores = await this.computeHealthScores();
    await query(
      `UPDATE sc_global_state
       SET infra_health_score = $1,
           data_integrity_score = $2,
           last_updated = NOW(),
           updated_by = 'system-monitor'
       WHERE id = 'primary'`,
      [scores.infra, scores.dataIntegrity]
    );
  }
}

export const systemMonitor = new SystemMonitor();
