<?php

namespace App\Controllers;

require_once APPPATH . 'Libraries/ServiceLibrary.php';
require_once APPPATH . 'Libraries/Defines/ServiceErrors.php';
require_once APPPATH . 'Libraries/Defines/MqttRequestResponseCodes.php';

use CodeIgniter\Controller;
use CodeIgniter\HTTP\CLIRequest;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Psr\Log\LoggerInterface;

/**
 * Class BaseController
 *
 * BaseController provides a convenient place for loading components
 * and performing functions that are needed by all your controllers.
 * Extend this class in any new controllers:
 *     class Home extends BaseController
 *
 * For security be sure to declare any new methods as protected or private.
 */
abstract class BaseController extends Controller
{
    /**
     * Instance of the main Request object.
     *
     * @var CLIRequest|IncomingRequest
     */
    protected $request;

    /**
     * An array of helpers to be loaded automatically upon
     * class instantiation. These helpers will be available
     * to all other controllers that extend BaseController.
     *
     * @var list<string>
     */
    protected $helpers = ['common', 'raspberrypi', 'array', 'form'];

    /**
     * Be sure to declare properties for any property fetch you initialized.
     * The creation of dynamic property is deprecated in PHP 8.2.
     */
    // protected $session;

    public $validation;
	public $email;
	public $objPasswordEncrypter;
	public $objEncrypter;
	public $g_objServiceLibrary;
	
	public static $s_arrmixValidationErrors;
	public static $s_objSession;


	const AUTH_TYPE_APP     = 'app';
    const AUTH_TYPE_DEVICES = 'devices';

	const MQTT_PUBLISH_TOPIC   = 'dt/web_to_app/cmd';
	const MQTT_SUBSCRIBE_TOPIC = 'dt/app_to_web/cmd';

    /**
     * @return void
     */
    public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
    {
        // Do Not Edit This Line
        parent::initController($request, $response, $logger);

        // Preload any models, libraries, etc, here.

        // E.g.: $this->session = \Config\Services::session();

        //--------------------------------------------------------------------
		// Preload any models, libraries, etc, here.
		//--------------------------------------------------------------------
		// E.g.: $this->session = \Config\Services::session();

		$this->loadConfigurations();
    }

    protected function loadConfigurations() {
		
		$this->email 	  = \Config\Services::email();
		$this->request 	  = \Config\Services::request();
		$this->validation = \Config\Services::validation();
		
		$objPasswordConfig = new \Config\Encryption();
		$objPasswordConfig->key = ENCRYPTION_PASSWORD_KEY;
		
		$this->objPasswordEncrypter = \Config\Services::encrypter( $objPasswordConfig );
		
		$objConfig = new \Config\Encryption();
		$objConfig->key = ENCRYPTION_TOKEN_KEY;
		
		$this->objEncrypter = \Config\Services::encrypter( $objConfig );

		$this->g_objServiceLibrary = new \Libraries\ServiceLibrary( $this->email );
	}
	
	protected function encryptPassword( $strPassword ) {
		return base64_encode( $this->objPasswordEncrypter->encrypt( $strPassword ) );
	}

	protected function decryptPassword( $strEncryptedPassword ) {
		return $this->objPasswordEncrypter->decrypt( base64_decode( $strEncryptedPassword ) );
	}
	
	protected function encryptToken( $arrmixTokenData ) {
		return base64_encode( $this->objEncrypter->encrypt( serialize( $arrmixTokenData ) ) );
	}

	protected function decryptToken( $strEncryptedToken ) {
		return ( array ) unserialize( $this->objEncrypter->decrypt( base64_decode( $strEncryptedToken ) ) );
	}
	
	public static function setFormValidationErrors( $arrmixValidationErrors ) {
		self::$s_arrmixValidationErrors = $arrmixValidationErrors;
	}

	public static function getFormValidationErrors() {
		return self::$s_arrmixValidationErrors;
	}

