<?php

/**
 * Description of Zone
 *
 * @author Vitalii Aloksa
 */


namespace MGModule\DNSManager2\mgLibs\ModuleAPI;


use MGModule\DNSManager2\mgLibs\custom\helpers\RecordSetHelper;
use MGModule\DNSManager2\mgLibs\custom\helpers\ZoneLogger\Actions\EditRecord;
use MGModule\DNSManager2\mgLibs\custom\helpers\ZoneLogger\Manager as ZoneLoggerManager;
use \MGModule\DNSManager2\models\custom\dns;
use \MGModule\DNSManager2\models\custom\zone\Zone as ZoneModel;
use \MGModule\DNSManager2\mgLibs\custom\manager\ZoneCreator;
use \MGModule\DNSManager2\mgLibs\custom\manager\ZoneRemover;
use \MGModule\DNSManager2\mgLibs\custom\dns\record\Record;
use \MGModule\DNSManager2\mgLibs\custom\manager\LogHelper;



class Zone extends Api
{
    //put your code here
    
    
    public $data;
    protected $module;
    protected $zone;

    
    /*
     * !IMPORTANT
     * 
     * this array is used to declare the variables, that will be used in API.
     * You may declare several api versions, that could have differnet names of api data values.
     * DO NOT change the keys, it is used in the code, and should not be changed, 
     * only the values are the names, that will be looked for in the INPUT DATA (POST/GET)
     */
    public $dataFields = array(
        'api1' => array(
            'userid'     => 'userid',
            'zone_id'    => 'zone_id',
            'zone_name'  => 'zone_name',
            'zone_ip'    => 'zone_ip',
            'type'       => 'type',
            'modulename' => 'modulename',
            'config'     => 'config',
            'recordSet'  => 'recordSet',
        )
    );

    
    
    function __construct( $data )
    {
        parent::__construct($data);
        $this->processZoneID();
        if(!isset($data['internal_use']) && $data['dnsaction'] != 'createZone')
        {
            $this->loadModule();
        }
    }

    
    
    /*
     * PREPARE METHODS
     */
    
    public function loadModule( )
    {
        $this->laodSubmoduleFromZone();
        $this->loadSubmodule();
        
        if(!is_object($this->module))
        {
            throw new \Exception(Response::errorSubmodule('notfound'));
        }
        
    }
    
    
    public function laodSubmoduleFromZone()
    {
        if ( $this->module )
        {
            return $this->module;
        }
        
        if(!isset($this->data['zone_id']))
        {
            return false;
        }
        $zoneid = $this->data['zone_id'];

        Validator::isValidZoneID( $zoneid );

        $ZoneModel    = new ZoneModel( $zoneid );
        $this->zone   = $ZoneModel;
        $this->module = $ZoneModel->getModule();
            
        return $this->module;
    }



    public function loadSubmodule( )
    {
        try {
            
            if($this->module)
            {
                return $this->module;
            }

            $modulename = $this->data['modulename'];
            $config = $this->data['config'];

            Validator::isValidModuleConfig($modulename, $config);

            $this->module  = dns\Core::getModule($modulename);
            $configuration = $this->getConfiguration();
            $this->module->setConfiguration($configuration);
        
        } catch (\Exception $ex) {
            Response::proccessExceptionResponse($this->errors, $ex);
        }
        return $this->module;
    }
    

    
    public function processZoneID( )
    {
        if(isset($this->data['zone_id']))
        {
            $this->data['zone_id'] = (int) $this->data['zone_id'];
        }
    }
    
    
    
    /*
     * API METHODS
     */

    public function get( )
    {
        $zone = $this->module->getZone();
        $zone->records = $this->module->getRecords();
        if($this->module->moduleHasMethod('getSignKeys'))
        {
            try
            {
                $zone->dnssec = $this->module->getSignKeys();
            }
            catch (\Exception $exception)
            {
                //do Nothing
            }
        }
        LogHelper::addSuccessLogUsingZone('api_getZone', 'Get Zone: ' . $this->zone->name , $this->zone);
        /*
         * @TODO:  make response 
         */
        return $zone;
    }
    
    
    
    
    public function create()
    {
        try {
            Validator::isValidZoneCreateData( $this->data );
            $ZoneCreator = new ZoneCreator( $this->data['zone_name'], $this->data['type'], $this->data['relid'], $this->data['zone_ip'], $this->data['userid'] );
            $result      = $ZoneCreator->create( (int) $this->data['recordSet'] );
            LogHelper::addSuccessLogUsingZone('api_createZone', 'Zone: ' . $this->data['zone_name'] . ' created', $this->zone);
            if($result != true)
            {
                throw new \Exception($result);
            }
            
            return Response::successZoneCreate();
        } catch (\Exception $ex) {
            LogHelper::addFailLogUsingZone('api_createZone', $ex->getMessage(), $this->zone);
            Response::proccessExceptionResponse($this->errors, $ex);
        }
    }



