package org.snmp4j.agent.example;

import org.junit.Test;
import org.snmp4j.*;
import org.snmp4j.agent.DefaultMOServer;
import org.snmp4j.agent.MOServer;
import org.snmp4j.agent.audit.AgentAuditLevel;
import org.snmp4j.agent.audit.AgentPolicyProfile;
import org.snmp4j.agent.audit.DefaultAgentAuditPolicy;
import org.snmp4j.agent.io.DefaultMOPersistenceProvider;
import org.snmp4j.agent.io.ImportMode;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.smi.*;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.snmp4j.util.ArgumentParser;
import org.snmp4j.util.DefaultPDUFactory;

import java.io.File;
import java.nio.file.Files;
import java.util.*;

public class SampleAgentVacmDiagTest {

    private static final String[] ARGS =
            {"-c", "-c SampleAgentVacmDiag.cfg", "-bc", "SampleAgentVacmDiagBC.cfg", "udp:127.0.0.1/0"};

    @Test
    public void diag() throws Exception {
        Files.deleteIfExists(new File("SampleAgentVacmDiag.cfg").toPath());
        Files.deleteIfExists(new File("SampleAgentVacmDiagBC.cfg").toPath());
        SecurityProtocols.getInstance().addDefaultProtocols();
        MOServer[] moServers = new MOServer[]{new DefaultMOServer()};
        ArgumentParser parser = new ArgumentParser(SampleAgent.COMMAND_LINE_OPTIONS, SampleAgent.COMMAND_LINE_PARAMS);
        Map<String, List<Object>> p = parser.parse(ARGS);
        Properties tsl = SampleAgent.getTableSizeLimitsProperties(p);
        // Reproduce the production path: real persistence provider + restoreChanges import mode.
        DefaultMOPersistenceProvider persistence =
                new DefaultMOPersistenceProvider(moServers, "SampleAgentVacmDiag.cfg");
        SampleAgent agent = new SampleAgent(p, moServers, persistence, tsl, ImportMode.restoreChanges,
                new DefaultAgentAuditPolicy(
                        new AgentPolicyProfile(DefaultAgentAuditPolicy.TEST.getAgentPolicyProfile(),
                        new String[] { "SampleAgentVacmDiag.cfg",
                                       "SampleAgentVacmDiagBC.cfg"}, null,
                        new String[] { "127.0.0.1/32"}, null),
                        AgentAuditLevel.RESTRICT));
        agent.run();

        UdpAddress addr = (UdpAddress) agent.agent.getMessageDispatcher().getTransportMappings().iterator().next().getListenAddress();
        Snmp snmp = new Snmp(new DefaultUdpTransportMapping(new UdpAddress()));
        snmp.listen();
        CommunityTarget<UdpAddress> t = new CommunityTarget<>(addr, new OctetString("public"));
        t.setVersion(SnmpConstants.version2c);
        t.setTimeout(3000);
        t.setRetries(1);

        // GET inside system (sysDescr.0)
        PDU getIn = new DefaultPDUFactory(PDU.GET).createPDU(t);
        getIn.add(new VariableBinding(new OID("1.3.6.1.2.1.1.1.0")));
        System.out.println("DIAG GET system sysDescr.0 -> " + snmp.send(getIn, t).getResponse());

        // GET outside system (an snmpEngineID / framework object)
        PDU getOut = new DefaultPDUFactory(PDU.GET).createPDU(t);
        getOut.add(new VariableBinding(new OID("1.3.6.1.6.3.10.2.1.1.0")));
        System.out.println("DIAG GET outside (1.3.6.1.6.3.10.2.1.1.0) -> " + snmp.send(getOut, t).getResponse());

        // GETNEXT walk start at 1.3 -> should jump to system and stay there
        PDU nextPdu = new DefaultPDUFactory(PDU.GETNEXT).createPDU(t);
        nextPdu.add(new VariableBinding(new OID("1.3")));
        System.out.println("DIAG GETNEXT from 1.3 -> " + snmp.send(nextPdu, t).getResponse());

        // SET sysContact.0 (in system) -> must be denied (read-only)
        PDU setPdu = new DefaultPDUFactory(PDU.SET).createPDU(t);
        setPdu.add(new VariableBinding(new OID("1.3.6.1.2.1.1.4.0"), new OctetString("hacker")));
        PDU setResp = snmp.send(setPdu, t).getResponse();
        System.out.println("DIAG SET sysContact.0 -> errorStatus=" + (setResp == null ? "null" : setResp.getErrorStatus() + " (" + setResp.getErrorStatusText() + ")") + " vbs=" + setResp);

        snmp.close();
        agent.agent.shutdown();
    }
}
