<?php
namespace Plugin\ExchangeResets;

class ExchangeResets {
    private $_exchangeTable = WE_PREFIX . 'WEBENGINE_EXCHANGERESETS';
    private $_resets;
    private $_characterData;
    private $_creditConfig = 0;
    private $_exchangeList = array();
    private $_configFile = 'config.json';
    private $_modulesPath = 'modules';
    private $_sqlPath = 'sql';
    private $_sqlList;
    private $_realm;
    private $_usercpmenu = array(
        array('active'=>true,'type'=>'internal','phrase'=>'exchangeresets_title','link'=>'exchangeresets/characters','icon'=>'usercp_default.png','visibility'=>'user','newtab'=>false,'order'=>120)
    );
    private $configFilePath;
    private $sqlFilePath;
    private $db;

    public function __construct() {
        $this->_realm = class_exists('NexoraRealm') ? \NexoraRealm::active() : null;
        $this->_sqlList = array('WEBENGINE_EXCHANGERESETS'=>$this->_exchangeTable);
        $this->configFilePath = self::configPath($this->_realm);
        $cfg = $this->loadConfig($this->configFilePath);
        $this->_creditConfig = isset($cfg['credit_config']) ? (int)$cfg['credit_config'] : 0;
        $this->_exchangeList = isset($cfg['exchange_list']) && is_array($cfg['exchange_list']) ? $cfg['exchange_list'] : array();
        $normalized=array();
        foreach($this->_exchangeList as $resets=>$reward) {
            $r=(int)$resets; $c=(int)$reward;
            if($r>0 && $c>=0) $normalized[$r]=$c;
        }
        ksort($normalized,SORT_NUMERIC);
        $this->_exchangeList=$normalized;
        $this->sqlFilePath = __PATH_EXCHANGERESETS_ROOT__.$this->_sqlPath.'/';
        $this->db = \Connection::Database('Me_MuOnline', $this->_realm);
        $this->_checkTables();
    }

    public static function configPath($realm=null) {
        if(class_exists('NexoraRealm')) {
            try {
                $realm=\NexoraRealm::validate($realm===null ? \NexoraRealm::active() : $realm);
                $realmPath=__PATH_EXCHANGERESETS_ROOT__.'config.'.$realm.'.json';
                if(is_file($realmPath)) return $realmPath;
            } catch(\Throwable $e) {}
        }
        return __PATH_EXCHANGERESETS_ROOT__.'config.json';
    }

    public static function issueExchangeToken() {
        if(session_status()!==PHP_SESSION_ACTIVE) return '';
        $token=bin2hex(random_bytes(24));
        $_SESSION['nexora_exchange_resets_token']=$token;
        $_SESSION['nexora_exchange_resets_token_at']=time();
        return $token;
    }

    public static function consumeExchangeToken($token) {
        if(session_status()!==PHP_SESSION_ACTIVE) return false;
        $expected=isset($_SESSION['nexora_exchange_resets_token'])?(string)$_SESSION['nexora_exchange_resets_token']:'';
        $at=isset($_SESSION['nexora_exchange_resets_token_at'])?(int)$_SESSION['nexora_exchange_resets_token_at']:0;
        unset($_SESSION['nexora_exchange_resets_token'],$_SESSION['nexora_exchange_resets_token_at']);
        return $expected!=='' && is_string($token) && hash_equals($expected,$token) && $at>0 && (time()-$at)<=900;
    }

    public function getRealm() { return $this->_realm; }
    public function getCreditConfigId() { return $this->_creditConfig; }
    public function getExchangeTable() { return $this->_exchangeTable; }

    public function loadModule($module) {
        if(!\Validator::Alpha($module) || !$this->_moduleExists($module)) throw new \Exception(lang('exchangeresets_error_4', true));
        if(!@include_once(__PATH_EXCHANGERESETS_ROOT__.$this->_modulesPath.'/'.$module.'.php')) throw new \Exception(lang('exchangeresets_error_4', true));
    }

    public function setCharacter($character) {
        $Character=new \Character();
        $AccountCharacters=$Character->AccountCharacter($_SESSION['username']);
        if(!is_array($AccountCharacters)) throw new \Exception(lang('error_46',true));
        if(!in_array($character,$AccountCharacters,true)) throw new \Exception(lang('exchangeresets_error_7',true));
        $characterData=$Character->CharacterData($character);
        if(!is_array($characterData)) throw new \Exception(lang('exchangeresets_error_7',true));
        $this->_characterData=$characterData;
    }

    public function setResets($resets) {
        $resets=(int)$resets;
        if($resets<1 || !array_key_exists($resets,$this->_exchangeList)) throw new \Exception(lang('exchangeresets_error_8',true));
        $this->_resets=$resets;
    }

    public function getExchangeList() { return $this->_exchangeList; }

