<?php

namespace App\Services;

use Exception;

class VsolManager2
{
    private string $host;
    private string $communityString;
    private int $port;
    private array $objectIdentities;

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

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

    public function getAllOnuInformation(): array
    {
        $macWithVlan = collect($this->getAllMacWithVlan())->keyBy('onuId');
        $interfaceWithOnuId = collect($this->getInterfaceNameWithOnuId())->keyBy('onuId');
        $onuLaserWithOnuId = collect($this->getAllOnuLaserWithOnuId())->keyBy('onuId');

        return $interfaceWithOnuId->map(function ($interfaceItem, $onuId) use ($macWithVlan, $onuLaserWithOnuId) {
            $interfaceName = $interfaceItem['interfaceName'] ?? '';
            $laserData = $this->getLaserData($interfaceName, $onuLaserWithOnuId);
            $macItem = $macWithVlan->get($onuId, []);

            return array_merge(
                ['onuId' => $onuId],
                $interfaceItem,
                $macItem,
                $laserData
            );
        })->toArray();
    }

    private function getLaserData(string $interfaceName, $onuLaserWithOnuId): array
    {
        if (preg_match('/PON(\d+):ONU(\d+)/', $interfaceName, $matches)) {
            $compositeId = $matches[1] . ':' . $matches[2];
            return $compositeId ? $onuLaserWithOnuId->get($compositeId, []) : [];
        }
        return $onuLaserWithOnuId->get($interfaceName, []);
    }

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

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

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

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

    private function getAllMacWithVlan(): array
    {
        $rawResult = $this->runCommandByOid($this->objectIdentities['vlanMac']);

        return collect(explode("\n", $rawResult))->map(function ($row) {
            if (empty($row)) {
                return null;
            }

            $macParts = explode("=", $row);
            if (count($macParts) < 2) {
                return null;
            }

            $oid = trim($macParts[0]);
            $macString = trim($macParts[1]);

            $oidParts = explode(".", $oid);
            $lastNumber = end($oidParts);

            preg_match('/Hex-STRING:\s+([0-9A-F\s]+)/', $macString, $stringMatches);
            $macAddress = isset($stringMatches[1]) ? str_replace(" ", ":", trim($stringMatches[1])) : null;

            return [
                'onuId' => $lastNumber,
                'macAddress' => $macAddress,
                'vLan' => $this->extractVlanFromOid($row, $this->objectIdentities['vlanMac']),
            ];
        })->filter()
        ->toArray();
    }

    private function getInterfaceNameWithOnuId(): array
    {
        $rawResult = $this->runCommandByOid($this->objectIdentities['onuInterfaceName']);

        return collect(explode("\n", $rawResult))->map(function ($row) {
            preg_match('/\.(\d+) = STRING: "([^"]+)"/', $row, $matches);

            return [
                'interfaceName' => $matches[2] ?? null,
                'onuId' => $matches[1] ?? null,
            ];
        })->toArray();
    }

    private function getAllOnuLaserWithOnuId(): array
    {
        $rawResult = $this->runCommandByOid($this->objectIdentities['onuLaser']);

        return collect(explode("\n", $rawResult))
            ->map(function ($row) {
                preg_match('/\.(\d+\.\d+) = STRING: "(-?\d+\.\d+)"/', $row, $matches);

                return [
                    'onuLaser' => $matches[2] ?? null,
                    'onuId' => isset($matches[1]) ? str_replace('.', ':', $matches[1]) : null,
                ];
            })
            ->filter(function ($item) {
                return !is_null($item['onuLaser']) && !is_null($item['onuId']);
            })
            ->toArray();
    }

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