<?php

namespace ModulesGarden\Servers\ProxmoxCloudVps\App\UI\VmUpdate\Providers;


use Illuminate\Database\Capsule\Manager as DB;
use MGProvision\Proxmox\v2\Api;
use MGProvision\Proxmox\v2\models\Kvm;
use MGProvision\Proxmox\v2\models\MountPoint;
use MGProvision\Proxmox\v2\models\NetworkDeviceKvm;
use MGProvision\Proxmox\v2\repository\FileRepository;
use MGProvision\Proxmox\v2\repository\StorageRepository;
use ModulesGarden\ProxmoxAddon\App\Events\Cloud\LxcUpdateEvent;
use ModulesGarden\ProxmoxAddon\App\Events\Cloud\QemuUpdateEvent;
use ModulesGarden\ProxmoxAddon\App\Jobs\Cloud\Agent\ChangePasswordJob;
use ModulesGarden\ProxmoxAddon\App\Jobs\Cloud\Agent\ConfigureNetworkJob;
use ModulesGarden\ProxmoxAddon\App\Models\VirtualInterface;
use ModulesGarden\ProxmoxAddon\App\Models\VmIpAddress;
use ModulesGarden\ProxmoxAddon\App\Models\VmModel;
use ModulesGarden\ProxmoxAddon\App\Services\ApiService;
use ModulesGarden\ProxmoxAddon\App\Services\Cloud\AdditionalDiskService;
use ModulesGarden\ProxmoxAddon\App\Services\Cloud\AdditionalMountPointService;
use ModulesGarden\ProxmoxAddon\App\Services\Cloud\AgentService;
use ModulesGarden\ProxmoxAddon\App\Services\Cloud\IpSetIpFilterService;
use ModulesGarden\ProxmoxAddon\App\Services\Cloud\NetworkService;
use ModulesGarden\ProxmoxAddon\App\Services\Cloud\ProductService;
use ModulesGarden\ProxmoxAddon\App\Services\Cloud\VirtualInterfaceConverter;
use ModulesGarden\Servers\ProxmoxCloudVps\App\UI\VmCreate\Providers\VmCreateProvider;
use ModulesGarden\Servers\ProxmoxCloudVps\Core\UI\ResponseTemplates\HtmlDataJsonResponse;
use function ModulesGarden\ProxmoxAddon\Core\Helper\fire;
use function ModulesGarden\ProxmoxAddon\Core\Helper\queue;
use function ModulesGarden\Servers\ProxmoxCloudVps\Core\Helper\sl;

class VmUpdateProvider extends VmCreateProvider
{
    use ApiService;
    use ProductService;


    /**
     * VmUpdateProvider constructor.
     */
    public function __construct()
    {
        $this->model = VmModel::class;
    }


