<?php


namespace ModulesGarden\ProxmoxAddon\App\Services\Cloud;

use Illuminate\Database\Capsule\Manager as DB;
use MGProvision\Proxmox\v2\Api;
use MGProvision\Proxmox\v2\models\Config;
use MGProvision\Proxmox\v2\models\IpConfig;
use MGProvision\Proxmox\v2\models\Kvm;
use MGProvision\Proxmox\v2\models\NetworkDeviceKvm;
use MGProvision\Proxmox\v2\models\NetworkDeviceLxc;
use ModulesGarden\ProxmoxAddon\App\Models\IpAddress;
use ModulesGarden\ProxmoxAddon\App\Models\VirtualInterface;
use ModulesGarden\ProxmoxAddon\App\Models\VirtualNetwork;
use ModulesGarden\ProxmoxAddon\App\Models\VmIpAddress;
use ModulesGarden\ProxmoxAddon\App\Services\ApiService;
use ModulesGarden\ProxmoxAddon\App\Services\IpLogService;
use ModulesGarden\ProxmoxAddon\App\Services\Utility;
use ModulesGarden\ProxmoxAddon\Core\UI\Traits\WhmcsParams;
use ModulesGarden\ProxmoxAddon\App\Enum\Cloud\ConfigurableOption;
use ModulesGarden\ProxmoxAddon\App\Enum\Cloud\CustomField;
use function ModulesGarden\ProxmoxAddon\Core\Helper\sl;

class NetworkService
{
    use WhmcsParams;
    use HostingService;
    use ApiService;
    use ProductService;

    /**
     * @param VmIpAddress[] $entries
     */
    public function create(array $entries)
    {
        //to do acl
        $this->setHostingId($this->getWhmcsParamByKey("serviceid"));
        DB::beginTransaction();

        try
        {
            foreach ($entries as &$ip)
            {
                //insert to data base
                $ip->save();
                $this->hosting()->ipAdd($ip->ip);
                //lock ip in proxmox addon
                if (!Utility::isIpManagerProxmoxVPSIntegration())
                {
                    IpAddress::where('ip', $ip->ip)
                        ->update(["hosting_id" => $ip->hosting_id]);
                }
            }
            //update hosting
            $this->hosting()->save();
            DB::commit();
        }
        catch (\Exception $ex)
        {
            DB::rollBack();
            throw $ex;
        }
    }

