<?php

namespace ModulesGarden\ProxmoxAddon\App\Jobs\Vps\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\Vps\LxcUpdateEvent;
use ModulesGarden\ProxmoxAddon\App\Events\Vps\QemuUpdateEvent;
use ModulesGarden\ProxmoxAddon\App\Events\Vps\VmReinstalledEvent;
use ModulesGarden\ProxmoxAddon\App\Jobs\Vps\BaseJob;
use ModulesGarden\ProxmoxAddon\App\Models\Job;
use ModulesGarden\ProxmoxAddon\App\Models\VmIpAddress;
use ModulesGarden\ProxmoxAddon\App\Services\Vps\AgentService;
use ModulesGarden\ProxmoxAddon\App\Services\Vps\ContainerService;
use ModulesGarden\ProxmoxAddon\App\Services\Vps\HighAvailabilityClusterService;
use ModulesGarden\ProxmoxAddon\App\Services\Vps\HostingService;
use ModulesGarden\ProxmoxAddon\App\Services\Vps\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;
    use HostingService;

    /**
     * @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();
        //is done
        if ($this->isTask() && $this->getTask()->isDone()) {

            //update password
            if ($this->getModelData()['password']   ) {
                if($this->params['customfields']['cipassword']){
                    $this->customFieldUpdate('cipassword', $this->getModelData()['password']);
                    $this->params['customfields']['cipassword'] = decrypt($this->getModelData()['password']);
                }else{
                    $this->hosting()->update(["password" => $this->getModelData()['password']]);
                    $this->params['password'] = decrypt($this->getModelData()['password']);
                }
                sl("whmcsParams")->setParams($this->params);
                \ModulesGarden\Servers\ProxmoxVps\Core\Helper\sl("whmcsParams")->setParams($this->params);
            }
            //reinstalled event
            if ($this->configuration()->isLxc()) {
                fire(new LxcUpdateEvent($this->vm()));
            } elseif ($this->configuration()->isQemu()) {
                //Reset IP addresses usage
                VmIpAddress::ofHostingId($this->getWhmcsParamByKey("serviceid"))
                    ->update([
                        'net'   => null
                    ]);
                $this->deleteNetwork();
                if(!$this->vm()->isRunning()){
                    $this->deleteNetwork();
                }
                fire(new QemuUpdateEvent($this->vm()));
                //Agent
                try{
                    if($this->agentService->isEnabled()){
                        if(!$this->vm()->isRunning()){
                            $this->log->info(sprintf("VM %s - Start", $this->vm()->getVmid()));
                            $this->vm()->start();
                            $this->sleep(40);
                            return false;
                        }
                        $this->vm()->agent()->ping();
                        $this->agentService ->getUserAndUpdate();
                        $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;
                }
            }
            //restoreFirewallRules
            $this->restoreFirewallRules();
            fire(new VmReinstalledEvent($this->vm()));
            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['lxc'], $container['parent']);
                //pool
                if ($this->configuration()->getPool())
                {
                    $container['pool'] = $this->configuration()->getPool();
                }
                $node = new Node($this->vm()->getNode());
                $node->setApi($this->api());
                $taskId = $node->lxc()->create($container);
                //save task id
                $this->putModelDataAndSave(["taskId" => $taskId, "node" => $this->vm()->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 = $this->vm()->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" => $this->vm()->getVmid(),
                    "full" => $this->configuration()->getCloneMode(),
                    "target" => $this->vm()->getNode()
                ];
                //description
                if ($this->configuration()->getDescription()) {
                    $container['description'] = $this->containerService->description();
                }
                //hostname
                $hostname = $this->containerService->hostname();
                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" => $this->vm()->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($this->vm())
            ->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 ($this->vm()->getNetworkDevices() as $networkDevice){
            if(VmIpAddress::ofHostingId($this->getWhmcsParamByKey('serviceid'))->ofNet($networkDevice->getId())->count()){
                continue;
            }
            $deleteNetwork[]=$networkDevice->getId();
        }
        if(!empty($deleteNetwork)){
            $this->vm()->deleteConfig(implode(",",$deleteNetwork));
        }
    }

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