<?php

namespace ModulesGarden\ProxmoxAddon\App\Jobs\Cloud\Reinstall;

use MGProvision\Proxmox\v2\models\FirewallRule;
use MGProvision\Proxmox\v2\models\Kvm;
use MGProvision\Proxmox\v2\models\Node;
use MGProvision\Proxmox\v2\ProxmoxApiException;
use MGProvision\Proxmox\v2\repository\ClusterResourcesRepository;
use MGProvision\Proxmox\v2\repository\FileRepository;
use ModulesGarden\ProxmoxAddon\App\Events\Cloud\LxcUpdateEvent;
use ModulesGarden\ProxmoxAddon\App\Events\Cloud\QemuUpdateEvent;
use ModulesGarden\ProxmoxAddon\App\Events\Cloud\VmReinstalledEvent;
use ModulesGarden\ProxmoxAddon\App\Jobs\BaseJob;
use ModulesGarden\ProxmoxAddon\App\Models\Job;
use ModulesGarden\ProxmoxAddon\App\Models\VmIpAddress;
use ModulesGarden\ProxmoxAddon\App\Services\Cloud\AgentService;
use ModulesGarden\ProxmoxAddon\App\Services\Cloud\ContainerService;
use ModulesGarden\ProxmoxAddon\App\Services\Cloud\HighAvailabilityClusterService;
use ModulesGarden\ProxmoxAddon\App\Services\Cloud\ProductService;
use function ModulesGarden\ProxmoxAddon\Core\Helper\fire;
use function ModulesGarden\ProxmoxAddon\Core\Helper\queue;
use function ModulesGarden\ProxmoxAddon\Core\Helper\sl;

class CreateVmJob extends BaseJob
{
    use ProductService;

    /**
     * @var  HighAvailabilityClusterService
     */
    protected $highAvailabilityClusterService;

    protected function initServices()
    {
        $this->highAvailabilityClusterService = new HighAvailabilityClusterService();
        $this->containerService = new ContainerService();
        $this->agentService = new AgentService();
    }

