<?php

namespace MGModule\DNSManager2\mgLibs\custom\manager;

use Exception;
use MGModule\DNSManager2 as main;
use MGModule\DNSManager2\helpers\custom\ZoneLinked;
use MGModule\DNSManager2\mgLibs\custom\helpers\DomainHelper;
use MGModule\DNSManager2\mgLibs\custom\helpers\ZoneLogger\Manager as ZoneLoggerManager;
use MGModule\DNSManager2\mgLibs\custom\helpers\IdnaHelper;
use MGModule\DNSManager2\mgLibs\custom\helpers\AddonHelper;
use MGModule\DNSManager2\mgLibs\MySQL\query;
use MGModule\DNSManager2\models\custom\server;
use MGModule\DNSManager2\models\custom\zone\Zone;
use MGModule\DNSManager2\models\custom\zone\ZoneTypeEnum;
use MGModule\DNSManager2\models\whmcs\servers\server as relatedServer;

class ZoneCreator
{
    private $zone;
    private $item = false;
    private $package = false;
    private $nameservers = false;
    private $zoneLinked;

    /**
     * @var ZoneLoggerManager
     */
    private $zoneLoggerManager;

    /**
     *
     * @var main\models\custom\server\Server
     */
    public $server = false;

    public function __construct($zone_name, $type, $relid, $ip = false, $clientid = false)
    {
        if($clientid)
        {
            $temp                   = explode("|",$clientid);
            $clientid               = $temp[0];
        }

        $this->zone             = new Zone();
        $this->zone->name       = strtolower($zone_name);
        $this->zone->relid      = $relid;
        $this->zone->type       = $type;
        $this->zone->clientid   = $clientid?:$this->zone->getRelatedItem()->clientID();
        $this->zone->is_locked  = 0;
        $this->nameservers      = $this->getNameServers($relid, $type);

        $item = $this->zone->getPackageItem();
        if($item === false)
        {
            return ;
        }

        $this->item     = $item;
        $this->package  = $item->getPackage();

        foreach ($this->package->getServers() as $packageServer){
            if($packageServer->isMaster())
            {
                if(!$packageServer->getServer()->isEnabled())
                {
                    $serverName = $packageServer->getServer() ? $packageServer->getServer()->name : '';
                    throw new \Exception(main\mgLibs\lang::T('master_server_disabled') . $serverName);
                }
                $this->server = $packageServer->getServer();
            }
        }
        if(!$this->server)
        {
            $this->server = $this->package->getFirstActiveServer();
        }
        if($this->server === false)
        {
            return ;
        }

        $dedicatedIp        = false;
        $relatedServerIp    = false;

        if($type == ZoneTypeEnum::HOSTING)
        {
            $dedicatedIp        = $this->zone->getRelatedItem()->dedicatedIP;
            $relatedServerId    = $this->zone->getRelatedItem()->serverID;
            if(!empty($relatedServerId)) {
                $relatedServer      = new relatedServer($relatedServerId);
                $relatedServerIp    = $relatedServer->ip;
            }
        }

        if(!empty($ip))
        {
            $this->zone->ip = $ip;
        }
        elseif(!empty($dedicatedIp))
        {
            $this->zone->ip = $dedicatedIp;
        }
        elseif(!empty($relatedServerIp))
        {
            $this->zone->ip = $relatedServerIp;
        }
        else
        {
            $this->zone->ip = $this->server->getModuleConfiguration('default_ip');
        }

        //$this->zone->ip = empty($ip)?$this->server->getModuleConfiguration('default_ip'):$ip;

        $this->zone->serverid = $this->server->id;

        $this->zoneLinked = new ZoneLinked($this->zone);
    }

    public function isZoneAlreadyExistInDB()
    {
        $rep = main\models\custom\zone\Repository::factory()->byName($this->zone->name)->byServerID($this->server->id);

        return $rep->count() > 0;
    }

    public function getPackage()
    {
        return $this->package;
    }

    private function isSubdomain()
    {
        $helper = new DomainHelper($this->zone->name);

        return $helper->isSubdamain();
    }

    private function isDomainValid()
    {
        $helper = new DomainHelper($this->zone->name);

        return $helper->isValid();
    }

