import WebSocket from 'ws';

const bytesToTb = (bytes) => bytes / (1024 ** 4);

const buildBaseUrl = (server) => {
  const rawHost = String(server.host || server.name || '').trim();
  if (!rawHost) {
    throw new Error('Missing host for TrueNAS 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 toWebSocketUrl = (baseUrl, wsPath) => {
  const wsUrl = new URL(baseUrl);
  wsUrl.protocol = wsUrl.protocol === 'https:' ? 'wss:' : 'ws:';
  wsUrl.pathname = wsPath;
  wsUrl.search = '';
  wsUrl.hash = '';
  return wsUrl;
};

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

const buildPoolEntry = (poolName, totalBytes, usedBytes) => ({
  name: poolName,
  totalTb: bytesToTb(totalBytes),
  usedTb: bytesToTb(usedBytes),
  totalBytes,
  usedBytes
});

const resolveWsPath = (server) => {
  const rawPath = server?.config?.wsPath || '/api/current';
  const normalized = String(rawPath).trim();
  if (!normalized) {
    return '/api/current';
  }
  return normalized.startsWith('/') ? normalized : `/${normalized}`;
};

const createRpcClient = (server, baseUrl) => {
  const wsPath = resolveWsPath(server);
  const wsUrl = toWebSocketUrl(baseUrl, wsPath);
  const allowInsecure = server?.config?.allowInsecure !== false;
  const socketOptions = allowInsecure ? { rejectUnauthorized: false } : undefined;
  const wsProtocol = String(server?.config?.wsProtocol || '').trim();
  const socket = wsProtocol
    ? new WebSocket(wsUrl, wsProtocol, socketOptions)
    : new WebSocket(wsUrl, socketOptions);

  let nextId = 1;
  const pending = new Map();

  const closeWithError = (error) => {
    pending.forEach(({ reject }) => reject(error));
    pending.clear();
    try {
      socket.close();
    } catch {
      // ignore close errors
    }
  };

  socket.on('message', (data) => {
    try {
      const payload = JSON.parse(String(data));
      const entry = pending.get(payload.id);
      if (!entry) {
        return;
      }
      pending.delete(payload.id);
      if (payload.error) {
        entry.reject(new Error(payload.error.message || 'TrueNAS JSON-RPC error.'));
        return;
      }
      entry.resolve(payload.result);
    } catch (error) {
      closeWithError(error);
    }
  });

  socket.on('error', (error) => {
    closeWithError(error);
  });

  const waitForOpen = () => new Promise((resolve, reject) => {
    const handleOpen = () => {
      socket.off('error', handleError);
      resolve();
    };
    const handleError = (error) => {
      socket.off('open', handleOpen);
      reject(error);
    };
    socket.once('open', handleOpen);
    socket.once('error', handleError);
  });

  const call = (method, params = []) => new Promise((resolve, reject) => {
    const id = nextId++;
    pending.set(id, { resolve, reject });
    socket.send(JSON.stringify({
      jsonrpc: '2.0',
      id,
      method,
      params
    }), (error) => {
      if (error) {
        pending.delete(id);
        reject(error);
      }
    });
  });

  return {
    waitForOpen,
    call,
    close: () => socket.close()
  };
};

export async function fetchTrueNasStorage(server) {
  const apiKey = server?.config?.apiKey;
  if (!apiKey) {
    throw new Error('TrueNAS credentials missing.');
  }

  const baseUrl = buildBaseUrl(server);
  const client = createRpcClient(server, baseUrl);

  try {
    await client.waitForOpen();
    const authResult = await client.call('auth.login_with_api_key', [apiKey]);
    if (authResult !== true) {
      throw new Error('TrueNAS authentication failed.');
    }
    const pools = await client.call('pool.query', []);

    if (!Array.isArray(pools) || pools.length === 0) {
      throw new Error('No TrueNAS pool data returned.');
    }

    let totalBytes = 0;
    let usedBytes = 0;

    const perPool = pools.map((pool) => {
      const total = Number(pool.size || 0);
      const used = Number(pool.allocated || pool.used || 0);
      totalBytes += total;
      usedBytes += used;
      return buildPoolEntry(pool.name || 'zfs-pool', total, 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: 'TrueNAS SCALE',
      cluster: server.config?.cluster || 'Vault TrueNAS',
      location: server.config?.location || server.host,
      pool: 'All pools',
      totalTb,
      usedTb,
      totalBytes,
      usedBytes,
      pools: perPool,
      iops: 0,
      latencyMs: 0,
      status: statusMeta.status,
      alerts: statusMeta.alerts
    };
  } finally {
    client.close();
  }
}
