import { Router } from 'express';
import { VLAN_ID } from '../config/env.js';
import { buildAcceptPayload, normalizeAttributes } from '../utils/attrs.js';
import { pushRequest } from '../sse.js';
import { activeSessions, sendRadiusSelfTest } from '../services/radius.js';
import { addDeviceToUser, connectDeviceForUser, disconnectDeviceForUser, getOrCreateDevice, upsertSession, endSession } from '../services/db.js';

const router = Router();

router.post('/authorize', (req, res) => {
  const attrs = normalizeAttributes(req.body);
  const reply = buildAcceptPayload();
  // Try to record device usage
  const mac = attrs['Calling-Station-Id'] || attrs['Calling-Station-Id*0'] || '';
  const username = attrs['User-Name'] || attrs['User-Name*0'] || '';
  if (mac && username) {
    getOrCreateDevice({ mac: String(mac) }).then(async (id) => {
      await addDeviceToUser(String(username), id);
    }).catch(() => {});
  }
  pushRequest({
    id: Date.now() + ':' + Math.random().toString(16).slice(2),
    ts: new Date().toISOString(),
    type: 'authorize',
    attrs,
    decision: 'accept',
    vlan: VLAN_ID,
  });
  return res.status(200).json(reply);
});

router.post('/accounting', async (req, res) => {
  const attrs = normalizeAttributes(req.body);
  try {
    const st = String(attrs['Acct-Status-Type'] || attrs['Acct-Status-Type*0'] || '').toUpperCase();
    const sessionId = String(attrs['Acct-Session-Id'] || '');
    const username = String(attrs['User-Name'] || '');
    const mac = attrs['Calling-Station-Id'] || '';

    // Extract statistics from RADIUS attributes
    const stats = {
      bytesIn: parseInt(attrs['Acct-Input-Octets'] || '0', 10),
      bytesOut: parseInt(attrs['Acct-Output-Octets'] || '0', 10),
      packetsIn: parseInt(attrs['Acct-Input-Packets'] || '0', 10),
      packetsOut: parseInt(attrs['Acct-Output-Packets'] || '0', 10),
      sessionTime: parseInt(attrs['Acct-Session-Time'] || '0', 10),
      interimInterval: parseInt(attrs['Acct-Interim-Interval'] || '0', 10) || null,
    };

    if (sessionId && username) {
      if (st === 'START' || st === 'ALIVE' || st === 'INTERIM-UPDATE' || st === 'INTERIM') {
        // Keep Map in memory for CoA compatibility
        activeSessions.set(sessionId, {
          username,
          sessionId,
          nasIp: attrs['NAS-IP-Address'] || '',
          nasId: attrs['NAS-Identifier'] || '',
          callingStationId: mac,
          calledStationId: attrs['Called-Station-Id'] || '',
          updatedAt: Date.now(),
        });

        // Persist session to database
        await upsertSession({
          sessionId,
          username,
          nasIp: attrs['NAS-IP-Address'] || '',
          nasId: attrs['NAS-Identifier'] || '',
          callingStationId: mac,
          calledStationId: attrs['Called-Station-Id'] || '',
          ...stats
        }).catch(e => console.error('[accounting] upsertSession error:', e?.message || e));

        // Upsert device and link as connected
        if (mac) {
          getOrCreateDevice({ mac: String(mac) }).then(async (id) => {
            await addDeviceToUser(String(username), id);
            await connectDeviceForUser(String(username), id);
          }).catch(() => {});
        }
      } else if (st === 'STOP') {
        activeSessions.delete(sessionId);

        // End session in database
        const stopReason = attrs['Acct-Terminate-Cause'] || 'Unknown';
        await endSession(sessionId, stopReason, stats)
          .catch(e => console.error('[accounting] endSession error:', e?.message || e));

        // Disconnect device
        if (mac) {
          getOrCreateDevice({ mac: String(mac) }).then(async (id) => {
            await disconnectDeviceForUser(String(username), id);
          }).catch(() => {});
        }
      }
    }
  } catch (e) {
    console.error('[accounting] error:', e?.message || e);
  }
  pushRequest({
    id: Date.now() + ':' + Math.random().toString(16).slice(2),
    ts: new Date().toISOString(),
    type: 'accounting',
    attrs,
  });
  return res.status(200).json({});
});

router.post('/authorize-inner', async (_req, res) => res.status(410).json({}));

router.post('/post-auth', async (req, res) => {
  try {
    const attrs = normalizeAttributes(req.body);
    const mac = attrs['Calling-Station-Id'] || attrs['Calling-Station-Id*0'] || '';
    const username = attrs['User-Name'] || attrs['User-Name*0'] || '';
    if (mac && username) {
      getOrCreateDevice({ mac: String(mac) }).then(async (id) => {
        await addDeviceToUser(String(username), id);
      }).catch(() => {});
    }
    pushRequest({
      id: Date.now() + ':' + Math.random().toString(16).slice(2),
      ts: new Date().toISOString(),
      type: 'post-auth',
      attrs,
    });
  } catch {}
  return res.status(200).json({});
});

router.post('/test/radius', async (_req, res) => {
  try {
    const result = await sendRadiusSelfTest();
    pushRequest({
      id: Date.now() + ':' + Math.random().toString(16).slice(2),
      ts: new Date().toISOString(),
      type: 'selftest',
      attrs: { 'User-Name': 'selftest-node' },
      decision: result.code,
    });
    res.json({ ok: true, result });
  } catch (err) {
    pushRequest({
      id: Date.now() + ':' + Math.random().toString(16).slice(2),
      ts: new Date().toISOString(),
      type: 'selftest',
      attrs: { 'User-Name': 'selftest-node' },
      decision: 'error',
      error: String(err && err.message || err),
    });
    res.status(500).json({ ok: false, error: String(err && err.message || err) });
  }
});

export default router;