    /**
     * @deprecated
     */
    public function addPrivate( $entries)
    {
        $this->setHostingId($this->getWhmcsParamByKey("serviceid"));
        $vm =  \ModulesGarden\ProxmoxAddon\Core\Helper\sl('Vm')->getVm();
        DB::beginTransaction();
        Api::beginTransaction();
        $container = [];
        $rate      = null;
        if ($this->isWhmcsConfigOption(ConfigurableOption::NETWORK_RATE) && $this->getWhmcsConfigOption(ConfigurableOption::NETWORK_RATE) != "-1")
        {
            $rate = $this->getWhmcsConfigOption(ConfigurableOption::NETWORK_RATE);
        }
        else
        {
            if ($this->configuration()->getRate())
            {
                $rate = $this->configuration()->getRate();
            }
        }
        //cloud init
        $isCloudInit = $this->configuration()->isQemu() && $this->configuration()->isCloudInit();
        try
        {

            foreach ($entries as &$ip)
            {
                //public network
                if($ip->vn_id == 0){
                    continue;
                }
                $tag       = $ip->virtualNetwork->tag;
                //insert to data base
                $ip->save();
                $this->hosting()->ipAdd($ip->ip);
                $networkId = $vm->findFreeNetworDeviceId();

                if ($this->configuration()->isLxc())
                {
                    $bridge = $this->configuration()->getPrivateBridge() ? $this->configuration()->getPrivateBridge() :  $this->configuration()->getBridge();
                    $networkDevice = new NetworkDeviceLxc('net' . $networkId);
                    $networkDevice->setName('eth' . $networkId)
                        ->setBridge($bridge)
                        ->setFirewall($this->configuration()->isNetworkFirewall() ? 1 : 0);

                    $networkDevice->setIp($ip->ip)
                        ->setCidr($ip->virtualNetwork->cidr)
                        ->setGw($ip->virtualNetwork->gateway)
                        ->setTag($tag)
                        ->setRate($rate);
                    $container[$networkDevice->getId()] = $networkDevice->asConfig();
                }
                else
                {
                    if ($this->configuration()->isQemu())
                    {
                        $networkDevice = new NetworkDeviceKvm('net' . $networkId);
                        $networkDevice->setBridge($this->configuration()->getPrivateBridge())
                            ->setModel($this->configuration()->getNetworkPrivateModel())
                            ->setRate($rate)
                            ->setTag($tag);
                        $container[$networkDevice->getId()] = $networkDevice->asConfig();
                        if ($isCloudInit)
                        {
                            $ipConfig = new IpConfig('ipconfig' . $networkId);
                            $ipConfig->setIp(trim($ip->ip))
                                ->setCidr(trim($ip->virtualNetwork->cidr))
                                ->setGw(trim($ip->virtualNetwork->gateway));
                            $container[$ipConfig->getId()] = $ipConfig->asConfig();
                        }
                    }
                }
                $ip->net = $networkDevice->getId();
                $ip->save();
            }
            //update hosting
            $this->hosting()->save();
            $vm->updateConfig($container);
            DB::commit();
        }
        catch (\Exception $ex)
        {
            DB::rollBack();
            Api::commit();
            throw $ex;
        }
    }

    protected function makeTag()
    {
        if (is_numeric($this->getWhmcsCustomField(CustomField::TAG)))
        {
            return $this->getWhmcsCustomField(CustomField::TAG);
        }
        if (!$this->configuration()->getTagFrom() && $this->configuration()->getTagTo())
        {
            return null;
        }
        $tag = $this->nextTag();
        if (!$tag)
        {
            throw new \Exception("Max VLAN tag have been reached, Please Configure product  (Max VLAN tag)");
        }
        $this->customFieldUpdate("VLAN Tag", $tag);
        return $tag;
    }

    public function nextTag()
    {
        if(!$this->configuration()->getTagFrom() || !$this->configuration()->getTagTo() && $this->configuration()->isPermissionVirtualNetwork()){
            throw new \Exception("Product configuration validator error: VLAN TAG Range Number is empty.");
        }
        $h = 'tblhosting';
        $vn = (new VirtualNetwork)->getTable();
        for ($i = $this->configuration()->getTagFrom(); $i <= $this->configuration()->getTagTo(); $i++)
        {
            $query = VirtualNetwork::select("{$vn}.tag")
                           ->leftJoin($h, "{$h}.id", "=", "{$vn}.hosting_id")
                          ->where("{$h}.packageid", $this->getWhmcsParamByKey("packageid"))
                          ->where("{$vn}.tag", $i);
            if ($query->count())
            {
                continue;
            }
            return $i;
        }
        return false;
    }

    /**
     * @return VmIpAddress
     */
    public function getPrivateIpAddress()
    {
        $ip    = IpAddress::where('private', 1)
            ->where('type', 'IPv4')
            ->whereIn("sid", [$this->getWhmcsParamByKey('serverid'), "0"])
            ->where('hosting_id', '0')
            ->firstOrFail();
        $newIp = new VmIpAddress();
        $newIp->fill($ip->toArray());
        $newIp->hosting_id = $this->getWhmcsParamByKey("serviceid");
        $newIp->server_id  = $this->getWhmcsParamByKey("serverid");
        return $newIp;
    }

    public function hasPrivateIpAddress()
    {
        $query = IpAddress::where('private', 1)
            ->where('type', 'IPv4')
            ->whereIn("sid", [$this->getWhmcsParamByKey('serverid'), "0"])
            ->where('hosting_id', '0');
        return $query->count();
    }

