<?php

namespace App\Services;

use Exception;

class SnmpManager
{
    private $host;
    private $communityString;
    private $port;
    private $objectIdentities;

    public function __construct($host, $communityString, $objectIdentities, $port = 161)
    {
        $this->host = $host;
        $this->communityString = $communityString;
        $this->port = $port;
        $this->objectIdentities = $objectIdentities;
    }

    public function getOltName()
    {
        $rawResult = $this->runCommandByOid($this->objectIdentities['oltName']);
        return $this->parseValueFromRawString(explode("\n", $rawResult)[0]);
    }

    public function getAllOnuInformation()
    {
        $macWithVlanOnuId = $this->getAllMacWithVlan();
        $vlanWithOnuId = $this->getAllVlanWithOnuId();
        $interfaceWithOnuId = $this->getInterfaceNameWithOnuId();
        $onuLaserWithOnuId = $this->getAllOnuLaserWithOnuId();
        $vlanLookup = collect($vlanWithOnuId)->keyBy('oid');
        $interfaceLookup = collect($interfaceWithOnuId)->keyBy('onuId');
        $onuLaserLookup = collect($onuLaserWithOnuId)->keyBy('onuId');


        return collect($macWithVlanOnuId)->map(function ($macAndId) use ($vlanLookup, $interfaceLookup, $onuLaserLookup) {
            $onuIdWith = str_replace($this->objectIdentities['vlanMac'], $this->objectIdentities['onuId'], $macAndId['oid']);
            $vlanOnuId = $vlanLookup->get($onuIdWith);
            $interfaceInfo = $interfaceLookup->get($vlanOnuId['onuId'] ?? null);
            $onuLaserInfo = $onuLaserLookup->get($vlanOnuId['onuId'] ?? null);

            return [
                'onuId' => $vlanOnuId['onuId'] ?? null,
                'macAddress' => $macAndId['macAddress'] ?? null,
                'vLan' => $macAndId['vLan'] ?? null,
                'interfaceName' => $interfaceInfo['interfaceName'] ?? null,
                'onuLaser' => isset($onuLaserInfo['onuLaser']) ? $onuLaserInfo['onuLaser'] / 10 : null,
            ];
        })->toArray();
    }

    private function parseValueFromRawString($rawString)
    {
        preg_match('/:\s(.+)$/', $rawString, $matches);
        return $matches[1] ?? null;
    }

    private function runCommandByOid($objectIdentity)
    {
        $command = "snmpwalk -v 2c -Cc -c {$this->communityString} {$this->host}:{$this->port} {$objectIdentity}";
        exec($command, $output, $returnVar);

        if ($returnVar !== 0) {
            throw new Exception("SNMP command failed");
        }

        return implode("\n", $output);
    }

    private function getAllMacWithVlan()
    {
        $rawResult = $this->runCommandByOid($this->objectIdentities['vlanMac']);
        $splitted = explode("\n", $rawResult);
        return collect($splitted)->map(function ($macVlanRow) {
            $macAddress = $this->parseValueFromRawString($macVlanRow);
            $vLan = $this->extractVlanFromOid($macVlanRow, $this->objectIdentities['vlanMac']);

            return [
                'macAddress' => $macAddress ? str_replace(' ', '', strtolower($macAddress)) : null,
                'vLan' => $vLan,
                'oid' => explode(" = ", $macVlanRow)[0] ?? null,
            ];
        })->toArray();
    }

    private function getAllVlanWithOnuId()
    {
        $rawResult = $this->runCommandByOid($this->objectIdentities['onuId']);
        $splitted = explode("\n", $rawResult);

        return collect($splitted)->map(function ($row) {
            $vLan = $this->extractVlanFromOid($row, $this->objectIdentities['onuId']);

            return [
                'oid' => explode(" = ", $row)[0] ?? null,
                'onuId' => $this->parseValueFromRawString($row),
                'vLan' => $vLan,
            ];
        })->toArray();
    }

    private function getInterfaceNameWithOnuId()
    {
        $rawResult = $this->runCommandByOid($this->objectIdentities['onuInterfaceName']);
        $splitted = explode("\n", $rawResult);

        return collect($splitted)->map(function ($row) {
            preg_match('/\\.([\\d]+)(?=\\s=\\s)/', $row, $matches);

            return [
                'interfaceName' => str_replace('"', '', $this->parseValueFromRawString($row)),
                'onuId' => $matches[1] ?? null,
            ];
        })->toArray();
    }

    private function getAllOnuLaserWithOnuId()
    {
        $rawResult = $this->runCommandByOid($this->objectIdentities['onuLaser']);
        $splitted = explode("\n", $rawResult);

        return collect($splitted)->map(function ($row) {
            preg_match('/\\.([\\d]+)(?=\\s=\\s)/', $row, $matches);

            return [
                'onuLaser' => $this->parseValueFromRawString($row),
                'onuId' => $matches[1] ?? null,
            ];
        })->toArray();
    }

    private function extractVlanFromOid($row, $baseOid)
    {
        return explode('.', str_replace($baseOid, '', $row))[1] ?? null;
    }
}