    private function isAlreadyInWHMCSDatabase()
    {
        $checkTblHosting = main\mgLibs\MySQL\query::query("SELECT id FROM tblhosting WHERE BINARY domain = BINARY :domain AND userid <> :userid AND domainstatus NOT IN ('Cancelled', 'Terminated', 'Fraud')", array('domain' => $this->zone->name, 'userid' => $this->zone->clientid))->fetch();
        if($checkTblHosting !== false)
        {
            return true;
        }

        $checkTblDomains = main\mgLibs\MySQL\query::query("SELECT id FROM tbldomains WHERE BINARY domain = BINARY :domain AND userid <> :userid AND status NOT IN ('Cancelled', 'Transferred Away', 'Fraud')", array('domain' => $this->zone->name, 'userid' => $this->zone->clientid))->fetch();
        if($checkTblDomains !== false)
        {
            return true;
        }

        return false;
    }

    public function create( $recordSetId = false, $force = false )
    {
        try
        {
            if(AddonHelper::isAddonTurnedON('ResellersCenter') === false && AddonHelper::isAddonTurnedON('Multibrand') === false)
            {
                
            }
        }
        catch(Exception $e)
        {
            return 'Problem occured! Please contact administrator.';
        }

        if(!$this->isDomainValid())
        {
            return "Invalid zone name";
        }

        if($this->isSubdomain())
        {
            return "Subdomain is not allowed";
        }

        if($this->package === false)
        {
            return "No package found";
        }

        if($this->package->status == '0')
        {
            return "Package is disabled";
        }

        if($this->server == false) //LOG?
        {
            return "There is no active server";
        }

        if($this->zoneLinked->connectByNewZone())
        {
            LogHelper::addSuccessLog('Linked Zones', 'Linked zones from domain and hosting', $this->zone->name, $this->zone->clientid);
            return;
        }

        if ( !$force && ( $this->isZoneAlreadyExistInDB() || $this->isAlreadyInWHMCSDatabase() ) )
        {
            return main\mgLibs\lang::T('zone_already_taken');
        }

        $module = $this->server->getModule();
        $module->setDomain($this->zone->name);

        if($this->zone->ip)
        {
            $module->setIP($this->zone->ip);
        }

        $this->zone->created_at = date('Y-m-d H:i:s');


        try
        {
            if ( !$module->zoneExists() )
            {
                $module->activateZone();
            } elseif ( !$force )
            {
                return main\mgLibs\lang::T('zone_already_taken');
            }

            $this->getZoneLoggerManager()->logCreateZoneAction($this->zone);


            $setHelper = new main\mgLibs\custom\helpers\RecordSetHelper($this->zone, $this->getZoneLoggerManager());
            $set = $setHelper->addRecordSet($recordSetId);

            $this->zone->status = 1;
            $this->updateOrSave();
            $soaOverwrite = server\setting\ServerSetting::byServerAndKey($this->server->id, server\setting\ServerSettingEnum::OVERWRITE_SOA);
            $populateNS = server\setting\ServerSetting::byServerAndKey($this->server->id, server\setting\ServerSettingEnum::POPULATE_NS);

            if($soaOverwrite == 'on')
            {
                $this->overwriteSoa($module);
            }

            if($populateNS == 'on')
            {
                $this->populateNsRecords($module);
            }

            $clientID = explode("|", $this->zone->clientid)[0];
//            LogHelper::addSuccessLog('Add Zone', '', $this->zone->name, $clientID);
            EmailNotificationHelper::sendClientNotification(DefaultNotifications::GENERAL_ZONE_CREATED_NOTIFICATION, $clientID, $this->item->type, $this->item->relid, array(
                'zone_name' => $this->zone->name,
            ));

            return true;
        }
        catch(Exception $exc)
        {
            $this->zone->status = 0;
            $this->zone->save();
            LogHelper::addFailLog('Add Zone', $exc->getMessage(), $this->zone->name, $this->zone->clientid);
            NotificationHelper::addProblemNotification('Zone creation failed ('. $this->zone->name .')');
            throw $exc;
        }
    }
    private function updateOrSave(){
        if($this->isZoneAlreadyExistInDB()){

            $rep = main\models\custom\zone\Repository::factory()->byName($this->zone->name)->byServerID($this->server->id)->one();
            $rep->relid = $this->zone->relid;
            $rep->updated_at = $this->zone->created_at;
            $rep->save();
        }else{
            $this->zone->save();
        }
    }