    public function deleteByNetworkId(array $networkIds)
    {
        if (is_null($networkIds))
        {
            throw new \InvalidArgumentException('The network ids cannot be empty');
        }
        $ipAddresses = VmIpAddress::whereIn("net", $networkIds)
            ->ofHostingId($this->getWhmcsParamByKey('serviceid'))
            ->ofVmId(\ModulesGarden\ProxmoxAddon\Core\Helper\sl('Vm')->getVmModel()->id)
            ->get()->all();
        return $this->deleteByIpAddresses($ipAddresses);
    }

    public function deleteByIpAddresses( $ipAddresses)
    {
        if (is_null($ipAddresses))
        {
            throw new \InvalidArgumentException('The IP Addresses cannot be empty');
        }
        $vm = \ModulesGarden\ProxmoxAddon\Core\Helper\sl('Vm')->getVm();
        $devices      = $vm->getNetworkDevices();
        $configDelete = [];
        $ipunassigned = [];
        $this->setHostingId($this->getWhmcsParamByKey("serviceid"));
        DB::beginTransaction();
        Api::beginTransaction();
        try
        {
            foreach ($ipAddresses as &$ip)
            {
                if (is_numeric($ip))
                {
                    $ip = VmIpAddress::where("id", $ip)->firstOrFail();
                }

                //vi or VmIpAddress
                $ip->delete();
                //Public IP
                if($ip instanceof  VirtualInterface && $ip->vn_id == 0) {
                    $query = VmIpAddress::ofHostingId($this->getWhmcsParamByKey("serviceid"))
                        ->ofVmId($ip->vm_id)
                        ->ofIp($ip->ip);
                    if($this->configuration()->isOrderPublicIp()){
                        $query->update(['vm_id' => null, 'net' => null]);
                    }else{
                        $query->delete();
                        //Unlock ip in Proxmox Addon
                        IpAddress::where('ip', $ip->ip)
                            ->where('hosting_id', $ip->hosting_id)
                            ->update(["hosting_id" => "0", 'last_check' => Utility::timeStamp()]);
                        $this->hosting()->ipDelete($ip->ip);
                        $ipunassigned[] = $ip->ip;
                    }
                }

                if ($ip instanceof VmIpAddress && !$this->configuration()->isOrderPublicIp())
                {
                    //Unlock ip in Proxmox Addon
                    IpAddress::where('ip', $ip->ip)
                        ->where('hosting_id', $ip->hosting_id)
                        ->update(["hosting_id" => "0", 'last_check' => Utility::timeStamp()]);
                    //virtual interface delete
                    VirtualInterface::ofHostingId($ip->hosting_id)
                        ->ofIp($ip->ip)
                        ->delete();
                    $this->hosting()->ipDelete($ip->ip);
                    $ipunassigned[] = $ip->ip;
                }

                foreach ($devices as $deviceIndex => $device)
                {
                    if ($this->configuration()->isOneNetworkDevice() && $device->getId() == "net0" && $ip instanceof VirtualInterface)
                    {
                        continue;
                    }
                    if($device instanceof NetworkDeviceLxc)
                    {
                        if ($ip->ip == $device->getIp())
                        {
                            $device->setIp(null);
                        }

                        if ($ip->ip == $device->getIp6())
                        {
                            $device->setIp6(null);
                        }

                        if(empty($device->getIp6()) && empty($device->getIp()))
                        {
                            unset($devices[$deviceIndex]);
                            $configDelete[] = $device->getId();
                        }
                    }
                    elseif($device instanceof  NetworkDeviceKvm && $ip->net && $ip->net == $device->getId())
                    {
                        $configDelete[] = $device->getId();
                        unset($devices[$deviceIndex]);
                    }
                }

                //cloud init
                if ($vm instanceof Kvm)
                {
                    foreach ($vm->getIpConfig()->fetch() as $ipConfig)
                    {
                        if ($ipConfig->getIp() == $ip->ip || $ipConfig->getIp6() == $ip->ip)
                        {
                            $configDelete[] = $ipConfig->getId();
                        }
                    }
                }
            }

            $this->hosting()->save();

            /**
             * Delete config. We need that when there is no more network interface
             */
            if (!empty($configDelete))
            {
                $vm->deleteConfig(implode(",", $configDelete));
            }
            if(!empty( $ipunassigned)){
                (new IpLogService())->unassigned($ipunassigned);
            }

            /**
             * Update current config. We need that only if we remove only one IP and other IP is still on the same interface

            $config = new Config();
            $config->setNet($devices);
            $config = $config->toArray();
            if($config)
            {
                $vm->updateConfig($config);
            }
             *
             */

            DB::commit();
        }
        catch (\Exception $ex)
        {
            DB::rollBack();
            Api::commit();
            throw $ex;
        }
    }

