<?php
namespace Plugin\TransferCharacter;

class TransferCharacter {
    private $_modulesPath='modules';
    private $_sqlTable;
    private $_sqlPath='sql';
    private $_cost=0;
    private $_configId=0;
    private $_requiredLevel=0;
    private $_requiredMasterLevel=0;
    private $_requiredResets=0;
    private $_userid;
    private $_username;
    private $_character;
    private $_newUserid;
    private $_realm;
    private $db;
    private $_usercpmenu=array(
        array('active'=>true,'type'=>'internal','phrase'=>'transfercharacter_title','link'=>'usercp/transfercharacter','icon'=>'usercp_default.png','visibility'=>'user','newtab'=>false,'order'=>150)
    );

    public function __construct() {
        $this->_realm=class_exists('NexoraRealm') ? \NexoraRealm::active() : null;
        $this->_sqlTable=WE_PREFIX.'WEBENGINE_TRANSFER_CHARACTER_LOGS';
        $path=self::configPath($this->_realm);
        if(!is_file($path)) throw new \Exception(lang('transfercharacter_error_2'));
        $xml=simplexml_load_file($path);
        if(!$xml) throw new \Exception(lang('transfercharacter_error_2'));
        $this->_cost=max(0,(int)$xml->transfer_cost);
        $this->_configId=max(0,(int)$xml->config_id);
        $this->_requiredLevel=max(0,(int)$xml->level_required);
        $this->_requiredMasterLevel=max(0,(int)$xml->masterlevel_required);
        $this->_requiredResets=max(0,(int)$xml->resets_required);
        $this->db=\Connection::Database('Me_MuOnline',$this->_realm);
        $this->_checkTable();
    }

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

    public function loadModule($module) {
        if(!\Validator::Alpha($module)) throw new \Exception(lang('transfercharacter_error_3'));
        $path=__PATH_TRANSFERCHARACTER_ROOT__.$this->_modulesPath.'/'.$module.'.php';
        if(!is_file($path)) throw new \Exception(lang('transfercharacter_error_3'));
        include $path;
    }

    public function setUserid($userid) {
        if(!\Validator::UnsignedNumber($userid)) throw new \Exception(lang('transfercharacter_error_7'));
        $this->_userid=(int)$userid;
    }
    public function setUsername($username) {
        if(!\Validator::UsernameLength($username)) throw new \Exception(lang('transfercharacter_error_10'));
        $this->_username=(string)$username;
    }
    public function setCharacter($character) {
        if(!\Validator::AlphaNumeric($character)) throw new \Exception(lang('transfercharacter_error_6'));
        $this->_character=(string)$character;
    }
    public function setTransferId($id) {
        $result=$this->decodeTransferId($id);
        if(!\Validator::UnsignedNumber($result) || (int)$result<1) throw new \Exception(lang('transfercharacter_error_16'));
        $this->_newUserid=(int)$result;
    }

    public function getCost(){return $this->_cost;}
    public function getRequiredLevel(){return $this->_requiredLevel;}
    public function getRequiredMasterLevel(){return $this->_requiredMasterLevel;}
    public function getRequiredResets(){return $this->_requiredResets;}
    public function getRealm(){return $this->_realm;}
    public function getLogTable(){return $this->_sqlTable;}

    public function getAccountCharacterList() {
        if(!check_value($this->_username)) throw new \Exception(lang('transfercharacter_error_4'));
        $list=(new \Character())->AccountCharacter($this->_username);
        if(!is_array($list) || count($list)<1) throw new \Exception(lang('transfercharacter_error_5'));
        return array_values(array_filter($list,function($v){return is_string($v) && trim($v)!=='';}));
    }