    public function handle($text = null)
    {
        $this->initParams();
        $this->initServices();
        $this->initVm();
        //is done
        if ($this->isTask() && $this->getTask()->isDone()) {

            //update password
            if ($this->getModelData()['password']) {
                $vmModel =   $this->getVmModel();
                $vmModel->password = $this->getModelData()['password'];
                $vmModel->save();
            }
            //reinstalled event
            if ($this->configuration()->isLxc()) {
                fire(new LxcUpdateEvent($this->getVmModel()));
            } elseif ($this->configuration()->isQemu()) {
                $this->deleteNetwork();
                if(!sl('Vm')->getVm()->isRunning()){
                    $this->deleteNetwork();
                }
                fire((new QemuUpdateEvent($this->getVmModel()))->setChangeVmPassword(true));
                //Agent
                try{
                    if($this->agentService->isEnabled()){
                        if(!sl('Vm')->getVm()->isRunning()){
                            $this->log->info(sprintf("VM %s - Start", sl('Vm')->getVm()->getVmid()));
                            sl('Vm')->getVm()->start();
                            $this->sleep(40);
                            return false;
                        }
                        sl('Vm')->getVm()->agent()->ping();
                        $this->agentService ->passwordUpdate();
                        $this->agentService ->configureNetwork();
                    }
                }catch (ProxmoxApiException $ex){
                    if(preg_match("/not running/", $ex->getMessage())){
                        $this->log->info($ex->getMessage());
                    }else{
                        $this->log->error($ex->getMessage());
                    }
                    //sleep
                    $this->sleep(30);
                    return false;
                }
            }
            $this->restoreFirewallRules();
            fire(new VmReinstalledEvent($this->getVmModel()));
            return true;
        } else if ($this->isTask() && $this->getTask()->isRunning()) {
            $this->sleep(20);
            return false;
        } else if ($this->isTask() && $this->getTask()->isFalied()) {
            if ($this->configuration()->isBackupVmBeforeReinstall()) {
                $this->log->error($this->getTask()->getExitstatus());
                $this->restoreJob();
                $this->model->setStatus(Job::STATUS_FAILED);
                return;
            }
            throw new \Exception($this->getTask()->getExitstatus());
        } //create vm
        else if (!$this->isTask() && $this->configuration()->isLxc()) {
            try {
                $container = $this->getParentModelData()['container'];
                unset($container['mp1'],$container['mp2']);
                unset($container['lxc'], $container['parent']);
                //pool
                if ($this->configuration()->getPool()) {
                    $container['pool'] = $this->configuration()->getPool();
                }
                $node = new Node(sl('Vm')->getVm()->getNode());
                $node->setApi($this->api());
                $taskId = $node->lxc()->create($container);
                //save task id
                $this->putModelDataAndSave(["taskId" => $taskId, "node" => sl('Vm')->getVm()->getNode()]);
                //task history
                $this->createTaskHistory($taskId, "Create");
            } catch (\Exception $ex) {
                $this->log->error($ex->getMessage());
                $this->restoreJob();
                return false;
            }
            //clone vm
        } else if (!$this->isTask() && $this->configuration()->isQemu()) {
            try {
                $osTemplate = $this->getModelData()['osTemplate'];
                //Support for configurable options i.e vmname|OS Name
                if (is_string($osTemplate) && !preg_match('/\//', $osTemplate)) {
                    $templateNode = sl('Vm')->getVm()->getNode();
                    $clusterRepository = new ClusterResourcesRepository();
                    $clusterRepository->setApi($this->api());
                    $clusterRepository->findByNodes([$templateNode])
                        ->findKvmTemplate();
                    foreach ($clusterRepository->fetch() as $resurce) {
                        if ($resurce->getName() == $osTemplate && $templateNode == $resurce->getNode()) {
                            $templateVmid = $resurce->getVmid();
                            break;
                        }
                    }
                    if (!$templateVmid) {
                        throw new \Exception(sprintf("Unable to find KVM  template: %s on node: %s", $osTemplate, $templateNode));
                    }
                    //Support for configurable options like nodename/vmid|OS Name
                } elseif (preg_match('/\//', $osTemplate)) {
                        list($templateNode, $templateVmid) = explode("/", $osTemplate);
                    }
                //init container
                $container = [
                    "newid" => sl('Vm')->getVm()->getVmid(),
                    "full" => $this->configuration()->getCloneMode(),
                    "target" => sl('Vm')->getVm()->getNode()
                ];
                //description
                if ($this->getModelData()['description'])
                {
                    $container['description'] = $this->getModelData()['description'];
                }
                //hostname
                $hostname = $this->getVmModel()->name;
                if ($hostname) {
                    $container['name'] = $hostname;
                }
                //Storage
                if (!$this->configuration()->isCloneOnTheSameStorage() && $this->configuration()->getDiskStorage() && $this->configuration()->getCloneMode() == "1") {
                    $container['storage'] = $this->configuration()->getDiskStorage();
                }
                //pool
                if ($this->configuration()->getPool()) {
                    $container['pool'] = $this->configuration()->getPool();
                }
                //Create
                $template = new Kvm($templateNode, $templateVmid);
                $template->setApi($this->api());
                $taskId = $template->cloneVm($container);

                //save task id
                $this->putModelDataAndSave(["taskId" => $taskId, "node" => sl('Vm')->getVm()->getNode(), "templateNode" => $templateNode]);
                //task history
                $this->createTaskHistory($taskId, "Create");
            } catch (\Exception $ex) {
                if ($this->configuration()->isBackupVmBeforeReinstall()) {
                    $this->log->error($ex->getMessage());
                    $this->restoreJob();
                    $this->model->setStatus(Job::STATUS_FAILED);
                    return;
                }
                throw $ex;
            }
        }
        $this->sleep(20);
        return false;
    }

    private function restoreJob()
    {
        //create restore job
        $storage = $this->configuration()->getBackupStorage() ? $this->configuration()->getBackupStorage() : 'local';
        $fileRepository = new FileRepository();
        $fileRepository->findBackup(sl('Vm')->getVm())
            ->findByStorages([$storage]);
        if (!$fileRepository->count()) {
            return;
        }
        queue(RestoreVm::class, [], null, "hosting", $this->getWhmcsParamByKey("serviceid"));
        $this->model->setStatus(Job::STATUS_CANCELLED)->save();
        return false;
    }

    private function  deleteNetwork(){

        $deleteNetwork = [];
        foreach (sl('Vm')->getVm()->getNetworkDevices() as $networkDevice){
            if(VmIpAddress::ofHostingId($this->getWhmcsParamByKey('serviceid'))
                ->ofVmId(sl('Vm')->getVmModel()->id)
                ->ofNet($networkDevice->getId())->count()){
                continue;
            }
            $deleteNetwork[]=$networkDevice->getId();
        }
        if(!empty($deleteNetwork)){
            sl('Vm')->getVm()->deleteConfig(implode(",",$deleteNetwork));
        }
    }

    protected function restoreFirewallRules(){
        foreach ($this->getModelData()['firewallRules'] as $rule){
            $firewallRule = new FirewallRule();
            $firewallRule->setApi($this->api());
            $firewallRule->setPath(sl('Vm')->getVm()->getPath() . "/firewall/rules/");
            $firewallRule->setAttributes($rule);
            $firewallRule->create();
        }
    }

}