    public function hasIp($totalIpv4, $totalIpv6, $nodeName, $bridge="")
    {
        $virtualization = $this->configuration()->isQemu() ? "KVM" : "LXC";
        if ((int)$totalIpv4 > 0)
        {
            /**
             * @var $ipCollections IpAddress
             */
            $ipCollections = IpAddress::where('hosting_id', '0')
                ->where('private', '0')
                ->where('type', 'IPv4')
                ->whereIn('sid', [$this->getWhmcsParamByKey('serverid'), '0'])
                ->whereIn('visualization', [$virtualization, 'Auto'])
                ->whereIn('node', [$nodeName, '0', ""])
                ->whereIn('bridge', [$bridge, ""]);
            if ($this->configuration()->getTags())
            {
                $ipCollections->ofTags($this->configuration()->getTags());
            }
            $count = $ipCollections->count();
            if ((int)$count < $totalIpv4)
            {
                throw new \Exception(sprintf("Unable to get %s of IPv4. IP Addresses available: %s", $totalIpv4, $count));
            }
        }
        if ((int)$totalIpv6 > 0)
        {
            $ipCollections = IpAddress::where('hosting_id', '0')
                ->where('private', '0')
                ->where('type', 'IPv6')
                ->whereIn('sid', [$this->getWhmcsParamByKey('serverid'), '0'])
                ->whereIn('visualization', [$virtualization, 'Auto'])
                ->whereIn('node', [$nodeName, '0', ""])
                ->whereIn('bridge', [$bridge, ""]);
            if ($this->configuration()->getTags())
            {
                $ipCollections->ofTags($this->configuration()->getTags());
            }
            $count = $ipCollections->count();
            if ((int)$count < $totalIpv6)
            {
                throw new \Exception(sprintf("Unable to get %s of IPv6. IP Addresses available: %s", $totalIpv6, $count));
            }
        }
        return $this;
    }