	protected function response( $arrmixData ) {
		echo json_encode( $arrmixData );
	}

    protected function generateErrorMessage( $intErrorCode, $strErrorMessage = '', $strErrorTypeName = '' ) {
        
		$arrmixResponseData['response']['status']  = $intErrorCode;
		$arrmixResponseData['response']['message'] = ( ( true == isVal( $strErrorTypeName ) ) ? $strErrorTypeName . ': ' : '' ) . $strErrorMessage;

		$this->g_objServiceLibrary->logger( 'Error response : ' . json_encode( $arrmixResponseData ) );
		
		throw new \Exception( $strErrorMessage, $intErrorCode );
    }

    protected function generateAPIErrorMessage( $intErrorCode, $strErrorMessage = '', $strErrorTypeName = '' ) {
        
		$arrmixResponseData['response']['status'] = $intErrorCode;

		if( true == isVal( $strErrorMessage ) ) {
			$arrmixResponseData['response']['message'] = ( ( true == isVal( $strErrorTypeName ) ) ? $strErrorTypeName . ': ' : '' ) . $strErrorMessage;
		}
		
		if( true == isset( $this->g_arrmixAuthDetails ) && true == isValArr( $this->g_arrmixAuthDetails ) && AUTH_TYPE_APP == $this->g_arrmixAuthDetails['type'] ) {
			// TODO: Need to add the error file for tracking the exact issue. 

			$this->g_objServiceLibrary->logger( 'Admin error response : ' . json_encode( $arrmixResponseData ) );
			
			unset( $arrmixResponseData['response']['message'] );
		} 
		
        $this->response( $arrmixResponseData );
        exit;
    }
    
    protected function generateAPISuccessMessage( $arrmixResponseDetails ) {
        
        $arrmixResponseData['response'] = [
            'status' => SUCCESS,
            'data' =>  $arrmixResponseDetails
        ];
        
		$this->g_objServiceLibrary->logger( 'Admin success response : ' . json_encode( $arrmixResponseData ) );

        $this->response( $arrmixResponseData );
        exit;
    }

	protected function generateMqttError( $strMessageErrorId, $intErrorCode, $strErrorMessage, $strErrorTypeName = ''  ) {
        
        $strLoggerMessage = 'Subscribe Mqtt Topic - ' . $this->g_strReceivedTopic . '. Error Code - ' . $intErrorCode . ', ' . ( ( true == isVal( $strErrorTypeName ) ) ? $strErrorTypeName . ': ' : 'Error Message - ' ) . $strErrorMessage;
        
        $this->g_objServiceLibrary->logger( $strLoggerMessage, 'mqtt-logs/subscribe/subscribe_error_' . replaceDateTimeDashFormatToUnderscore( date( 'Y-m-d' ) ) );
        
        $this->g_boolIsMqttValidateRequest = false;

        if( 'error' != $strMessageErrorId ) {
            $arrmixMessage = [
                'status' => 200,
                'data' => [
					'epoach' 	=> strtotime( date( 'Y-m-d H:i:s' ) ),
                    'msgid' 	=> $strMessageErrorId,
                    'msgstatus' => $intErrorCode
                ]
            ];

            $strMessage = json_encode( $arrmixMessage );
            $arrmixReceivedTopic =  explode( "/", $this->g_strReceivedTopic );

            $strTopic = self::MQTT_TOPIC . 'pub/' . $arrmixReceivedTopic[3] . '/ondemand';

			$strPublishLoggerMessage = 'Subscribe Mqtt Topic - ' . $this->g_strReceivedTopic . '. Publish Mqtt Topic - ' . $strTopic . ' Successfully action performed from Mqtt Error. Publish Required - 1  Message - ' . $strMessage;

			$this->g_objServiceLibrary->logger( $strPublishLoggerMessage, 'mqtt-logs/publish/publish_success_' . replaceDateTimeDashFormatToUnderscore( date( 'Y-m-d' ) ) );
			
           	$this->publishMqttData( $strTopic, $strMessage );
        }
        
        throw new \RuntimeException(  $strLoggerMessage );
    }

