<?php

/* * ********************************************************************
 * ProxmoxVPS product developed. (Apr 19, 2019)
 * *
 *
 *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
 *  CONTACT                        ->       contact@modulesgarden.com
 *
 *
 * This software is furnished under a license and may be used and copied
 * only  in  accordance  with  the  terms  of such  license and with the
 * inclusion of the above copyright notice.  This software  or any other
 * copies thereof may not be provided or otherwise made available to any
 * other person.  No title to and  ownership of the  software is  hereby
 * transferred.
 *
 *
 * ******************************************************************** */

namespace ModulesGarden\ProxmoxAddon\App\Cron;

use MGProvision\Proxmox\v2\VmFactory;
use ModulesGarden\ProxmoxAddon\App\Jobs\Vps\NetworkRate\MaximumVmRateJob;
use ModulesGarden\ProxmoxAddon\App\Jobs\Vps\NetworkRate\MinimumVmRateJob;
use ModulesGarden\ProxmoxAddon\App\Models\ModuleSetting;
use ModulesGarden\ProxmoxAddon\App\Models\ProductConfiguration;
use ModulesGarden\ProxmoxAddon\App\Models\VmModel;
use ModulesGarden\ProxmoxAddon\App\Models\Whmcs\Hosting;
use ModulesGarden\ProxmoxAddon\App\Services\ApiService;
use ModulesGarden\ProxmoxAddon\App\Services\Utility;
use ModulesGarden\ProxmoxAddon\App\Services\Vps\ProductService;
use ModulesGarden\ProxmoxAddon\Core\Api\Whmcs;
use ModulesGarden\ProxmoxAddon\Core\CommandLine\Command;
use ModulesGarden\ProxmoxAddon\Core\CommandLine\Hypervisor;
use ModulesGarden\ProxmoxAddon\Core\Models\Whmcs\Admins;
use ModulesGarden\ProxmoxAddon\Core\Models\Whmcs\Server;
use ModulesGarden\ProxmoxAddon\Core\UI\Traits\WhmcsParams;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use function ModulesGarden\ProxmoxAddon\Core\Helper\queue;
use function ModulesGarden\ProxmoxAddon\Core\Helper\sl;
use ModulesGarden\ProxmoxAddon\App\Enum\Vps;
use ModulesGarden\ProxmoxAddon\App\Enum\Cloud;

/**
 * Description of Users
 *
 * @author Pawel Kopec <pawelk@modulesgardne.com>
 */
class UsageUpdate extends Command
{
    use WhmcsParams;
    use ProductService;
    use ApiService;

    /**
     * Command name
     * @var string
     */
    protected $name = 'update-server-usage';

    /**
     * Command description
     * @var string
     */
    protected $description = 'Updating Disk & Bandwidth Usage Stat';

    /**
     * Command help text
     * @var string
     */
    protected $help = '';