    public function addIp($totalIpv4, $totalIpv6, $nodeName, $bridge="")
    {
        try {
            DB::beginTransaction();
            $virtualization = $this->configuration()->isQemu() ? "KVM" : "LXC";
            $this->setHostingId($this->getWhmcsParamByKey('serviceid'));
            if ((int)$totalIpv4 > 0) {
                $query = IpAddress::where('hosting_id', '0')
                    ->where('private', '0')
                    ->where('type', 'IPv4')
                    ->whereIn('sid', [$this->getWhmcsParamByKey('serverid'), '0'])
                    ->whereIn('visualization', [$virtualization, 'Auto'])
                    ->whereIn('node', [$nodeName, '0', ""])
                    ->whereIn('bridge', [$bridge, ""]);
                if ($this->configuration()->getTags()) {
                    $query->ofTags($this->configuration()->getTags());
                }
                $count = $query->limit($totalIpv4)
                    ->orderBy('last_check', 'asc')
                    ->update(["hosting_id" => $this->getWhmcsParamByKey('serviceid'), 'last_check' => Utility::timeStamp()]);
                if ((int)$count < $totalIpv4) {
                    throw new \Exception(sprintf("Unable to get %s of IPv4. IP Addresses available: %s", $totalIpv4, $count));
                }

            }
            if ((int)$totalIpv6 > 0) {
                $query = IpAddress::where('hosting_id', '0')
                    ->where('private', '0')
                    ->where('type', 'IPv6')
                    ->whereIn('sid', [$this->getWhmcsParamByKey('serverid'), '0'])
                    ->whereIn('visualization', [$virtualization, 'Auto'])
                    ->whereIn('node', [$nodeName, '0', ""])
                    ->whereIn('bridge', [$bridge, ""]);
                if ($this->configuration()->getTags()) {
                    $query->ofTags($this->configuration()->getTags());
                }
                $count = $query->limit($totalIpv6)
                    ->orderBy('last_check', 'asc')
                    ->update(["hosting_id" => $this->getWhmcsParamByKey('serviceid'), 'last_check' => Utility::timeStamp()]);

                if ((int)$count < $totalIpv6) {
                    throw new \Exception(sprintf("Unable to get %s of IPv6. IP Addresses available: %s", $totalIpv6, $count));
                }
            }
            $vmIp = (new VmIpAddress())->getTable();
            $collection = IpAddress::where('hosting_id', $this->getWhmcsParamByKey('serviceid'))
                ->orderBy("ip", "desc")
                ->whereNotIn('ip', function ($query) use ($vmIp) {
                    $query->select('ip')->from($vmIp);
                });
            $newIps = [];
            foreach ($collection->get()->sortBy("type")->all() as $ip) {
                /*@var $ip IpAddress */
                $newIp = new VmIpAddress();
                $ipData = $ip->toArray();
                unset($ipData['id']);
                $ipData['hosting_id'] = $this->getWhmcsParamByKey('serviceid');
                $ipData['server_id'] = $this->getWhmcsParamByKey('serverid');
                $newIp->vm_id = null;
                if(sl('Vm')->hasVmModel()){
                    $newIp->vm_id =     sl('Vm')->getVmModel()->id;
                }
                $newIp->fill($ipData)->save();
                $this->hosting()->ipAdd($ip->ip);
                $newIps[] = $ip->ip;
            }
            if(!empty($newIps)){
                (new IpLogService())->assigned($newIps);
            }
            $this->hosting()->save();
            DB::commit();
        } catch (\Exception $ex) {
            DB::rollBack();
            throw $ex;
        }
    }

    public function deleteIpAddresses()
    {

        $query = VmIpAddress::ofHostingId($this->getWhmcsParamByKey('serviceid'))
            ->ofVmId(sl('Vm')->getVmModel()->id);
        if($this->configuration()->isOrderPublicIp()){
            $query ->update(['vm_id'=>null,'net' => null]);
        }else{
            $ipunassigned = [];
            foreach ($query->get() as $vmIp){
                //Unlock ip in Proxmox Addon
                IpAddress::where('hosting_id', $this->getWhmcsParamByKey('serviceid'))
                    ->ofIp($vmIp->ip)
                    ->update(["hosting_id" => "0", 'last_check' => Utility::timeStamp()]);
                $this->hosting()->ipDelete($vmIp->ip);
                $vmIp->delete();
                $ipunassigned[] = $vmIp->ip;
            }
            if(!empty($ipunassigned)){
                if(!empty( $ipunassigned)){
                    (new IpLogService())->unassigned($ipunassigned);
                }
            }
            $this->hosting()->save();
        }

        return $this;
    }