    public function update(  )
    {
        $client = $_SESSION['uid'] ? $_SESSION['uid'] : $this->zone->clientid;
        $zoneLoggerManager = new ZoneLoggerManager($client);
        try {
            $postRecords = $this->data['records'];

            $existedRecords = $this->module->getRecords();

            $oldRecords = RecordSetHelper::formatRecordsToIndexById($existedRecords);

            $recordArray = array();
            $postHelper = [];
            foreach ($postRecords as $postKey => $postRecord) {
                if($postRecord instanceof Record)
                {
                    $postRecord = $postRecord->toArray(false);
                }
                if($postRecord['rdata'])
                {
                    $postRecord['data'] = $postRecord['rdata'];
                    unset($postRecord['rdata']);
                }
                $postHelper[$postKey]['exists'] = false;

                foreach ($existedRecords as $key => $record) {

                    if(empty($recordArray[$key]))
                    {
                        $recordArray[$key]['id']    = $record->line;
                        $recordArray[$key]['name']  = $record->name;
                        $recordArray[$key]['type']  = $record->type;
                        $recordArray[$key]['class'] = $record->class;
                        $recordArray[$key]['ttl']   = $record->ttl;
                        $recordArray[$key]['data'] = $record->rdata->toString();
                        $recordArray[$key]['rdata'] = $record->rdata->toArray(false);
                        $recordArray[$key]['exists'] = false;
                    }

                    //UPDATE IF EXISTS
                    if((isset($postRecord['id']) && $record->line == $postRecord['id'] || (isset($postRecord['line']) && $postRecord['line'] == $record->line)) ||
                        $this->compareRecords( $postRecord, $record )
                        )
                    {
                        try {
                            $record->setDataFromArray($postRecord);

                            if(is_array($postRecord['data'])){
                                $record->rdata->setDataFromArray($postRecord['data']);
                            }else{
                                $record->rdata->fromString($postRecord['data']);
                            }
                            $record->decode();
                            $this->module->editRecord($record);
                            $zoneLoggerManager->logEditRecordsInZone($this->zone, new EditRecord($record, $oldRecords[$record->line]), 'API: ');
//                            LogHelper::addSuccessLogUsingZone('api_editRecord', 'Record: ' . $record->name . ' updated', $this->zone);
                        } catch (\Exception $ex) {
                            LogHelper::addFailLogUsingZone('api_editRecord', $ex->getMessage(), $this->zone);
                        }
                        $recordArray[$key]['exists'] = true;
                        $postHelper[$postKey]['exists'] = true;
                    }
                }
            }


            foreach ($postRecords as $postKey => $postRecord)
            {
                if($postHelper[$postKey]['exists'] === false)
                {
                    try {
                        $newRecord = Record::tryToCreateFromArray($postRecord);
                        if(is_array($postRecord['data'])){
                            $newRecord->rdata->setDataFromArray($postRecord['data']);
                        }else{
                            $newRecord->rdata->fromString($postRecord['data']);
                        }
                        $newRecord->decode();


                        $this->module->addRecord($newRecord);

                        $zoneLoggerManager->logAddRecordToZone($this->zone, $newRecord, 'API: ');
//                        LogHelper::addSuccessLogUsingZone('api_addRecord', 'Record: ' . $newRecord->name . ' added', $this->zone);
                    } catch (\Exception $ex) {
                        LogHelper::addFailLogUsingZone('api_addRecord', $ex->getMessage(), $this->zone);
                    }
                    $postHelper[$postKey]['exists'] = true;
                }
            }

            foreach ($recordArray as $key => $recordItem) {

                if($recordItem['exists'] === false)
                {
                    try {
                        $recordToDelete = $existedRecords[$key];
//                        LogHelper::addFailLogUsingZone('api_addRecord', json_encode($recordToDelete), $this->zone);
//                        LogHelper::addFailLogUsingZone('api_addRecord', get_class($recordToDelete), $this->zone);

                        $this->module->deleteRecord($recordToDelete);
                        $zoneLoggerManager->logAddRecordToZone($this->zone, $recordToDelete, 'API: ');
//                        LogHelper::addSuccessLogUsingZone('api_deleteRecord', 'Record: ' . $recordToDelete->name . ' removed', $this->zone);
                    } catch (\Exception $ex) {
                        LogHelper::addFailLogUsingZone('api_deleteRecord', $ex->getMessage(), $this->zone);
                    }
                }
            }

            return Response::successZoneUpdate();
        } catch (\Exception $ex) {
            Response::proccessExceptionResponse($this->errors, $ex);
        }
        
    }
    
    
    protected function compareRecords( $postRecord,  $record )
    {
        $parsedName = preg_replace('/.'.$this->zone->name.'/', '', $record->name);
        if(
            ($postRecord['name'] == $record->name || $postRecord['name'] == $parsedName) &&
            $postRecord['type'] == $record->type &&
            $postRecord['class'] == $record->class &&
            $postRecord['ttl'] == $record->ttl &&
            (
                $postRecord['data'] == $record->rdata->toArray(false) ||
                $postRecord['data'] == $record->rdata->toString()  ||
                strtolower($postRecord['data']) == trim( $record->rdata->toString(), '"')
            ))
        {
            return true;
        } 
    }




    public function remove( )
    {
        $client = $_SESSION['uid'] ? $_SESSION['uid'] : $this->zone->clientid;
        $zoneLoggerManager = new ZoneLoggerManager($client);

        try {
            Validator::isValidZoneDeleteData($this->data, $this->zone);
            ZoneRemover::removeZone($this->zone);
            $zoneLoggerManager->logTerminateZoneAction($this->zone, 'API: ');
//            LogHelper::addSuccessLogUsingZone('Remove Zone', '', $this->zone);
            Response::successZoneRemove();
//            LogHelper::addSuccessLogUsingZone('api_RemoveZone', 'Zone' .$this->zone->name . ' removed', $this->zone);
        } catch (\Exception $ex) {
            Response::proccessExceptionResponse($this->errors, $ex);
            LogHelper::addFailLogUsingZone('api_RemoveZone', $ex->getMessage(), $this->zone);
//             throw $ex;
        }
    }
    
    
    
    public function transfer(  )
    {
        /*
         * TODO:  make transfer zone after transfer domain/hosting in the API
         */
    }
    
    
    
}