    public function transfer() {
        if($this->_configId<1) throw new \Exception(lang('transfercharacter_error_15'));
        if(!$this->_userid || !check_value($this->_username) || !check_value($this->_character) || !$this->_newUserid) throw new \Exception(lang('transfercharacter_error_7'));
        if($this->_newUserid===$this->_userid) throw new \Exception(lang('transfercharacter_error_17'));

        $Account=new \Account();
        $source=$Account->accountInformation($this->_userid);
        if(!is_array($source) || strcasecmp((string)$source[_CLMN_USERNM_],$this->_username)!==0) throw new \Exception(lang('transfercharacter_error_11'));
        $target=$Account->accountInformation($this->_newUserid);
        if(!is_array($target)) throw new \Exception(lang('transfercharacter_error_18'));

        $creditSystem=new \CreditSystem();
        $creditSystem->setConfigId($this->_configId);
        $cfg=$creditSystem->showConfigs(true);
        if(!is_array($cfg)) throw new \Exception(lang('transfercharacter_error_15'));
        switch($cfg['config_user_col_id']) {
            case 'userid': $identifier=$source[_CLMN_MEMBID_]; break;
            case 'username': $identifier=$source[_CLMN_USERNM_]; break;
            case 'email': $identifier=$source[_CLMN_EMAIL_]; break;
            case 'character': $identifier=$this->_character; break;
            default: throw new \Exception(lang('transfercharacter_error_11'));
        }

        $economy=new \NexoraEconomy($this->_realm);
        $result=$economy->transferCharacter(
            $this->_configId,
            $identifier,
            $this->_username,
            $this->_userid,
            (string)$target[_CLMN_USERNM_],
            $this->_newUserid,
            $this->_character,
            $this->_cost,
            $this->_requiredLevel,
            $this->_requiredMasterLevel,
            $this->_requiredResets,
            $this->_sqlTable
        );
        redirect(1,'usercp/transfercharacter/success/1/');
        return $result;
    }

    public function generateTransferId($userid) {
        if(!\Validator::UnsignedNumber($userid) || (int)$userid<1) return '';
        $realm=$this->_realm ?: 'legacy';
        $realmCode=$realm==='seasonal'?'S':'L';
        $body=strtoupper(base_convert((string)(int)$userid,10,36));
        $signature=strtoupper(substr(hash_hmac('sha256',$realm.'|'.(int)$userid,$this->transferCodeKey()),0,10));
        return 'NX-'.$realmCode.'-'.$body.'-'.$signature;
    }

    public function decodeTransferId($id) {
        $id=strtoupper(trim((string)$id));
        if(!preg_match('/^NX-([LS])-([0-9A-Z]+)-([A-F0-9]{10})$/D',$id,$m)) return null;
        $tokenRealm=$m[1]==='S'?'seasonal':'legacy';
        if($this->_realm && $tokenRealm!==$this->_realm) throw new \Exception(lang('transfercharacter_error_26'));
        $userid=(int)base_convert(strtolower($m[2]),36,10);
        if($userid<1) return null;
        $expected=strtoupper(substr(hash_hmac('sha256',$tokenRealm.'|'.$userid,$this->transferCodeKey()),0,10));
        if(!hash_equals($expected,$m[3])) return null;
        return $userid;
    }

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

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

    public function getTransferLogs($limit=50) {
        $limit=(int)$limit;
        if($limit<0) $limit=50;
        if($limit===0) $rows=$this->db->query_fetch('SELECT * FROM '.$this->_sqlTable.' ORDER BY id DESC');
        else $rows=$this->db->query_fetch('SELECT TOP '.$limit.' * FROM '.$this->_sqlTable.' ORDER BY id DESC');
        return is_array($rows)?$rows:null;
    }

    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_UNICODE|JSON_UNESCAPED_SLASHES);
        if($json===false || file_put_contents(__PATH_CONFIGS__.'usercp.json',$json,LOCK_EX)===false) throw new \Exception('No se pudo actualizar el menu UserCP.');
    }

    private function transferCodeKey() {
        if(class_exists('NexoraSecurity')) {
            $configured=\NexoraSecurity::secret('NEXORA_TRANSFER_SIGNING_KEY');
            if(is_string($configured) && strlen($configured)>=24) return $configured;
        }
        $root=defined('__ROOT_DIR__')?__ROOT_DIR__:dirname(__DIR__,5);
        $host=defined('HTTP_HOST')?HTTP_HOST:'nexora';
        return hash('sha256',$root.'|'.$host.'|NexoraTransferCharacter2026');
    }

    private function _checkTable() {
        $exists=$this->db->query_fetch_single("SELECT name FROM sysobjects WHERE xtype='U' AND name=?",array($this->_sqlTable));
        if($exists) return true;
        $file=__PATH_TRANSFERCHARACTER_ROOT__.$this->_sqlPath.'/WEBENGINE_TRANSFER_CHARACTER_LOGS.txt';
        if(!is_file($file)) throw new \Exception(lang('transfercharacter_error_23'));
        $query=file_get_contents($file);
        if(!is_string($query) || trim($query)==='') throw new \Exception(lang('transfercharacter_error_23'));
        if(!$this->db->query(str_replace('{TABLE_NAME}',$this->_sqlTable,$query))) throw new \Exception(lang('transfercharacter_error_23'));
        return true;
    }
}
