import { sendPushoverMessage } from './pushover.js';

const LEVELS = {
  none: 0,
  warn: 1,
  crit: 2
};

const alertState = new Map();

const toNumber = (value, fallback) => {
  const parsed = Number(value);
  return Number.isFinite(parsed) ? parsed : fallback;
};

const clampPercent = (value, fallback) => {
  const parsed = toNumber(value, fallback);
  if (!Number.isFinite(parsed)) {
    return fallback;
  }
  return Math.max(1, Math.min(100, parsed));
};

const formatBytes = (bytes) => {
  const value = Number(bytes);
  if (!Number.isFinite(value) || value <= 0) {
    return '0 B';
  }
  const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'];
  let current = value;
  let unitIndex = 0;
  while (current >= 1024 && unitIndex < units.length - 1) {
    current /= 1024;
    unitIndex += 1;
  }
  return `${current.toFixed(current >= 100 ? 0 : 2)} ${units[unitIndex]}`;
};

const percentUsed = (usedBytes, totalBytes) => {
  const total = toNumber(totalBytes, 0);
  const used = toNumber(usedBytes, 0);
  if (total <= 0 || used <= 0) {
    return 0;
  }
  return Math.max(0, Math.min(100, (used / total) * 100));
};

const normalizeSettings = (settings) => {
  const warnPercent = clampPercent(settings.warnPercent, 80);
  const critPercent = clampPercent(settings.critPercent, 90);
  const normalizedCrit = Math.max(critPercent, warnPercent + 1);
  const cooldownMinutes = Math.max(0, Math.round(toNumber(settings.cooldownMinutes, 60)));
  return {
    enabled: settings.enabled !== false,
    warnPercent,
    critPercent: normalizedCrit,
    cooldownMinutes,
    cooldownMs: cooldownMinutes * 60 * 1000,
    once: Boolean(settings.once),
    includePools: settings.includePools !== false
  };
};

const mergeSettings = (base, override) => {
  if (!override || typeof override !== 'object') {
    return base;
  }
  return normalizeSettings({
    ...base,
    ...override
  });
};

const resolveSettings = (alertsConfig, serverConfig, poolName) => {
  const base = normalizeSettings(alertsConfig?.defaults || {});
  const serverAlerts = serverConfig?.alerts || {};
  const poolOverrides = poolName
    ? serverAlerts?.pools?.[poolName] || serverAlerts?.pools?.[String(poolName)]
    : null;

  const serverMerged = mergeSettings(base, serverAlerts);
  const poolMerged = poolOverrides ? mergeSettings(serverMerged, poolOverrides) : serverMerged;

  return {
    ...poolMerged,
    enabled: Boolean(alertsConfig?.enabled) && poolMerged.enabled
  };
};

const levelForPercent = (percent, settings) => {
  if (percent >= settings.critPercent) {
    return 'crit';
  }
  if (percent >= settings.warnPercent) {
    return 'warn';
  }
  return 'none';
};

const shouldSend = (key, level, settings) => {
  if (level === 'none') {
    alertState.delete(key);
    return false;
  }

  const now = Date.now();
  const currentRank = LEVELS[level];
  const previous = alertState.get(key);

  if (!previous) {
    alertState.set(key, { lastSentAt: 0, levelRank: 0, latched: false });
  }

  const state = alertState.get(key);

  if (currentRank > state.levelRank) {
    return true;
  }

  if (settings.once && state.latched) {
    return false;
  }

  if (!settings.cooldownMs) {
    return true;
  }

  return now - state.lastSentAt >= settings.cooldownMs;
};

const markSent = (key, level, settings) => {
  const now = Date.now();
  const levelRank = LEVELS[level] || 0;
  alertState.set(key, {
    lastSentAt: now,
    levelRank,
    latched: settings.once ? true : false
  });
};

const buildServerMessage = (server, percent, level) => {
  const used = formatBytes(server.usedBytes);
  const total = formatBytes(server.totalBytes);
  const rounded = Math.round(percent);
  const levelLabel = level === 'crit' ? 'CRITICAL' : 'Warning';
  return {
    title: `Vault: ${server.name} ${levelLabel}`,
    message: `Server total at ${rounded}% (${used} / ${total}).`,
    priority: level === 'crit' ? 1 : 0
  };
};

const buildPoolMessage = (server, pool, percent, level) => {
  const used = formatBytes(pool.usedBytes);
  const total = formatBytes(pool.totalBytes);
  const rounded = Math.round(percent);
  const levelLabel = level === 'crit' ? 'CRITICAL' : 'Warning';
  return {
    title: `Vault: ${server.name} / ${pool.name} ${levelLabel}`,
    message: `${pool.name} at ${rounded}% (${used} / ${total}).`,
    priority: level === 'crit' ? 1 : 0
  };
};

const poolKey = (serverId, poolName) => `${serverId}::pool::${poolName}`;
const serverKey = (serverId) => `${serverId}::server`;

export async function evaluateAndSendAlerts(payload, storedServers, alertsConfig) {
  if (!payload || !Array.isArray(payload.servers)) {
    return { sent: 0, skipped: 0 };
  }

  const serverMap = new Map((storedServers || []).map((server) => [server.id, server]));
  let sent = 0;
  let skipped = 0;

  for (const server of payload.servers) {
    const stored = serverMap.get(server.id) || {};

    const serverSettings = resolveSettings(alertsConfig, stored, null);
    if (serverSettings.enabled) {
      const percent = percentUsed(server.usedBytes, server.totalBytes);
      const level = levelForPercent(percent, serverSettings);
      const key = serverKey(server.id);
      if (shouldSend(key, level, serverSettings)) {
        try {
          await sendPushoverMessage(alertsConfig, buildServerMessage(server, percent, level));
          markSent(key, level, serverSettings);
          sent += 1;
        } catch (error) {
          // eslint-disable-next-line no-console
          console.error('Pushover server alert failed:', error.message || error);
        }
      } else {
        skipped += 1;
      }
    }

    if (!serverSettings.includePools || !Array.isArray(server.pools)) {
      continue;
    }

    for (const pool of server.pools) {
      const poolSettings = resolveSettings(alertsConfig, stored, pool.name);
      if (!poolSettings.enabled) {
        continue;
      }
      const percent = percentUsed(pool.usedBytes, pool.totalBytes);
      const level = levelForPercent(percent, poolSettings);
      const key = poolKey(server.id, pool.name);
      if (shouldSend(key, level, poolSettings)) {
        try {
          await sendPushoverMessage(alertsConfig, buildPoolMessage(server, pool, percent, level));
          markSent(key, level, poolSettings);
          sent += 1;
        } catch (error) {
          // eslint-disable-next-line no-console
          console.error('Pushover pool alert failed:', error.message || error);
        }
      } else {
        skipped += 1;
      }
    }
  }

  return { sent, skipped };
}

export async function sendTestAlert(alertsConfig) {
  return sendPushoverMessage(alertsConfig, {
    title: 'Vault: Test Alert',
    message: 'Pushover integration is working.',
    priority: 0
  });
}