    protected function process(InputInterface $input, OutputInterface $output, SymfonyStyle $io)
    {
        $io->title('Update Server Usage: Starting');
        if (!function_exists('ModuleBuildParams'))
        {
            require_once ROOTDIR . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . "modulefunctions.php";
        }
        //Update hosting date 0000-00-00 00:00:00 to now
        $this->updateEmptyLastUpdate();
        //init bandwidth reset date to run daily cron job
        $this->updateEmptyBandwidthResetDate();
        //Run daily
        if($this->isDailyJob()){
            //Reset BW
            $this->resetBandwidth();
            $this->unsuspendOverageUsage();
            $this->endDailyJob();
        }
        //Update hosting bandwidth
        $h        = (new Hosting())->getTable();
        $s        = (new Server())->getTable();
        $hostings = Hosting::select("{$h}.id", "{$h}.packageid", "{$h}.lastupdate", "{$h}.bwusage", "{$h}.bwlimit", "{$h}.regdate")
            ->rightJoin($s, "{$h}.server", '=', "{$s}.id")
            ->where("{$h}.domainstatus", "Active")
            ->whereIn("{$s}.type", ["proxmoxVPS", "ProxmoxCloudVps"])
            ->orderBy("{$h}.server");
        $i        = 0;
        /**
         * @var Hosting $hosting
         */
        foreach ($hostings->get() as $hosting)
        {
            $i++;
            $output->writeln(sprintf("Synchronize hosting: %s", $hosting->id));
            try
            {
                $params = \ModuleBuildParams($hosting->id);
                sl("whmcsParams")->setParams($params);
                unset($this->vm, $this->api, $this->configuration, $this->productId, $this->hostingId);
                $this->api();
                if ($params['moduletype'] == "proxmoxVPS"){
                    if (!$params['customfields'][Vps\CustomField::VMID])
                    {
                        throw new \Exception("Custom Field \"vmid\" is empty");
                    }
                    if (!$params['customfields'][Vps\CustomField::NODE])
                    {
                        throw new \Exception("Custom Field \"node\" is empty");
                    }
                    $status    = $this->vm()->status();
                    $rrData    = $this->vm()->rrdData(["timeframe" => "hour", "cf" => "AVERAGE"]);
                    $bandwidth = 0;
                    foreach ($rrData as $k => $rrd)
                    {
                        if ($rrd['time'] < $hosting->getLastUpdate()->getTimestamp())
                        {
                            continue;
                        }
                        $total = ((float)$rrd['netin'] + (float)$rrd['netout']);
                        if ($total > 1000000000)
                        {
                            $total = 0;
                        }
                        $bandwidth    += ($total * 57.3);
                        $bandwithTime = date("Y-m-d H:i:s", $rrd['time']);
                    }
                    if (!$bandwithTime)
                    {
                        continue;
                    }
                    $diskUsage = (int)$status['disk'];
                    Utility::unitFormat($bandwidth, "bytes", "mb");
                    Utility::unitFormat($diskUsage, "bytes", "mb");
                    $hosting->bwusage += $bandwidth;
                    $hosting->update(
                        [
                            "diskusage"  => $diskUsage,
                            "bwusage"    => $hosting->bwusage,
                            "lastupdate" => $bandwithTime
                        ]
                    );
                    $rate = null;
                    if ($this->getWhmcsConfigOption(Vps\ConfigurableOption::NETWORK_RATE, $this->configuration()->getRate())  &&  $this->getWhmcsConfigOption(Vps\ConfigurableOption::NETWORK_RATE, $this->configuration()->getRate()) !="-1")
                    {
                        $rate = $this->getWhmcsConfigOption(Vps\ConfigurableOption::NETWORK_RATE, $this->configuration()->getRate());
                    }
                    //Minimum VM Network Rate
                    if ($hosting->isBandwidthOverageUsage() && $this->configuration()->getMinimumRate() &&
                        $this->vm()->getNetworkDevices($this->configuration()->getBridge())[0] &&
                        $this->vm()->getNetworkDevices($this->configuration()->getBridge())[0]->getRate() != $this->configuration()->getMinimumRate())
                    {
                        queue(MinimumVmRateJob::class, [], null, "hosting", $hosting->id);
                        //Maximum VM Network Rate
                    } else if (!$hosting->isBandwidthOverageUsage() &&
                        $this->configuration()->getMinimumRate() &&
                        $this->vm()->getNetworkDevices($this->configuration()->getBridge())[0] &&
                        $this->vm()->getNetworkDevices($this->configuration()->getBridge())[0]->getRate() != $rate) {
                        queue(MaximumVmRateJob::class, [], null, "hosting", $hosting->id);
                    }
                    //Suspend On Bandwidth Overage
                    if ($this->configuration()->isSuspendOnBandwidthOverage() && $hosting->domainstatus != "Suspended" && $hosting->isBandwidthOverageUsage())
                    {
                        $localApi = new Whmcs(new Admins());
                        $localApi->call("modulesuspend", ["accountid" => $hosting->id, "suspendreason" => "Overage Usage"]);
                        $output->writeln(sprintf("Hosting: %s has been suspended, reanson: Overage Usage", $hosting->id));
                    }
                    $output->writeln(sprintf("Hosting: %s has been synchronized, bandwith usage: %s MB", $hosting->id, $bandwidth));
                }
                if ($params['moduletype'] == "ProxmoxCloudVps"){
                    $bandwidth = 0;
                    $diskUsage = 0;
                    $rate = null;
                    if ($this->getWhmcsConfigOption(Cloud\ConfigurableOption::NETWORK_RATE, $this->configuration()->getRate())  &&  $this->getWhmcsConfigOption(Cloud\ConfigurableOption::NETWORK_RATE, $this->configuration()->getRate()) !="-1")
                    {
                        $rate = $this->getWhmcsConfigOption(Cloud\ConfigurableOption::NETWORK_RATE, $this->configuration()->getRate());
                    }
                    $vservers = VmModel::ofHostingId($hosting->id);
                    foreach ($vservers->get() as $vserver)
                    {
                        $vm = (new VmFactory())->fromVmModel($vserver);
                        $status    = $vm->status();
                        $diskUsage += (int)$status['disk'];
                        $rrData    = $vm->rrdData(["timeframe" => "hour", "cf" => "AVERAGE"]);
                        foreach ($rrData as $k => $rrd)
                        {
                            if ($rrd['time'] < $hosting->getLastUpdate()->getTimestamp())
                            {
                                continue;
                            }
                            $total = ((float)$rrd['netin'] + (float)$rrd['netout']);
                            if ($total > 1000000000)
                            {
                                $total = 0;
                            }
                            $bandwidth    += ($total * 57.3);
                            $bandwithTime = date("Y-m-d H:i:s", $rrd['time']);
                        }
                        if (!$bandwithTime)
                        {
                            continue;
                        }

                        //Minimum VM Network Rate
                        if ($hosting->isBandwidthOverageUsage() && $this->configuration()->getMinimumRate() &&
                            $vm->getNetworkDevices($this->configuration()->getBridge())[0] &&
                            $vm->getNetworkDevices($this->configuration()->getBridge())[0]->getRate() != $this->configuration()->getMinimumRate())
                        {
                            queue(\ModulesGarden\ProxmoxAddon\App\Jobs\Cloud\NetworkRate\MinimumVmRateJob::class, [], null, "hosting", $hosting->id, $vserver->id);
                            //Maximum VM Network Rate
                        } else if (!$hosting->isBandwidthOverageUsage() &&
                            $this->configuration()->getMinimumRate() &&
                            $vm->getNetworkDevices($this->configuration()->getBridge())[0] &&
                            $vm->getNetworkDevices($this->configuration()->getBridge())[0]->getRate() != $rate) {
                            queue(\ModulesGarden\ProxmoxAddon\App\Jobs\Cloud\NetworkRate\MaximumVmRateJob::class, [], null, "hosting", $hosting->id, $vserver->id);
                        }
                    }
                    Utility::unitFormat($bandwidth, "bytes", "mb");
                    Utility::unitFormat($diskUsage, "bytes", "mb");
                    $hosting->bwusage += $bandwidth;
                    $hosting->update(
                        [
                            "diskusage"  => $diskUsage,
                            "bwusage"    => $hosting->bwusage,
                            "lastupdate" => $bandwithTime
                        ]
                    );
                    //Suspend On Bandwidth Overage
                    if ($this->configuration()->isSuspendOnBandwidthOverage() && $hosting->domainstatus != "Suspended" && $hosting->isBandwidthOverageUsage())
                    {
                        $localApi = new Whmcs(new Admins());
                        $localApi->call("modulesuspend", ["accountid" => $hosting->id, "suspendreason" => "Overage Usage"]);
                        $output->writeln(sprintf("Hosting: %s has been suspended, reanson: Overage Usage", $hosting->id));
                    }
                    $output->writeln(sprintf("Hosting: %s has been synchronized, bandwith usage: %s MB", $hosting->id, $bandwidth));
                }

            }
            catch (\Exception $ex)
            {
                $msg = $ex->getMessage();
                if ($hosting)
                {
                    $msg = sprintf("Hosting Id #%s, %s", $hosting->id, $ex->getMessage());
                }
                $io->error($msg);
            }
            (new Hypervisor($this->getName(), $input->getOptions()))
                ->ping();
        }
        $output->writeln("");
        $io->success([
            sprintf("Synchronize hostings: %s Entries Processed.", $i),
            "Update Server Usage: Done"
        ]);
    }