    public function exchangeResets() {
        if($this->_creditConfig<1) throw new \Exception(lang('exchangeresets_error_11',true));
        if(!is_array($this->_characterData) || $this->_resets<1) throw new \Exception(lang('exchangeresets_error_8',true));
        $creditsReward=(int)$this->_exchangeList[$this->_resets];

        $creditSystem=new \CreditSystem();
        $creditSystem->setConfigId($this->_creditConfig);
        $configSettings=$creditSystem->showConfigs(true);
        if(!is_array($configSettings)) throw new \Exception(lang('exchangeresets_error_11',true));
        switch($configSettings['config_user_col_id']) {
            case 'userid': $identifier=$_SESSION['userid']; break;
            case 'username': $identifier=$_SESSION['username']; break;
            case 'email':
                $account=(new \Account())->accountInformation($_SESSION['userid']);
                if(!is_array($account)) throw new \Exception(lang('exchangeresets_error_10',true));
                $identifier=$account[_CLMN_EMAIL_];
                break;
            case 'character': $identifier=$this->_characterData[_CLMN_CHR_NAME_]; break;
            default: throw new \Exception('Invalid identifier (credit system).');
        }

        $economy=new \NexoraEconomy($this->_realm);
        $economy->exchangeResets(
            $this->_creditConfig,
            $identifier,
            $_SESSION['username'],
            $this->_characterData[_CLMN_CHR_NAME_],
            $this->_resets,
            $creditsReward,
            $this->_exchangeTable
        );
        redirect(1,'exchangeresets/characters/success/1/');
    }

    public function getExchangedResetsFromCharacter($character) {
        $result=$this->db->query_fetch_single('SELECT exchanged_resets FROM '.$this->_exchangeTable.' WHERE [character] = ?',array($character));
        return is_array($result) ? (int)$result['exchanged_resets'] : 0;
    }

    public function getExchangedResetsForCharacters($characters) {
        if(!is_array($characters) || count($characters)<1) return array();
        $clean=array_values(array_unique(array_filter(array_map('strval',$characters),function($v){return $v!=='';})));
        if(count($clean)<1) return array();
        $marks=implode(',',array_fill(0,count($clean),'?'));
        $rows=$this->db->query_fetch('SELECT [character], exchanged_resets FROM '.$this->_exchangeTable.' WHERE [character] IN ('.$marks.')',$clean);
        $map=array();
        if(is_array($rows)) foreach($rows as $row) $map[$row['character']]=(int)$row['exchanged_resets'];
        return $map;
    }

    public function checkPluginUsercpLinks() {
        $cfg=loadConfig('usercp'); if(!is_array($cfg)) $cfg=array();
        $links=array(); foreach($cfg as $row) if(isset($row['link'])) $links[]=$row['link'];
        foreach($this->_usercpmenu as $row) if(!in_array($row['link'],$links,true)) $cfg[]=$row;
        usort($cfg,function($a,$b){return ((int)$a['order'])<=>((int)$b['order']);});
        $json=json_encode($cfg,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
        if($json===false || file_put_contents(__PATH_CONFIGS__.'usercp.json',$json,LOCK_EX)===false) throw new \Exception('No se pudo actualizar el menu UserCP.');
    }

    public function getExchangedResetsList() {
        $result=$this->db->query_fetch('SELECT [character], exchanged_resets FROM '.$this->_exchangeTable.' ORDER BY exchanged_resets DESC, [character] ASC');
        return is_array($result)?$result:null;
    }

    private function loadConfig($path) {
        if(!is_file($path) || !is_readable($path)) throw new \Exception(lang('exchangeresets_error_2',true));
        $raw=file_get_contents($path); $cfg=json_decode((string)$raw,true);
        if(!is_array($cfg)) throw new \Exception(lang('exchangeresets_error_2',true));
        if(!isset($cfg['exchange_list']) || !is_array($cfg['exchange_list'])) $cfg['exchange_list']=array();
        return $cfg;
    }

    private function _moduleExists($module) { return is_file(__PATH_EXCHANGERESETS_ROOT__.$this->_modulesPath.'/'.$module.'.php'); }

    private function _checkTables() {
        foreach($this->_sqlList as $sqlFile=>$tableName) {
            $exists=$this->db->query_fetch_single("SELECT name FROM sysobjects WHERE xtype='U' AND name = ?",array($tableName));
            if($exists) continue;
            if(!$this->_createTable($tableName,$sqlFile)) throw new \Exception(lang('exchangeresets_error_3',true));
        }
    }

    private function _createTable($tableName,$sqlFile) {
        if(!array_key_exists($sqlFile,$this->_sqlList)) return false;
        $file=$this->sqlFilePath.$sqlFile.'.txt'; if(!is_file($file)) return false;
        $query=file_get_contents($file); if(!is_string($query) || trim($query)==='') return false;
        return (bool)$this->db->query(str_replace('{TABLE_NAME}',$tableName,$query));
    }
}