    public function read()
    {
        $this->vmModel = \ModulesGarden\ProxmoxAddon\Core\Helper\sl('Vm')->getVmModel();
        $this->data =  $this->vmModel->toArray();
        $vm = \ModulesGarden\ProxmoxAddon\Core\Helper\sl('Vm')->getVm();
        $config = $vm->config();
        $this->data['password'] = $this->vmModel->getPassword();
        $this->data['description'] = html_entity_decode($config['description']);
        $this->data['searchdomain'] = $config['searchdomain'];
        $ns = explode(" ",$config['nameserver']);
        $this->data['nameserver[0]'] = $ns[0];
        $this->data['nameserver[1]'] = $ns[1];
        /**
         * @deprecated
        $this->data['ipv4'] = $this->vmModel->ipv4Addresses->count();
        $this->data['ipv6'] = $this->vmModel->ipv6Addresses->count();
         */
        //Boot order
        if($vm instanceof Kvm){
            $bootOrder                           = $vm->getBootOrder();
            $this->data['bootOrder0']            = $bootOrder[0];
            $this->data['bootOrder1']            = $bootOrder[1];
            $this->data['bootOrder2']            = $bootOrder[2];
            $options                             = [
                0   => sl("lang")->tr("None"),
                "c" => sl("lang")->tr("Disk"),
                "d" => sl("lang")->tr("CD-ROM"),
                "n" => sl("lang")->tr("Network"),
            ];
            if(version_compare($this->api()->getVersion(), "6.3", '>=')
            && !in_array("c", (array) $bootOrder )){
                $cdrom = $vm->cdrom();
                $nd = $vm->getNetworkDevices();
                $options                             = [
                    0   => sl("lang")->tr("None"),
                    $vm->getMasterHardDisk()->getId() => sl("lang")->tr("Disk"),
                ];
                if($cdrom['bus']){
                    $options[$cdrom['bus']] = sl("lang")->tr("CD-ROM");
                }
                if($nd[0] instanceof  NetworkDeviceKvm){
                    $options[$nd[0]->getId()] = sl("lang")->tr("Network");
                }
            }
            $this->availableValues['bootOrder0'] = $options;
            $this->availableValues['bootOrder1'] = $options;
            $this->availableValues['bootOrder2'] = $options;
        }
        //sshkeys
        $this->data['sshkeys'] = rawurldecode($vm->config()['sshkeys']);
        //cpu Priority
        if($this->configuration()->hasCpuPriority()){
            for($i=1; $i<=5; $i++){
                $cpuunits = $this->configuration()->get('cpuunitsPriority'.$i);
                $cpulimit = $this->configuration()->get('cpulimitPriority'.$i);
                if($this->vmModel->cpulimit >= $cpulimit){
                    $this->data['cpuPriority'] = $i;
                }
                if($this->vmModel->cpuunits >= $cpuunits){
                    $this->data['cpuPriority'] = $i;
                }
            }
        }
        //iso
        if ($this->configuration()->isQemu() && $this->configuration()->isPermissionIsoImage()) {
            $this->availableValues['iso'] = ["none" => sl('lang')->abtr("None")];
            $storageRepository = new StorageRepository();
            $storageRepository->findByNodes([$vm->getNode()])
                ->findEnabed();
            $storages = $storageRepository->fetchAsArray();
            $isoRepository = new FileRepository();
            $isoRepository->findByNodes([$vm->getNode()])
                ->findByStorages($storages);
            $isoRepository->findIso();
            foreach ($isoRepository->fetch() as $entity) {
                if ($this->configuration()->isPermissionIsoImages() && !in_array($entity->getVolid(), $this->configuration()->getPermissionIsoImages())) {
                    continue;
                }
                $this->availableValues['iso'][$entity->getVolid()] = $entity->getFriendlyName();
            }
            if( $vm->getCdRomRepository()->first()){
                $this->data['iso']= $vm->getCdRomRepository()->first()->getLocation();
            }
        }



    }