    /**
     * @deprecated
     */
    public function buildLxc()
    {

        $vmId = sl('Vm')->getVmModel()->id;
        $container = [];
        /**
         * @var VmIpAddress[] $ipv4
         */
        $ipv4 = VmIpAddress::ofHostingId($this->getWhmcsParamByKey("serviceid"))
            ->ofIp4()
            ->ofNetNull()
            ->ofVmId($vmId)
            ->get();
        //ip6
        $ipv6 = VmIpAddress::ofHostingId($this->getWhmcsParamByKey("serviceid"))
            ->ofIp6()
            ->ofNetNull()
            ->ofVmId($vmId)
            ->get();

        //Network
        $bridge   = $this->configuration()->getBridge();
        $firewall = $this->configuration()->isNetworkFirewall() ? 1 : 0;
        $ip4Mode  = $this->configuration()->getIpv4NetworkMode();
        $ip6Mode  = $this->configuration()->getIpv6NetworkMode();
        $rate     = null;
        if ($this->getWhmcsConfigOption(ConfigurableOption::NETWORK_RATE, $this->configuration()->getRate()) && $this->getWhmcsConfigOption(ConfigurableOption::NETWORK_RATE, $this->configuration()->getRate()) != "-1")
        {
            $rate = $this->getWhmcsConfigOption(ConfigurableOption::NETWORK_RATE, $this->configuration()->getRate());
        }
        $networkId = 0;
        if (sl('Vm')->hasVm())
        {
            $networkId = sl('Vm')->getVm()->findFreeNetworDeviceId();
        }
        for ($i = 0; $i <= 9; $i++)
        {
            //Empty IP Addresses
            if (!$ipv4->get($i) && !$ipv6->get($i))
            {
                break;
            }
            if ($networkId > 9)
            {
                break;
            }
            $mac = null;
            //Name
            $interface = new NetworkDeviceLxc('net' . $networkId);
            $interface->setName('eth' . $networkId)
                ->setBridge($bridge)
                ->setFirewall($firewall);
            //IP 4
            if ($ipv4->get($i))
            {
                if ($ipv4->get($i)->mac_address)
                {
                    $mac = $ipv4->get($i)->mac_address;
                }
                if ($ipv4->get($i)->tag)
                {
                    $interface->setTag($ipv4->get($i)->tag);
                }
                $interface->setTrunks($ipv4->get($i)->trunks);
                if ($ip4Mode == 'static')
                { //IP & CIRD
                    $interface->setIp($ipv4->get($i)->ip);
                    $interface->setCidr($ipv4->get($i)->cidr);
                    //Gateway
                    $interface->setGw($ipv4->get($i)->gateway);
                }
                $ip = trim($ipv4->get($i)->ip);
                $ipv4->get($i)->net = $interface->getId();
                $ipv4->get($i)->save();
            }
            if ($ip4Mode == "dhcp")
            {
                $interface->setIp("dhcp");
            }
            //IP 6
            if ($ip6Mode == "dhcp")
            {
                $interface->setIp6("dhcp");
            }
            else
            {
                if ($ip6Mode == "slaac")
                {
                    $interface->setIp6("auto");
                }
            }
            if ($ipv6->get($i))
            {  //IP & CIRD
                $ip = trim($ipv6->get($i)->ip);
                if ($ip6Mode == "static")
                {
                    $interface->setIp6($ipv6->get($i)->ip);
                    $interface->setCidr6($ipv6->get($i)->cidr);
                    //Gateway
                    $interface->setGw6($ipv6->get($i)->gateway);
                }
                if ($ipv6->get($i)->tag)
                {
                    $interface->setTag($ipv6->get($i)->tag);
                }
                if ($ipv6->get($i)->trunks)
                {
                    $interface->setTrunks($ipv6->get($i)->trunks);
                }
                if ($mac == null && $ipv6->get($i)->mac_address)
                {
                    $mac = $ipv6->get($i)->mac_address;
                }
                $ipv6->get($i)->net = $interface->getId();
                $ipv6->get($i)->save();
            }
            if ($rate)
            {
                $interface->setRate($rate);
            }
            $interface->setHwaddr($mac);
            $container['net' . $networkId] = $interface->asConfig();
            $networkId++;
            VirtualInterface::ofHostingId($this->getWhmcsParamByKey("serviceid"))
                ->ofVmId($vmId )
                ->ofIp($ip)
                ->update(['net' =>$interface->getId()]);
        }
        return $container;

    }