    private function updateEmptyLastUpdate()
    {
        $h = 'tblhosting';
        Hosting::ofProxmoxVpsAndProxmoxCloud()
            ->where("{$h}.domainstatus", 'Active')
            ->where("{$h}.lastupdate", "0000-00-00 00:00:00")
            ->update(["{$h}.lastupdate" => Hosting::raw('NOW()')]);
        return $this;
    }

    private function updateEmptyBandwidthResetDate()
    {
        if (ModuleSetting::bandwidthResetDate()->count())
        {
            return $this;
        }
        $setting          = new ModuleSetting();
        $setting->setting = "bandwidthResetDate";
        $setting->value   = date("Y-m-d H:i:s");
        $setting->save();
        return $this;
    }

    private function isDailyJob(){
        return ModuleSetting::bandwidthResetDate()->whereRaw("DATE(`value`) <= DATE_SUB(NOW(),INTERVAL 24 HOUR) ")->count() > 0;
    }

    private function endDailyJob(){
        $setting = ModuleSetting::bandwidthResetDate()->firstOrFail();
        // save last rest bandwidth date
        $setting->value = date("Y-m-d H:i:s");
        $setting->save();
    }

    private function resetBandwidth()
    {
        $productIds = ProductConfiguration::ofSetting('resetUsageFirstDayOfMonth')
            ->where("value", "like", '\"on\"')
            ->pluck("product_id")
            ->all();
        $h       = 'tblhosting';
        Hosting::ofProxmoxVpsAndProxmoxCloud()
            ->whereIn("{$h}.domainstatus", ["Active", "Suspended"])
            ->whereNotIn("{$h}.packageid", $productIds)
            ->whereRaw(" DAY({$h}.regdate) = DAY(NOW()) ")
            ->update(["{$h}.bwusage" => 0]);
        //Reset Usage First Day Of Month
        if(date("d")=="01"){
            Hosting::ofProxmoxVpsAndProxmoxCloud()
                ->whereIn("{$h}.domainstatus", ["Active", "Suspended"])
                ->whereIn("{$h}.packageid", $productIds)
                ->update(["{$h}.bwusage" => 0]);
        }

    }

