<?php

namespace ModulesGarden\ProxmoxAddon\App\Jobs\Vps;

use Illuminate\Database\Capsule\Manager as DB;
use MGProvision\Proxmox\v2\Api;
use MGProvision\Proxmox\v2\models\Kvm;
use MGProvision\Proxmox\v2\ProxmoxApiException;
use MGProvision\Proxmox\v2\repository\ClusterResourcesRepository;
use ModulesGarden\ProxmoxAddon\App\Enum\Vps\CustomField;
use ModulesGarden\ProxmoxAddon\App\Events\Vps\QemuUpdateEvent;
use ModulesGarden\ProxmoxAddon\App\Events\Vps\VmCreatedEvent;
use ModulesGarden\ProxmoxAddon\App\Factory\Ssh2Factory;
use ModulesGarden\ProxmoxAddon\App\Models\CloudInitScript;
use ModulesGarden\ProxmoxAddon\App\Models\TaskHistory;
use ModulesGarden\ProxmoxAddon\App\Models\VmIpAddress;
use ModulesGarden\ProxmoxAddon\App\Providers\SnippetProvider;
use ModulesGarden\ProxmoxAddon\App\Repositories\ServerConfigurationRepository;
use ModulesGarden\ProxmoxAddon\App\Services\CloudInitScriptConveter;
use ModulesGarden\ProxmoxAddon\App\Services\Vps\ProductService;
use ModulesGarden\ProxmoxAddon\App\Services\Vps\UserService;
use ModulesGarden\ProxmoxAddon\App\Enum\Vps\ConfigurableOption;
use function ModulesGarden\ProxmoxAddon\Core\Helper\fire;

class CloneQemuJob extends BaseJob
{

    use ProductService;
    use UserService;

    public function handle()
    {
        $this->initParams();
        $this->initServices();
        $this->setHostingId($this->getWhmcsParamByKey("serviceid"));
        //create task validation
        if ($this->isDone())
        {
            $this->qemuUpdate();
            if($this->agentUpdate()){
                fire(new VmCreatedEvent($this->vm()));
                return true;
            }
            return false;
        }
        elseif ($this->isTaskRunning())
        {
            //sleep
            $this->sleep(5);
            return false;
        }
        try
        {
            Api::beginTransaction();
            DB::beginTransaction();
            //node
            $node = $this->getWhmcsCustomField(CustomField::NODE) ?  $this->getWhmcsCustomField(CustomField::NODE) :$this->getNode()->getNode();
            //os template
            $osTemplate = $this->getWhmcsConfigOption(ConfigurableOption::OS_TEMPLATE, $this->configuration()->getOsTemplate() );
            //Support for configurable options i.e vmname|OS Name
            if (is_string($osTemplate) && !preg_match('/\//', $osTemplate))
            {
                $clusterRepository = new ClusterResourcesRepository();
                $clusterRepository->setApi($this->api());
                if(!$this->configuration()->isOsTemplatesInAllNodes()){
                    $clusterRepository->findByNodes([$node]);
                }
                $clusterRepository->findKvmTemplate();
                foreach ($clusterRepository->fetch() as $resurce)
                {
                    if ($resurce->getName() == $osTemplate)
                    {
                        $templateVmid = $resurce->getVmid();
                        $templateNode = $resurce->getNode();
                        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
            }
            else
            {
                if (preg_match('/\//', $osTemplate))
                {
                    list($templateNode, $templateVmid) = explode("/", $osTemplate);
                }
            }
            //vmid
            $vmid = $this->nextVmid();
            $this->customFieldUpdate("vmid", $vmid);
            $this->customFieldUpdate("node", $node);
            //init container
            $container = [
                "newid"  => $vmid,
                "full"   => $this->configuration()->getCloneMode(),
                "target" => $node
            ];
            //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();
            }
            if($this->getWhmcsConfigOption(ConfigurableOption::STORAGE)){
                list($storage,$diskSize) = explode(":", $this->getWhmcsConfigOption(ConfigurableOption::STORAGE),2);
                $container['storage'] = $storage;
            }
            //pool
            if ($this->configuration()->getPool())
            {
                $container['pool'] = $this->configuration()->getPool();
            }
            //bwlimit
            if($this->configuration()->getBwLimit()){
                $container['bwlimit'] = $this->configuration()->getBwLimit();
            }
            //Create
            $template = new Kvm($templateNode, $templateVmid);
            $template->setApi($this->api());
            $taskId = $template->cloneVm($container);
            DB::commit();
        }
        catch (\Exception $ex)
        {
            DB::rollBack();
            Api::commit();
            $this->failed($ex->getMessage());
            throw $ex;
        }
        //task history
        $task = new TaskHistory();
        $task->fill([
            'hosting_id' => $this->getWhmcsParamByKey("serviceid"),
            'upid'       => $taskId,
            'name'       => sprintf("VM %s - %s", $vmid, "Clone"),
            'vmid'       => $template->getVmid(),
            'node'       => $template->getNode(),
            'status'     => 0
        ])->save();
        //save task id
        $this->putModelDataAndSave(["taskId" => $taskId, "node" => $template->getNode(), "templateNode" => $templateNode]);
        //sleep
        $this->sleep();
        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 qemuUpdate(){
        if(!$this->vm()->isRunning()){
            $this->deleteNetwork();
        }
        if($this->configuration()->isAdditionalDisk() &&   !$this->additionalDiskService->hasDisk() ){
            $this->additionalDiskService->create();
        }
        fire(new QemuUpdateEvent($this->vm()));
    }

    protected function agentUpdate(){
        try{
            if($this->agentService->isEnabled()){
                if(!$this->vm()->isRunning()){
                    $this->log->info(sprintf("VM %s - Start", $this->vm()->getVmid()));
                    $this->vm()->start();
                    $this->sleep(5);
                    return false;
                }
                $this->vm()->agent()->ping();
                $this->agentService ->getUserAndUpdate();
                $this->agentService ->passwordUpdate();
                $this->agentService ->configureNetwork();
            }
            return true;
        }catch (ProxmoxApiException $ex){
            if(preg_match("/not running/", $ex->getMessage())){
                $this->log->info($ex->getMessage());
            }else{
                $this->log->error($ex->getMessage());
            }
            //sleep
            $this->sleep(5);
            return false;
        }
    }

}