    public function update()
    {
        $this->vmModel = \ModulesGarden\ProxmoxAddon\Core\Helper\sl('Vm')->getVmModel();
        $vm = \ModulesGarden\ProxmoxAddon\Core\Helper\sl('Vm')->getVm();
        $this->networkService = new NetworkService();
        $networkService = new NetworkService();
        /**
         * @deprecated
         * $ipv4 = (int) $this->formData['ipv4'] - (int)  $this->vmModel->ipv4Addresses->count();
         * $ipv6  = (int) $this->formData['ipv6'] - (int)  $this->vmModel->ipv6Addresses->count();
         * $networkService->hasIp($ipv4, $ipv6, $this->vmModel->node);
         * $networkService->addIp($ipv4, $ipv6, $this->vmModel->node);
         * $networkService->unassignIpAddressesAndDeleteNetwork($ipv4, $ipv6);
         */

        //load data
        $this->getFormDataValues();
        if ($vm instanceof Kvm)
        {
            //iso
            if ($vm->cdrom())
            {
                $vm->updateCdrom($this->formData['iso']);
            }
            //Boot order
            $bootOrder = null;
            $order = [];
            for ($i = 0; $i <= 2; $i++)
            {
                if ($this->formData['bootOrder' . $i])
                {
                    $bootOrder .= $this->formData['bootOrder' . $i];
                    if(!in_array($this->formData['bootOrder' . $i],["c","d","n"])){
                        $order[] = $this->formData['bootOrder' . $i];
                    }
                }
            }
            if(version_compare($this->api()->getVersion(), "6.3", '>=') && !empty($order)){
                //order=scsi0;ide0;ide1;net0
                $vm->updateConfig(['boot' => "order=".implode(";", $order)]);
            }else{
                $vm->changeBootOrder($bootOrder);
            }
            //sshkeys
            if($this->configuration()->isPermissionSshkeys()){
                if ($this->formData['sshkeys'])
                {
                    $vm->updateConfig(["sshkeys" => rawurlencode($this->formData['sshkeys'])]);
                } else if($vm->config()['sshkeys'])
                {
                    $vm->deleteConfig('sshkeys');
                }
            }
        }
        try{
            Api::beginTransaction();
            DB::beginTransaction();
            $this->fillVmModel();
            if($this->getFormDataValues()['password']){
                $this->vmModel->setPassword($this->getFormDataValues()['password']);
            }
            if($this->configuration()->isCalculateSocketsAndCores()){
                $this->calculateSocketsAndCores();
            }
            $this->vmModel->save();
            if($this->configuration()->isQemu()){
                fire(new QemuUpdateEvent($this->vmModel, $this->formData));
            }
            if($this->configuration()->isLxc()){
                fire(new LxcUpdateEvent($this->vmModel, $this->formData));
            }
            if ($this->configuration()->isDetailsCombinedView()) {
                //virtual interfaces
                $this->virtualNetworkUpdate();
                //IP Set
                if ($this->configuration()->isIpsetIpFilter()){
                    $ipSetFilterService =  new IpSetIpFilterService();
                    $ipSetFilterService->create();
                }
                //disks
                $this->disksUpdate();

            }
            $agentEnabled = (new AgentService())->isEnabled();
            if($agentEnabled && $this->configuration()->isAgentConfigureNetwork()){
                queue(ConfigureNetworkJob::class,
                    ['hostingId' => $this->getWhmcsParamByKey('serviceid')],
                    null,
                    "hosting",
                    $this->getWhmcsParamByKey("serviceid"),
                    $this->vmModel->id
                );
            }
            if($agentEnabled && $this->configuration()->isAgentPassword()){
                queue(ChangePasswordJob::class,
                    ['hostingId' => $this->getWhmcsParamByKey('serviceid')],
                    null,
                    "hosting",
                    $this->getWhmcsParamByKey("serviceid"),
                    $this->vmModel->id
                );
            }
            DB::commit();
            return (new  HtmlDataJsonResponse())
                ->setMessageAndTranslate('Server has been updated')
                ->setCallBackFunction('pcVmUpdatedAjaxDone')
                ->addRefreshTargetId('serviceInformationDataTable')
                ->setCallBackFunction('mgLocationReload');
        }catch (\Exception $ex){
            DB::rollBack();
            Api::commit();
            return (new  HtmlDataJsonResponse())
                ->setStatusError()
                ->setMessageAndTranslate($ex->getMessage());
        }

    }