    /**
     * @deprecated
     */
    public function buildQemu()
    {
        $container = [];
        $vmId = sl('Vm')->getVmModel()->id;
        /**
         * @var VmIpAddress[] $ipv4
         */
        $ipv4 = VmIpAddress::ofHostingId($this->getWhmcsParamByKey("serviceid"))
            ->ofIp4()
            ->ofNetNull()
            ->ofVmId($vmId)
            ->get();
        //ip6
        $ipv6 = VmIpAddress::ofHostingId($this->getWhmcsParamByKey("serviceid"))
            ->ofIp6()
            ->ofNetNull()
            ->ofVmId($vmId)
            ->get();
        //Network
        $model    = $this->configuration()->getNetworkModel();
        $bridge   = $this->configuration()->getBridge();
        $firewall = $this->configuration()->isNetworkFirewall() ? 1 : 0;
        $rate     = null;
        if ($this->getWhmcsConfigOption(ConfigurableOption::NETWORK_RATE, $this->configuration()->getRate()) && $this->getWhmcsConfigOption(ConfigurableOption::NETWORK_RATE, $this->configuration()->getRate()) != "-1")
        {
            $rate = $this->getWhmcsConfigOption(ConfigurableOption::NETWORK_RATE, $this->configuration()->getRate());
        }
        $networkId          = 0;
        $vmNetworkDevices   = [];

        if (sl('Vm')->hasVm())
        {
            $networkId =  sl('Vm')->getVm()->findFreeNetworDeviceId();
            $vmNetworkDevices   =  sl('Vm')->getVm()->getNetworkDevices();
        }
        $i4      = 0;
        $i6      = 0;
        $isApi52 = version_compare($this->api()->getVersion(), "5.2", '>=');
        for ($i = 0; $i <= 31; $i++)
        {
            //Empty IP Addresses
            if (!$ipv4->get($i4) && !$ipv6->get($i6))
            {
                break;
            }
            if ($networkId > 31)
            {
                break;
            }
            if ($this->configuration()->isOneNetworkDevice() && ($i > 0 || count($vmNetworkDevices)))
            {
                VmIpAddress::ofHostingId($this->getWhmcsParamByKey("serviceid"))
                    ->ofNetNull()
                    ->ofVmId($vmId )
                    ->update(["net" => 'net0']);
                break;
            }

            $mac   = null;
            $isIp4 = false;
            //Name
            $interface = new NetworkDeviceKvm('net' . $networkId);
            $interface->setModel($model)
                ->setBridge($bridge)
                ->setFirewall($firewall);
            $ipConfig = new IpConfig('ipconfig' . $networkId);
            //IP 4

            if ($ipv4->get($i4))
            {
                $isIp4 = true;
                $ip = trim($ipv4->get($i4)->ip);
                if (trim($ipv4->get($i4)->mac_address))
                {
                    $mac = $ipv4->get($i4)->mac_address;
                }
                if ($ipv4->get($i4)->tag)
                {
                    $interface->setTag(trim($ipv4->get($i4)->tag));
                }
                $interface->setTrunks(trim($ipv4->get($i4)->trunks));

                $ipConfig->setIp(trim($ipv4->get($i4)->ip));
                $ipConfig->setCidr(trim($ipv4->get($i4)->cidr));
                //Gateway
                $ipConfig->setGw(trim($ipv4->get($i4)->gateway));
                $ipv4->get($i4)->net = $interface->getId();
                $ipv4->get($i4)->save();
                $i4++;
            }
            //ipv6
            if ($ipv6->get($i6) && (!$isIp4 || $interface->getTag() == $ipv6->get($i6)->tag))
            {  //IP & CIRD
                $ip = ttrim($ipv6->get($i6)->ip);
                $ipConfig->setIp6(trim($ipv6->get($i6)->ip));
                $ipConfig->setCidr6(trim($ipv6->get($i6)->cidr));
                //Gateway
                $ipConfig->setGw6(trim($ipv6->get($i6)->gateway));
                if ($ipv6->get($i6)->tag)
                {
                    $tag = $ipv6->get($i6)->tag;
                    $interface->setTag(trim($ipv6->get($i6)->tag));
                }
                if ($ipv6->get($i6)->trunks)
                {
                    $interface->setTrunks(trim($ipv6->get($i6)->trunks));
                }
                if ($mac == null && $ipv6->get($i6)->mac_address)
                {
                    $mac = $ipv6->get($i6)->mac_address;
                }
                $ipv6->get($i6)->net = $interface->getId();
                $ipv6->get($i6)->save();
                $i6++;
            }
            $interface->setRate(trim($rate));
            $interface->setMacAddress(trim($mac));
            $interface->setQueues(trim($this->configuration()->getQueues()));
            $container[$interface->getId()] = $interface->asConfig();
            if ($isApi52 && ($ipConfig->getIp() || $ipConfig->getIp6()))
            {
                $container[$ipConfig->getId()] = $ipConfig->asConfig();
            }
            VirtualInterface::ofHostingId($this->getWhmcsParamByKey("serviceid"))
                ->ofVmId($vmId )
                ->ofIp($ip)
                ->update(['net' =>$interface->getId()]);
            $networkId++;
        }
        return $container;
    }