    private  function unsuspendOverageUsage(){
        //get products where option suspendOnBandwidthOverage is on
        $productIds = ProductConfiguration::ofSetting('suspendOnBandwidthOverage')
            ->where("value", "like", '\"on\"')
            ->pluck("product_id")
            ->all();
        if(empty(   $productIds)){
            return;
        }
        //get hosting where domainstatus is Suspended and registration day is now and suspendreason is Overage Usage
        $h        = (new Hosting())->getTable();
        $s        = (new Server())->getTable();
        $hostings = Hosting::select("{$h}.id", "{$h}.packageid", "{$h}.lastupdate", "{$h}.bwusage", "{$h}.bwlimit", "{$h}.regdate")
            ->rightJoin($s, "{$h}.server", '=', "{$s}.id")
            ->where("{$h}.domainstatus", "Suspended")
            ->where("{$h}.suspendreason", "Overage Usage")
            ->whereIn("{$h}.packageid", $productIds)
            ->where("{$s}.type", "proxmoxVPS")
            ->whereRaw(" DAY({$h}.regdate) = DAY(NOW()) ");
        $i        = 0;
        /**
         * @var Hosting $hosting
         */
        foreach ($hostings->get() as $hosting)
        {
            try{
                $localApi = new Whmcs(new Admins());
                $localApi->call("ModuleUnsuspend", ["accountid" => $hosting->id]);
            } catch (\Exception $ex)
            {
                echo sprintf("Error: %s, Hosting ID: #%s",$ex->getMessage(), $hosting->id);
            }
        }

    }
}
