import { query } from '../../db';
import { alerter } from '../../services/alerter';
import { exec } from 'child_process';
import { promisify } from 'util';
import { readdirSync, statSync, unlinkSync } from 'fs';
import { listPm2Processes, shellQuote, summarizePm2Apps, summarizePm2Error } from '../../lib/pm2';

const execAsync = promisify(exec);

// Expected lock file max age in minutes (2x the normal cron interval)
const LOCK_MAX_AGE_MINUTES = 120;

async function checkStaleLocks(): Promise<void> {
  try {
    const files = readdirSync('/tmp').filter(f => f.startsWith('cron-') && f.endsWith('.lock'));
    const now = Date.now();

    for (const file of files) {
      const fullPath = `/tmp/${file}`;
      try {
        const stat = statSync(fullPath);
        const ageMinutes = (now - stat.mtimeMs) / 60000;

        if (ageMinutes > LOCK_MAX_AGE_MINUTES) {
          // Check if PID is alive
          let pidAlive = false;
          try {
            const { stdout } = await execAsync(`fuser ${fullPath} 2>/dev/null`);
            pidAlive = stdout.trim().length > 0;
          } catch {
            pidAlive = false;
          }

          if (!pidAlive) {
            unlinkSync(fullPath);
            await alerter.logEvent(
              'stale_lock_cleared', 'info', 'watchdog',
              `Cleared stale lock: ${file} (age: ${Math.round(ageMinutes)}m)`,
              { file, age_minutes: Math.round(ageMinutes) }
            );
          }
        }
      } catch {
        // Skip unreadable files
      }
    }
  } catch {
    // /tmp not readable
  }
}

async function checkPM2Processes(): Promise<void> {
  try {
    const apps = summarizePm2Apps(await listPm2Processes());

    for (const app of apps) {
      if (app.onlineCount > 0 || (app.status !== 'errored' && app.status !== 'stopped')) {
        continue;
      }

      try {
        await execAsync(`pm2 restart ${shellQuote(app.name)}`);
        await alerter.logEvent(
          'pm2_restart', 'warn', 'watchdog',
          `Restarted crashed process: ${app.name} (was: ${app.status})`,
          {
            name: app.name,
            namespace: app.namespace,
            previous_status: app.status,
            pm_ids: app.pmIds,
            unhealthy_count: app.unhealthyCount,
          }
        );
      } catch (err) {
        await alerter.logEvent(
          'pm2_restart_failed', 'critical', 'watchdog',
          `Failed to restart process: ${app.name}`,
          {
            name: app.name,
            namespace: app.namespace,
            previous_status: app.status,
            pm_ids: app.pmIds,
            error: summarizePm2Error(err),
          }
        );
      }
    }
  } catch (err) {
    console.error('[watchdog] PM2 check failed:', summarizePm2Error(err));
  }
}

async function checkDataFreshness(): Promise<void> {
  const checks = [
    { table: '"PlayerPropLine"', col: '"updatedAt"', source: 'PlayerPropLine', maxAgeMin: 120 },
    { table: '"SportsGame"', col: '"updatedAt"', source: 'SportsGame', maxAgeMin: 120 },
  ];

  for (const check of checks) {
    try {
      const result = await query(`SELECT MAX(${check.col}) as latest FROM ${check.table}`);
      const latest = result.rows[0]?.latest;
      if (!latest) continue;

      const ageMin = (Date.now() - new Date(latest).getTime()) / 60000;

      if (ageMin > check.maxAgeMin * 2) {
        await alerter.logEvent(
          'data_stale', 'warn', 'watchdog',
          `${check.source} data is stale: ${Math.round(ageMin)}min old (threshold: ${check.maxAgeMin}min)`,
          { source: check.source, age_minutes: Math.round(ageMin), threshold: check.maxAgeMin }
        );
      }
    } catch {
      // Table might not exist or query issue
    }
  }
}

export async function runWatchdog(): Promise<void> {
  await checkStaleLocks();
  await checkPM2Processes();
  await checkDataFreshness();
}
