import { Agent } from 'undici';

const bytesToTb = (bytes) => bytes / 1e12;

const buildBaseUrl = (server) => {
  const rawHost = String(server.host || server.name || '').trim();
  if (!rawHost) {
    throw new Error('Missing host for Proxmox Backup Server.');
  }
  const hasScheme = rawHost.startsWith('http://') || rawHost.startsWith('https://');
  const scheme = server?.config?.scheme || 'https';
  const url = new URL(hasScheme ? rawHost : `${scheme}://${rawHost}`);
  if (server.port && !url.port) {
    url.port = String(server.port);
  }
  return url;
};

const getDispatcher = (server) => {
  if (server?.config?.allowInsecure === false) {
    return undefined;
  }
  return new Agent({ connect: { rejectUnauthorized: false } });
};

const getStatus = (percent) => {
  if (percent >= 85) {
    return { status: 'critical', alerts: 2 };
  }
  return { status: 'healthy', alerts: 0 };
};

const requestJson = async (url, dispatcher, headers) => {
  const response = await fetch(url, { headers, dispatcher });
  if (!response.ok) {
    throw new Error(`PBS API error (${response.status}).`);
  }
  const payload = await response.json();
  return payload?.data ?? payload;
};

const requestJsonWithAuth = async (url, dispatcher, headersList) => {
  let lastError = null;
  for (const headers of headersList) {
    try {
      return await requestJson(url, dispatcher, headers);
    } catch (error) {
      lastError = error;
      const message = String(error?.message || '');
      if (!message.includes('(401)') && !message.includes('(403)')) {
        break;
      }
    }
  }
  throw lastError || new Error('PBS API error.');
};

const normalizeDatastore = (item) => {
  const name = item.store || item.name || item.datastore || item.id;
  const rawTotal = item.total ?? item.total_bytes ?? item.size ?? item.max ?? item.max_bytes ?? item.total_size;
  const rawUsed = item.used ?? item.used_bytes ?? item.used_size ?? item.bytes_used ?? item.usage;
  const rawAvail = item.avail ?? item.available ?? item.free ?? item.avail_bytes ?? item.free_bytes ?? item.available_bytes;

  let total = Number(rawTotal ?? 0);
  let used = Number(rawUsed ?? 0);
  const avail = rawAvail !== undefined ? Number(rawAvail ?? 0) : null;

  if (!Number.isFinite(total)) {
    total = 0;
  }
  if (!Number.isFinite(used)) {
    used = 0;
  }

  if (total === 0 && avail !== null) {
    total = used > 0 ? used + avail : avail;
  }
  if (used === 0 && avail !== null && total > 0) {
    used = Math.max(0, total - avail);
  }

  return {
    name,
    total,
    used,
    avail
  };
};

const fetchDatastores = async (baseUrl, dispatcher, headersList) => {
  const endpoints = [
    '/api2/json/status/datastore',
    '/api2/json/admin/datastore',
    '/api2/json/datastore'
  ];

  let lastError = null;
  for (const path of endpoints) {
    try {
      const endpoint = new URL(path, baseUrl);
      const data = await requestJsonWithAuth(endpoint, dispatcher, headersList);
      if (Array.isArray(data) && data.length > 0) {
        return data;
      }
    } catch (error) {
      lastError = error;
    }
  }
  if (lastError) {
    throw lastError;
  }
  return [];
};

const fetchDatastoreDetails = async (baseUrl, dispatcher, headersList, name) => {
  const endpoints = [
    `/api2/json/admin/datastore/${encodeURIComponent(name)}/status`,
    `/api2/json/status/datastore/${encodeURIComponent(name)}`,
    `/api2/json/admin/datastore/${encodeURIComponent(name)}`,
    `/api2/json/datastore/${encodeURIComponent(name)}`
  ];

  let lastError = null;
  for (const path of endpoints) {
    try {
      const endpoint = new URL(path, baseUrl);
      const data = await requestJsonWithAuth(endpoint, dispatcher, headersList);
      if (data) {
        return data;
      }
    } catch (error) {
      lastError = error;
    }
  }
  if (lastError) {
    throw lastError;
  }
  return null;
};

export async function fetchPbsStorage(server) {
  const tokenId = server?.config?.apiTokenId;
  const tokenSecret = server?.config?.apiTokenSecret;
  if (!tokenId || !tokenSecret) {
    throw new Error('Proxmox Backup Server credentials missing.');
  }

  const dispatcher = getDispatcher(server);
  const headersList = [
    {
      Accept: 'application/json',
      Authorization: `PBSAPIToken=${tokenId}=${tokenSecret}`
    },
    {
      Accept: 'application/json',
      Authorization: `PBSAPIToken=${tokenId}:${tokenSecret}`
    }
  ];
  const baseUrl = buildBaseUrl(server);

  const datastores = await fetchDatastores(baseUrl, dispatcher, headersList);
  if (!Array.isArray(datastores) || datastores.length === 0) {
    throw new Error('No Proxmox Backup Server datastores returned.');
  }

  const normalized = await Promise.all(datastores.map(async (item) => {
    const candidate = normalizeDatastore(item);
    if (!candidate.name) {
      return null;
    }
    if (candidate.total > 0 || candidate.used > 0) {
      return candidate;
    }
    const details = await fetchDatastoreDetails(baseUrl, dispatcher, headersList, candidate.name);
    if (!details) {
      return candidate;
    }
    return normalizeDatastore({ ...details, store: candidate.name });
  }));

  const pools = normalized.filter(Boolean);
  if (pools.length === 0) {
    throw new Error('No Proxmox Backup Server datastore capacity data returned.');
  }

  let totalBytes = 0;
  let usedBytes = 0;
  pools.forEach((pool) => {
    totalBytes += pool.total;
    usedBytes += pool.used;
  });

  const totalTb = bytesToTb(totalBytes);
  const usedTb = bytesToTb(usedBytes);
  const usedPercent = totalBytes ? Math.round((usedBytes / totalBytes) * 100) : 0;
  const statusMeta = getStatus(usedPercent);

  return {
    id: server.id,
    name: server.name || server.host,
    platform: 'Proxmox Backup Server',
    cluster: server.config?.cluster || 'Vault PBS',
    location: server.config?.location || server.host,
    pool: 'Datastores',
    totalTb,
    usedTb,
    totalBytes,
    usedBytes,
    pools: pools.map((pool) => ({
      name: pool.name,
      totalTb: bytesToTb(pool.total),
      usedTb: bytesToTb(pool.used),
      totalBytes: pool.total,
      usedBytes: pool.used
    })),
    iops: 0,
    latencyMs: 0,
    status: statusMeta.status,
    alerts: statusMeta.alerts
  };
}
