import { contentLoader } from './ContentLoader.js';
import { runVirsh } from '../lib/virsh.js';

const DEFAULT_VM_PREFIX = process.env.VM_PREFIX ?? 'sc-';
const IP_CACHE_TTL_MS = 60_000;

export function extractIpv4Address(text) {
  const match = String(text ?? '').match(/\b(\d{1,3}(?:\.\d{1,3}){3})(?:\/\d+)?\b/);
  return match ? match[1] : '';
}

export function extractMacAddress(domainXml) {
  const match = String(domainXml ?? '').match(/<mac address=['"]([^'"]+)['"]/i);
  return match ? match[1].toLowerCase() : '';
}

export function findLeaseIpByMac(leasesOutput, macAddress) {
  if (!leasesOutput || !macAddress) {
    return '';
  }

  const normalizedMac = macAddress.toLowerCase();
  for (const line of String(leasesOutput).split('\n')) {
    if (!line.toLowerCase().includes(normalizedMac)) {
      continue;
    }

    const ip = extractIpv4Address(line);
    if (ip) {
      return ip;
    }
  }

  return '';
}

export class VMManager {
  constructor({ virshRunner = runVirsh, loader = contentLoader } = {}) {
    this.virshRunner = virshRunner;
    this.loader = loader;
    this.ipCache = new Map();
  }

  getProfile(vmId) {
    return this.loader.get('vmProfiles', vmId) ?? {};
  }

  getDomainName(vmId) {
    const profile = this.getProfile(vmId);
    return profile.domain ?? `${DEFAULT_VM_PREFIX}${vmId}`;
  }

  getHostname(vmId) {
    return this.getProfile(vmId).hostname ?? vmId;
  }

  getManagementUser(vmId) {
    return this.getProfile(vmId).management_user ?? 'opsbridge';
  }

  getSshKeyPath(vmId) {
    return this.getProfile(vmId).ssh_key ?? process.env.SSH_KEY_PATH ?? '~/.ssh/sc_host_key';
  }

  getDistro(vmId) {
    return this.getProfile(vmId).distro ?? '';
  }

  async domainExists(vmId) {
    const result = await this.virshRunner(['dominfo', this.getDomainName(vmId)]);
    return result.ok;
  }

  async getState(vmId) {
    const result = await this.virshRunner(['domstate', this.getDomainName(vmId)]);
    return result.ok ? result.stdout.trim() : 'missing';
  }

  async start(vmId) {
    return await this.virshRunner(['start', this.getDomainName(vmId)], { timeoutSec: 20 });
  }

  async ensureWorkstationLive() {
    const vmId = 'workstation';
    if (!await this.domainExists(vmId)) {
      return { ok: false, reason: 'missing_domain' };
    }

    const state = await this.getState(vmId);
    if (state === 'running') {
      return { ok: true, started: false };
    }

    const result = await this.start(vmId);
    return { ok: result.ok, started: result.ok, state_before: state };
  }

  async getIP(vmId, { refresh = false } = {}) {
    const cacheKey = this.getDomainName(vmId);
    if (!refresh) {
      const cached = this.ipCache.get(cacheKey);
      if (cached && cached.expiresAt > Date.now()) {
        return cached.ip;
      }
    }

    if (!await this.domainExists(vmId)) {
      return '';
    }

    const domainName = this.getDomainName(vmId);
    const ipFromAgent = await this._ipFromDomifaddr(domainName);
    if (ipFromAgent) {
      this._cacheIp(cacheKey, ipFromAgent);
      return ipFromAgent;
    }

    const ipFromDhcp = await this._ipFromDhcp(vmId, domainName);
    if (ipFromDhcp) {
      this._cacheIp(cacheKey, ipFromDhcp);
      return ipFromDhcp;
    }

    return '';
  }

  _cacheIp(cacheKey, ip) {
    this.ipCache.set(cacheKey, {
      ip,
      expiresAt: Date.now() + IP_CACHE_TTL_MS
    });
  }

  async _ipFromDomifaddr(domainName) {
    const result = await this.virshRunner(['domifaddr', domainName, '--source', 'agent']);
    if (!result.ok) {
      return '';
    }

    return extractIpv4Address(result.stdout);
  }

  async _ipFromDhcp(vmId, domainName) {
    const profile = this.getProfile(vmId);
    const networkName = profile.network?.libvirt_network ?? 'sc-internal';
    const xmlResult = await this.virshRunner(['dumpxml', domainName]);
    if (!xmlResult.ok) {
      return '';
    }

    const macAddress = extractMacAddress(xmlResult.stdout);
    if (!macAddress) {
      return '';
    }

    const leasesResult = await this.virshRunner(['net-dhcp-leases', networkName]);
    if (!leasesResult.ok) {
      return '';
    }

    return findLeaseIpByMac(leasesResult.stdout, macAddress);
  }
}

export const vmManager = new VMManager();