    protected function generateMqttSuccess( $arrmixResponseData  ) {

        $arrmixMessage = [
            'status' => 200,
            'data' => $arrmixResponseData['message']
        ];

        $strMessage = json_encode( $arrmixMessage, JSON_NUMERIC_CHECK );

        $strLoggerMessage = 'Subscribe Mqtt Topic - ' . $this->g_strReceivedTopic . '. Publish Mqtt Topic - ' . $arrmixResponseData['topic'] . ' Successfully action performed. Publish Required - ' .  $arrmixResponseData['is_require_publish']  . '  Message - ' . $strMessage;
        
        $this->g_objServiceLibrary->logger( $strLoggerMessage, 'mqtt-logs/publish/publish_success_' . replaceDateTimeDashFormatToUnderscore( date( 'Y-m-d' ) ) );

        if( true == $arrmixResponseData['is_require_publish'] ) {
            $this->publishMqttData( $arrmixResponseData['topic'], $strMessage );
        }
        
    }

    protected function publishMqttData( $strTopic, $strMessage ) {
        
        require_once APPPATH . 'Libraries/MosquittoMqtt.php';

        try {

            $objMosquittoMqtt = new \Libraries\MosquittoMqtt();
                
            $objMosquittoMqtt->getConnection();

            $objMosquittoMqtt->publish( $strTopic, $strMessage );
        
            $objMosquittoMqtt->disconnectConnection();

            $this->g_objServiceLibrary->logger( $strMessage, 'mqtt-logs/publish/success/' . replaceDateTimeDashFormatToUnderscore( date( 'Y-m-d' ) ) );

        } catch( \Exception $objException ) {

            $this->g_objServiceLibrary->logger( $objException->getMessage(), 'mqtt-logs/publish/errors/' . replaceDateTimeDashFormatToUnderscore( date( 'Y-m-d' ) ) );
        }
    
        return true;
    }

    protected function getConfigData() {

        $arrmixDeviceConfigJsonFileData = [];

        if( true == $this->g_objServiceLibrary->checkConfigFilePresentOrNot( TEMP_CONFIG_FILE_NAME ) ) {
            $strConfigFileData = $this->g_objServiceLibrary->getConfigFile( TEMP_CONFIG_FILE_NAME );

            if( true == isVal( $strConfigFileData ) ) {
                $arrmixDeviceConfigJsonFileData = json_decode( $strConfigFileData, true );
            }

            $intUsedConfigFile = USED_TEMP_CONFIG_FILE;
        } else if( true == $this->g_objServiceLibrary->checkConfigFilePresentOrNot( LATEST_CONFIG_FILE_NAME ) ) {

            $strConfigFileData = $this->g_objServiceLibrary->getConfigFile( LATEST_CONFIG_FILE_NAME );

            if( true == isVal( $strConfigFileData ) ) {
                $arrmixDeviceConfigJsonFileData = json_decode( $strConfigFileData, true );
            }
            $intUsedConfigFile = USED_LATEST_CONFIG_FILE;
        } else {
            $strConfigFileData = $this->g_objServiceLibrary->getConfigFile( DEFAULT_CONFIG_FILE_NAME );

            if( true == isVal( $strConfigFileData ) ) {
                $arrmixDeviceConfigJsonFileData = json_decode( $strConfigFileData, true );
            }

            $intUsedConfigFile = USED_DEFAULT_CONFIG_FILE;
        }

        return [ 'status' => true, 'device_config_josn_file_data' => $arrmixDeviceConfigJsonFileData, 'used_config_file' => $intUsedConfigFile ];
    }

	public static function createService() {
        $strClassName = static::class;
        
        return new $strClassName();
    }
}
