import dgram from 'dgram';
import radius from 'radius';
import { RADIUS_AUTH_PORT, RADIUS_HOST, RADIUS_SECRET } from '../config/env.js';
import { pushRequest } from '../sse.js';

export const activeSessions = new Map();

export async function sendRadiusSelfTest() {
  return new Promise((resolve, reject) => {
    try {
      const packet = radius.encode({
        code: 'Access-Request',
        secret: RADIUS_SECRET,
        attributes: {
          'User-Name': 'selftest-node',
          'NAS-Identifier': 'node-dashboard',
          'Calling-Station-Id': '001122334455',
        },
      });
      const client = dgram.createSocket('udp4');
      const started = Date.now();
      const timeout = setTimeout(() => {
        client.close();
        reject(new Error('timeout'));
      }, 4000);
      client.on('message', (msg) => {
        clearTimeout(timeout);
        client.close();
        const res = radius.decode({ packet: msg, secret: RADIUS_SECRET });
        resolve({ code: res.code, rtt_ms: Date.now() - started });
      });
      client.send(packet, 0, packet.length, RADIUS_AUTH_PORT, RADIUS_HOST, (err) => {
        if (err) {
          clearTimeout(timeout);
          client.close();
          reject(err);
        }
      });
    } catch (e) {
      reject(e);
    }
  });
}

export async function sendDisconnectRequest({ nasIp, username, sessionId, callingStationId, nasId }) {
  if (!nasIp) throw new Error('NAS IP required for Disconnect-Request');
  const packet = radius.encode({
    code: 'Disconnect-Request',
    secret: RADIUS_SECRET,
    attributes: {
      'User-Name': username,
      'Acct-Session-Id': sessionId,
      'Calling-Station-Id': callingStationId || undefined,
      'NAS-IP-Address': nasIp,
      'NAS-Identifier': nasId || undefined,
    },
  });
  return new Promise((resolve, reject) => {
    const client = dgram.createSocket('udp4');
    const timeout = setTimeout(() => {
      client.close();
      reject(new Error('CoA timeout'));
    }, 3000);
    client.on('message', (msg) => {
      clearTimeout(timeout);
      client.close();
      try {
        const res = radius.decode({ packet: msg, secret: RADIUS_SECRET });
        resolve({ code: res.code });
      } catch (e) {
        resolve({ code: 'unknown' });
      }
    });
    client.send(packet, 0, packet.length, 3799, nasIp, (err) => {
      if (err) {
        clearTimeout(timeout);
        client.close();
        reject(err);
      }
    });
  });
}

export async function disconnectUserSessions(username) {
  const targets = [];
  for (const sess of activeSessions.values()) {
    if (sess.username === username && sess.nasIp) targets.push(sess);
  }
  if (targets.length === 0) return;
  for (const sess of targets) {
    try {
      const result = await sendDisconnectRequest({
        nasIp: sess.nasIp,
        username: sess.username,
        sessionId: sess.sessionId,
        callingStationId: sess.callingStationId,
        nasId: sess.nasId,
      });
      pushRequest({
        id: Date.now() + ':' + Math.random().toString(16).slice(2),
        ts: new Date().toISOString(),
        type: 'coa-disconnect',
        attrs: {
          'User-Name': sess.username,
          'NAS-IP-Address': sess.nasIp,
          'Acct-Session-Id': sess.sessionId,
          'Calling-Station-Id': sess.callingStationId,
          'result': result.code,
        },
      });
    } catch (e) {
      pushRequest({
        id: Date.now() + ':' + Math.random().toString(16).slice(2),
        ts: new Date().toISOString(),
        type: 'coa-disconnect',
        attrs: {
          'User-Name': sess.username,
          'NAS-IP-Address': sess.nasIp,
          'Acct-Session-Id': sess.sessionId,
          'Calling-Station-Id': sess.callingStationId,
          'error': String(e?.message || e),
        },
      });
    }
  }
}