    /**
     * @throws \Exception
     */
    public function unassignIpAddressesAndDeleteNetwork($requestIPv4, $requestIPv6)
    {
        $vmId = sl('Vm')->getVmModel()->id;
        $ip4 = [];
        $ip6 = [];
        if ($requestIPv4 < 0)
        {
            $ip4 = VmIpAddress::ofHostingId($this->getWhmcsParamByKey('serviceid'))
                ->ofIp4()
                ->ofNetNotNull()
                ->ofVmId($vmId)
                ->limit(abs($requestIPv4))
                ->orderByIdDesc()
                ->get()
                ->all();
        }
        if ($requestIPv6 < 0)
        {
            $ip6 = VmIpAddress::ofHostingId($this->getWhmcsParamByKey('serviceid'))
                ->ofIp6()
                ->ofNetNotNull()
                ->ofVmId($vmId)
                ->limit(abs($requestIPv6))
                ->orderByIdDesc()
                ->get()
                ->all();
        }
        $ipAddresses = array_merge($ip4, $ip6);
        if (!empty($ipAddresses))
        {
            $this->deleteByIpAddresses($ipAddresses);
        }
    }

    public function getIpAddressesRequest()
    {
        $ipv4Used    = (int)VmIpAddress::ofHostingId($this->getWhmcsParamByKey('serviceid'))
            ->ofIp4()
            ->count();
        $ipv6Used    = (int)VmIpAddress::ofHostingId($this->getWhmcsParamByKey('serviceid'))
            ->ofIp6()
            ->count();
        $requestIPv4 = (int)$this->configuration()->getIpv4();
        $requestIPv6 = (int)$this->configuration()->getIpv6();
        if ($this->isWhmcsConfigOption(ConfigurableOption::IPV4))
        {
            $requestIPv4 = (int)$this->getWhmcsConfigOption(ConfigurableOption::IPV4);
        }
        if ($this->isWhmcsConfigOption(ConfigurableOption::IPV6))
        {
            $requestIPv6 = (int)$this->getWhmcsConfigOption(ConfigurableOption::IPV6);
        }
        return [(int)$requestIPv4 -= $ipv4Used, (int)$requestIPv6 -= $ipv6Used];
    }
}