    private function overwriteSoa(main\mgLibs\custom\SubmoduleCap $module)
    {
        $serverNSconfig = server\nameserver\Repository::factory()->setFilter('serverid', $this->server->id)->get();

        if($serverNSconfig)
        {
            $soa       = $module->getRecords('SOA');
            $soaRecord = $soaOldRecord = $soa[0];

            if($serverNSconfig[0]->name)
            {
                $soaRecord->rdata->mname = $serverNSconfig[0]->name;
            }
        }

        if($soaRecord instanceof main\mgLibs\custom\dns\record\Record)
        {
            $module->editRecord($soaRecord);
            $zoneEditRecord = new main\mgLibs\custom\helpers\ZoneLogger\Actions\EditRecord($soaRecord, $soaOldRecord);
            $this->getZoneLoggerManager()->logEditRecordsInZone($this->zone, $zoneEditRecord);
        }

        return;
    }

    private function populateNsRecords(main\mgLibs\custom\SubmoduleCap $module)
    {
        $nsAuth     = [];
        $nsAnswer   = dns_get_record(IdnaHelper::idnaEncode($this->zone->name),DNS_NS, $nsAuth);

        $domainNameserver = $nsAuth ?: $nsAnswer;
        $zoneNameserver   = $module->getRecords('NS');

        foreach($domainNameserver as $domainNs)
        {
            try{
                foreach($zoneNameserver as $zoneNs)
                {
                    if(trim($zoneNs->name, '.') === $domainNs['host'] && trim($zoneNs->rdata->nsdname, '.') === $domainNs['target'])
                    {
                        continue 2;
                    }
                }
                $record = main\mgLibs\custom\dns\record\Record::tryToCreateFromArray($domainNs);
                $record->name           = $domainNs['host'].'.';
                $record->rdata->nsdname = $domainNs['target'];
                $module->addRecord($record);
                $this->getZoneLoggerManager()->logAddRecordToZone($this->zone, $record);
            } catch (Exception $e) {
                LogHelper::addFailLog('Populate NS Records', $e->getMessage(), $this->zone->name, $this->zone->clientid);
                continue;
            }
        }
    }

    public function getZone()
    {
        return $this->zone;
    }

    /**
     * @return ZoneLoggerManager
     */
    public function getZoneLoggerManager()
    {
        if(!$this->zoneLoggerManager)
        {
            $this->zoneLoggerManager = new ZoneLoggerManager();
        }
        return $this->zoneLoggerManager;
    }

    /**
     * @param ZoneLoggerManager $zoneLoggerManager
     */
    public function setZoneLoggerManager($zoneLoggerManager)
    {
        $this->zoneLoggerManager = $zoneLoggerManager;
    }
    private function getNameServers( $relid, $type)
    {
        switch ( $type )
        {
            case 1:
                $type = 'tbldomains';
                break;
            case 2:
                $type = 'tblhosting';
                break;
            case 3:
                $type = 'tblhostingaddons';
                break;
            default :
                $type = false;
                break;
        }

        if ( $type )
        {
            $sql         = "SELECT tblorders.id AS product_id,tblorders.nameservers
					FROM $type
					INNER JOIN tblorders ON $type.orderid = tblorders.id
					WHERE $type.id = :relid";
            $nameServers = query::query($sql, ['relid' => $relid])->fetch();
            $nameServers = explode(',', $nameServers['nameservers']);
        }
        else
        {
            $nameServers = false;
        }

        if ( !is_array($nameServers) || !count($nameServers) )
        {
            global $CONFIG;
            foreach ( range(1, 5) as $i )
            {
                if ( isset($CONFIG['DefaultNameserver' . $i]) )
                {
                    $nameServers[] = $CONFIG['DefaultNameserver' . $i];
                }
            }
        }

        return $nameServers;
    }
}