    private function virtualNetworkUpdate()
    {
        try {

            $viIds = [];
            //delete
            for ($i = 0; $i <= 20; $i++) {
                if ($this->formData['virtualInterface' . $i]) {
                    $viIds[] = $this->formData['virtualInterface' . $i]['id'];
                }
            }
            $virtualInterfaces = VirtualInterface::ofHostingId($this->getWhmcsParamByKey('serviceid'))
                ->ofVmId($this->vmModel->id);
            if ($viIds) {
                $virtualInterfaces->notId($viIds);
            }
            $this->networkService->deleteByIpAddresses($virtualInterfaces->get());
            DB::commit();
            DB::beginTransaction();
            //create
            $newVi = [];
            for ($i = 1; $i <= 20; $i++) {
                if (!$this->formData['virtualNetwork' . $i]) {
                    continue;
                }
                $ip = $this->formData['virtualNetwork' . $i]['ip'];
                $virtualNetworkId = $this->formData['virtualNetwork' . $i]['id'];
                //lock public ip
                if ($virtualNetworkId == 'public') {
                    VmIpAddress::ofHostingId($this->getWhmcsParamByKey('serviceid'))
                        ->ofIp($ip)
                        ->update(['vm_id' => $this->vmModel->id]);
                }
                $vi = new VirtualInterface();
                $vi->ip = $ip;
                $vi->ip_long = ip2long($ip);
                $vi->vm_id = $this->vmModel->id;
                $vi->vn_id = $virtualNetworkId == "public" ? 0 : $virtualNetworkId;
                $vi->hosting_id = $this->getWhmcsParamByKey('serviceid');
                $vi->save();
                $newVi[] = $vi;
                $viIds[] = $vi->id;
            }
            if ($newVi) {
                $config = (new VirtualInterfaceConverter($newVi, \ModulesGarden\ProxmoxAddon\Core\Helper\sl('Vm')->getVm()))->asConfig();
                \ModulesGarden\ProxmoxAddon\Core\Helper\sl('Vm')->getVm()->updateConfig($config);
            }
            DB::commit();
        } catch (\Exception $ex) {
            DB::rollBack();
            Api::commit();
            throw $ex;
        }
    }

    private function disksUpdate()
    {
        $vm = \ModulesGarden\ProxmoxAddon\Core\Helper\sl('Vm')->getVm();
        $vmModel  = \ModulesGarden\ProxmoxAddon\Core\Helper\sl('Vm')->getVmModel();
        $additonalDiskService = $this->configuration()->isQemu() ? new AdditionalDiskService() : new AdditionalMountPointService();
        //Update
        $diskIds=[];
        $resizeTask = null;
        for ($i = 0; $i <= 20; $i++) {
            if ($this->formData['disks' . $i]) {
                $id = $this->formData['disks' . $i]['id'];
                $size = $this->formData['disks' . $i]['size'];
                $backup = $this->formData['disks' . $i]['backup'] == "on" ? null : 0;
                $mp =  $this->formData['disks' . $i]['mp'];
                $hdd = $vm->getHardDiskRepostiory()->findById($id);
                if($resizeTask && $this->configuration()->isLxc()){
                    for($i=0;  $i<=15; $i++){
                        if($vm->node()->task($resizeTask)->isDone()){
                            $resizeTask = null;
                            break;
                        }
                        sleep(1);
                    }
                }
                //Backup
                $hdd->setBackup($backup);
                //mp
                if($hdd instanceof MountPoint){
                    $hdd->setBackup($this->formData['disks' . $i]['backup'] == "on" ? 1 : null);
                    $hdd->setMp($mp);
                }
                $hdd->update();
                //resize
                if ((int)$hdd->getGb() < (int)$size)
                {
                    $size = "+" . abs((int)$size - (int)$hdd->getGb()) . "G";
                    $resizeTask = $hdd->resize($size);
                }
                $diskIds[] = $id;
            }
        }
        //delete
        foreach ($vm->getHardDiskRepostiory()->fetch() as $disk){
            if(!$disk->isMaster() && !in_array($disk->getId() ,$diskIds)){
                $disk->delete();
            }
        }
        $vm->getHardDiskRepostiory()->deleteUnused();
        //Create
        $additonalDiskService->create($this->formData);
        //Update disk size
        $vmModel->disks = $vm->getHardDiskRepostiory()->reset()->additionalSize();
        $vmModel->save();

    }


}