'use strict';

const { execFile } = require('child_process');
const { promisify } = require('util');
const execFileAsync = promisify(execFile);

const APPCMD = `${process.env.SystemRoot || 'C:\\Windows'}\\system32\\inetsrv\\appcmd.exe`;

/**
 * Run appcmd.exe with the given arguments.
 * Returns { ok, stdout, stderr }.
 */
async function appcmd(args, timeoutMs = 30000) {
  try {
    const { stdout, stderr } = await execFileAsync(APPCMD, args, { timeout: timeoutMs });
    return { ok: true, stdout: (stdout || '').trim(), stderr: (stderr || '').trim() };
  } catch (err) {
    return { ok: false, stdout: '', stderr: err.message };
  }
}

//  Sites 

async function getSites() {
  const { ok, stdout, stderr } = await appcmd(['list', 'site', '/xml']);
  if (!ok) return { ok: false, data: [], error: stderr };
  try {
    // Parse appcmd XML: <appcmd><SITE SITE.NAME="..." state="..." ...><bindings>...</bindings></SITE></appcmd>
    const sites = [];
    const siteRegex = /<SITE\s([^>]*)>/g;
    let m;
    while ((m = siteRegex.exec(stdout)) !== null) {
      const attrs = m[1];
      const get = (k) => { const r = new RegExp(`${k}="([^"]*)"`); const x = r.exec(attrs); return x ? x[1] : ''; };
      sites.push({
        Name:         get('SITE.NAME'),
        ID:           get('SITE.ID'),
        State:        get('state'),
        PhysicalPath: '',   // populated below if needed
        Bindings:     [get('bindings')],
      });
    }
    // Fetch physical paths individually
    await Promise.all(sites.map(async (s) => {
      const r = await appcmd(['list', 'vdir', `/${s.Name}/`]);
      if (r.ok) {
        const pm = /physicalPath:"([^"]*)"/.exec(r.stdout);
        if (pm) s.PhysicalPath = pm[1];
      }
    }));
    return { ok: true, data: sites };
  } catch (e) {
    return { ok: false, data: [], error: e.message };
  }
}

async function controlSite(name, action) {
  // action: Start | Stop
  const cmd = action.toLowerCase() === 'start' ? 'start' : 'stop';
  const result = await appcmd([cmd, 'site', `/site.name:${name}`]);
  if (!result.ok) return result;
  // Get new state
  const info = await appcmd(['list', 'site', `/name:${name}`]);
  const sm = /state:([A-Za-z]+)/.exec(info.stdout);
  return { ...result, stdout: sm ? sm[1] : '' };
}

//  Application Pools 

async function getAppPools() {
  const { ok, stdout, stderr } = await appcmd(['list', 'apppool', '/xml']);
  if (!ok) return { ok: false, data: [], error: stderr };
  try {
    const pools = [];
    const poolRegex = /<APPPOOL\s([^>]*)>/g;
    let m;
    while ((m = poolRegex.exec(stdout)) !== null) {
      const attrs = m[1];
      const get = (k) => { const r = new RegExp(`${k}="([^"]*)"`); const x = r.exec(attrs); return x ? x[1] : ''; };
      pools.push({
        Name:                   get('APPPOOL.NAME'),
        State:                  get('state'),
        ManagedRuntimeVersion:  get('managedRuntimeVersion'),
        ManagedPipelineMode:    get('managedPipelineMode'),
      });
    }
    return { ok: true, data: pools };
  } catch (e) {
    return { ok: false, data: [], error: e.message };
  }
}

async function controlPool(name, action) {
  // action: Start | Stop | Recycle
  const cmd = action.toLowerCase() === 'recycle' ? 'recycle' : action.toLowerCase();
  const result = await appcmd([cmd, 'apppool', `/apppool.name:${name}`]);
  if (!result.ok) return result;
  const info = await appcmd(['list', 'apppool', `/name:${name}`]);
  const sm = /state:([A-Za-z]+)/.exec(info.stdout);
  return { ...result, stdout: sm ? sm[1] : '' };
}

//  IIS Reset 

async function iisReset() {
  try {
    const { stdout, stderr } = await execFileAsync(
      `${process.env.SystemRoot || 'C:\\Windows'}\\system32\\iisreset.exe`,
      ['/restart', '/timeout:60'],
      { timeout: 90000 }
    );
    return { ok: true, stdout: (stdout || '').trim(), stderr: (stderr || '').trim() };
  } catch (err) {
    return { ok: false, stdout: '', stderr: err.message };
  }
}

//  Helpers 

async function getSitePhysicalPath(iisName) {
  const r = await appcmd(['list', 'vdir', `/${iisName}/`]);
  if (!r.ok) return null;
  const pm = /physicalPath:"([^"]*)"/.exec(r.stdout);
  return pm ? pm[1] : null;
}

async function getPoolForSite(iisName) {
  const r = await appcmd(['list', 'app', `/${iisName}/`, '/text:applicationPool']);
  return r.ok ? r.stdout.trim() : null;
}

module.exports = { getSites, controlSite, getAppPools, controlPool, iisReset, getSitePhysicalPath, getPoolForSite };