<?php
/**
 * GoldSlotPalace API Wrapper Class
 * 
 * Game Provider Aggregator API v4
 * Base URL: https://agent.goldslotpalase.com
 * Auth: Bearer Token via Authorization header
 * 
 * API Token: dc076dab-5076-4e76-96eb-e3587fdadaaf
 * Callback Token: fe5c7c62-9495-41d4-9834-2d19575f81fb
 */

class GoldSlotPalace {
    private $baseUrl = "https://agent.goldslotpalase.com";
    private $apiToken;
    private $callbackToken;
    private $timeout = 30;

    /**
     * Constructor
     * @param string $apiToken      Bearer token untuk autentikasi API
     * @param string $callbackToken Token untuk verifikasi callback
     */
    public function __construct($apiToken, $callbackToken = '') {
        $this->apiToken = $apiToken;
        $this->callbackToken = $callbackToken;
    }

    /* ================================================================
       PRIVATE: HTTP Request Handler
       ================================================================ */
    private function request($endpoint, $params = []) {
        $fullUrl = $this->baseUrl . $endpoint;
        
        $ch = curl_init($fullUrl);
        curl_setopt_array($ch, [
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => json_encode($params),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => $this->timeout,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_HTTPHEADER     => [
                'Authorization: Bearer ' . $this->apiToken,
                'Accept: application/json',
                'Content-Type: application/json'
            ],
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        if ($error) {
            return [
                'success' => false,
                'code'    => -1,
                'message' => 'Connection Error: ' . $error
            ];
        }

        $decoded = json_decode($response, true);
        
        if ($decoded === null) {
            return [
                'success' => false,
                'code'    => -1,
                'message' => 'Invalid JSON response. HTTP Code: ' . $httpCode,
                'raw'     => substr($response, 0, 500)
            ];
        }

        // Standardize response: code 0 = success
        $decoded['success'] = (isset($decoded['code']) && $decoded['code'] === 0);
        
        return $decoded;
    }

    /* ================================================================
       AGENT ACCOUNT
       ================================================================ */

    /**
     * Get Agent Information
     * @return array {code, message, data: {name, currency, balance, rtp, whitelist, client_ip}}
     */
    public function getAgentInfo() {
        return $this->request('/v4/agent/info');
    }

    /**
     * Change Agent RTP (Winning Rate)
     * @param float $rtp Winning rate value
     * @return array {code, message}
     */
    public function setAgentRtp($rtp) {
        return $this->request('/v4/agent/rtp', ['rtp' => (float)$rtp]);
    }

    /**
     * Test Callback Connection
     * @return array {code, message}
     */
    public function testCallback() {
        return $this->request('/v4/agent/callback-test');
    }

    /* ================================================================
       USER ACCOUNT
       ================================================================ */

    /**
     * Create User (Auto-create if exists, returns existing user_code)
     * @param string $name Username (2-50 chars, alphanumeric + underscore)
     * @return array {code, message, data: {user_code, is_new_user}}
     */
    public function createUser($name) {
        return $this->request('/v4/user/create', ['name' => $name]);
    }

    /**
     * Get User Information
     * @param int $userCode User's unique code
     * @return array {code, message, data: {name, balance}}
     */
    public function getUserInfo($userCode) {
        return $this->request('/v4/user/info', ['user_code' => (int)$userCode]);
    }

    /* ================================================================
       WALLET (Transfer Mode Only)
       ================================================================ */

    /**
     * Deposit User Balance
     * Money deposited to user is deducted from agent's points
     * @param int   $userCode User's unique code
     * @param float $amount   Amount to deposit
     * @return array {code, message, data: {balance, amount}}
     */
    public function deposit($userCode, $amount) {
        return $this->request('/v4/wallet/deposit', [
            'user_code' => (int)$userCode,
            'amount'    => (float)$amount
        ]);
    }

    /**
     * Withdraw User Balance
     * Money withdrawn from user is added to agent's points
     * @param int   $userCode User's unique code
     * @param float $amount   Amount to withdraw
     * @return array {code, message, data: {balance, amount}}
     */
    public function withdraw($userCode, $amount) {
        return $this->request('/v4/wallet/withdraw', [
            'user_code' => (int)$userCode,
            'amount'    => (float)$amount
        ]);
    }

    /**
     * Withdraw All User Balance
     * @param int $userCode User's unique code
     * @return array {code, message, data: {balance, amount}}
     */
    public function withdrawAll($userCode) {
        return $this->request('/v4/wallet/withdraw-all', [
            'user_code' => (int)$userCode
        ]);
    }

    /* ================================================================
       GAME DETAILS
       ================================================================ */

    /**
     * Get Providers List
     * @param int $lang Language code (1-13, 9 = Indonesian)
     * @return array {code, message, data: [{provider_id, provider_name, locale_name, status}]}
     */
    public function getProviders($lang = 1) {
        return $this->request('/v4/game/providers', ['lang' => (int)$lang]);
    }

    /**
     * Get Games List by Provider
     * @param int $providerId Provider ID from providers list
     * @param int $lang       Language code (1-13, 9 = Indonesian)
     * @return array {code, message, data: [{provider_id, game_code, game_name, locale_name, game_image, game_image_narrow, launch_enable, category, reg_date}]}
     */
    public function getGames($providerId, $lang = 9) {
        return $this->request('/v4/game/games', [
            'provider_id' => (int)$providerId,
            'lang'        => (int)$lang
        ]);
    }

    /**
     * Get All Games (across all providers)
     * @return array {code, message, data: [...]}
     */
    public function getAllGames() {
        return $this->request('/v4/game/all');
    }

    /* ================================================================
       GAME LAUNCH
       ================================================================ */

    /**
     * Get Game Access URL
     * URL valid for 10 minutes, one-time use
     * @param int    $userCode      User's unique code
     * @param int    $providerId    Provider ID
     * @param string $gameSymbol    Game symbol/code
     * @param int    $lang          Language code (1-13, 9 = Indonesian)
     * @param string $returnUrl     Home URL for return (optional)
     * @param float  $rtp           User-specific RTP, 0 = use agent's RTP (optional)
     * @param bool   $isFinishJackpot Finish jackpot flag (optional)
     * @return array {code, message, data: {game_url}}
     */
    public function getGameUrl($userCode, $providerId, $gameSymbol, $lang = 9, $returnUrl = '', $rtp = 0, $isFinishJackpot = false) {
        $params = [
            'user_code'   => (int)$userCode,
            'provider_id' => (int)$providerId,
            'game_symbol' => $gameSymbol,
            'lang'        => (int)$lang,
            'rtp'         => (float)$rtp
        ];
        
        if (!empty($returnUrl)) {
            $params['return_url'] = $returnUrl;
        }
        
        if ($isFinishJackpot) {
            $params['is_finish_jackpot'] = true;
        }
        
        return $this->request('/v4/game/game-url', $params);
    }

    /**
     * Get Online Games List
     * Returns games connected up to 30 minutes ago
     * @return array {code, message, data: [{gplay_id, user_code, user_name, provider_id, provider_name, game_code, game_name, category, last_round_bet, spend, win, call_win, call_id, call_enable, callend_flag, call_status, start_time, last_update}]}
     */
    public function getOnlineGames() {
        return $this->request('/v4/game/online-games');
    }

    /* ================================================================
       BONUS CALL
       ================================================================ */

    /**
     * Check Bonus-Call Settings
     * @return array {code, message, data: {call_min}}
     */
    public function getCallConfig() {
        return $this->request('/v4/game/call_config');
    }

    /**
     * Start Bonus-Call
     * @param int   $gplayId  Game-play ID from online games list
     * @param float $setPoint Bonus call amount
     * @param int   $type     Bonus call type
     * @param string $memo    Memo (optional)
     * @return array {code, message, data: {call_id}}
     */
    public function startBonusCall($gplayId, $setPoint, $type = 1, $memo = '') {
        $params = [
            'gplay_id'  => (int)$gplayId,
            'set_point' => (float)$setPoint,
            'type'      => (int)$type
        ];
        if (!empty($memo)) {
            $params['memo'] = $memo;
        }
        return $this->request('/v4/game/call_start', $params);
    }

    /**
     * Cancel Bonus-Call
     * @param int $callId Bonus-call ID from start
     * @return array {code, message}
     */
    public function cancelBonusCall($callId) {
        return $this->request('/v4/game/call_cancel', ['call_id' => (int)$callId]);
    }

    /* ================================================================
       FREE ROUND
       ================================================================ */

    /**
     * Create Free Round
     * @param int    $userCode       User's unique code
     * @param int    $providerId     Provider ID
     * @param string $gameSymbol     Game symbol
     * @param float  $bet            Bet amount
     * @param float  $win            Win amount
     * @param int    $rounds         Number of rounds
     * @param int    $expirationDate Expiration timestamp (unix)
     * @return array {code, message}
     */
    public function createFreeRound($userCode, $providerId, $gameSymbol, $bet, $win, $rounds, $expirationDate) {
        return $this->request('/v4/game/freeround/create', [
            'user_code'      => (int)$userCode,
            'provider_id'    => (int)$providerId,
            'game_symbol'    => $gameSymbol,
            'bet'            => (float)$bet,
            'win'            => (float)$win,
            'rounds'         => (int)$rounds,
            'expirationDate' => (int)$expirationDate
        ]);
    }

    /**
     * Cancel Free Round
     * @param string $frId Free round ID
     * @return array {code, message}
     */
    public function cancelFreeRound($frId) {
        return $this->request('/v4/game/freeround/cancel', ['fr_id' => $frId]);
    }

    /* ================================================================
       GAME TRANSACTIONS
       ================================================================ */

    /**
     * Search Game Transactions by Time
     * Max 2000 results per request. Next request must be delayed 30+ seconds.
     * Time must be in UTC(0).
     * @param string $startTime Start time (yyyy-MM-dd HH:mm:ss)
     * @param string $endTime   End time (yyyy-MM-dd HH:mm:ss)
     * @param int    $offset    Start position (0-based)
     * @param int    $limit     Results per page (10-2000)
     * @return array {code, message, data: {total, offset, count, list: [{trans_id, user_code, round_id, trans_type, provider_id, provider_name, game_code, game_name, category, prebalance, trans_amount, balance, regdate, time_stamp}]}}
     */
    public function getTransactions($startTime, $endTime, $offset = 0, $limit = 100) {
        return $this->request('/v4/game/transaction', [
            'start_time' => $startTime,
            'end_time'   => $endTime,
            'offset'     => (int)$offset,
            'limit'      => (int)$limit
        ]);
    }

    /**
     * Search Game Transactions by Transaction ID
     * Initial call with last_id=0, subsequent calls with returned ID.
     * Must delay 30+ seconds between calls with same last_id.
     * @param int $lastId Starting transaction ID (0 for initial)
     * @param int $limit  Results per page (10-2000)
     * @return array {code, message, data: [...]}
     */
    public function getTransactionsById($lastId = 0, $limit = 100) {
        return $this->request('/v4/game/transaction-id', [
            'last_id' => (int)$lastId,
            'limit'   => (int)$limit
        ]);
    }

    /**
     * Get Round Detail URL
     * Round info up to 30 days ago can be queried.
     * @param int    $userCode   User's unique code
     * @param string $roundId    Round ID
     * @param int    $providerId Provider ID (optional)
     * @param string $gameCode   Game symbol (optional)
     * @return array {code, message, data: {url}}
     */
    public function getRoundDetails($userCode, $roundId, $providerId = null, $gameCode = null) {
        $params = [
            'user_code' => (int)$userCode,
            'round_id'  => $roundId
        ];
        if ($providerId !== null) {
            $params['provider_id'] = (int)$providerId;
        }
        if ($gameCode !== null) {
            $params['game_code'] = $gameCode;
        }
        return $this->request('/v4/game/round-details', $params);
    }

    /* ================================================================
       STATISTICS
       ================================================================ */

    /**
     * Get User Statistics
     * @param string $startTime Start time (date-time format)
     * @param string $endTime   End time (date-time format)
     * @param int    $offset    Offset (0-based)
     * @param int    $limit     Limit (10-2000)
     * @return array {code, message, data: {total, offset, count, list: [{user_code, user_name, slot_bet, slot_win, live_bet, live_win, mini_bet, mini_win}]}}
     */
    public function getStatistics($startTime, $endTime, $offset = 0, $limit = 100) {
        return $this->request('/v4/statistics/user', [
            'start_time' => $startTime,
            'end_time'   => $endTime,
            'offset'     => (int)$offset,
            'limit'      => (int)$limit
        ]);
    }

    /* ================================================================
       HELPER METHODS
       ================================================================ */

    /**
     * Get callback token for verification
     * @return string
     */
    public function getCallbackToken() {
        return $this->callbackToken;
    }

    /**
     * Check if API response is successful
     * @param array $response API response
     * @return bool
     */
    public static function isSuccess($response) {
        return isset($response['code']) && $response['code'] === 0;
    }

    /**
     * Get error message from API response
     * @param array $response API response
     * @return string
     */
    public static function getErrorMessage($response) {
        $errorCodes = [
            0    => 'Success',
            1    => 'Under Maintenance',
            1001 => 'Internal Server Error',
            1002 => 'Validation Error',
            1003 => 'Service Exception',
            1007 => 'Token Not Found',
            1009 => 'Token Invalid',
            1010 => 'Permission Error',
            1011 => 'Provider Error',
            1012 => 'Parameters Invalid',
            1015 => 'Callback Error',
            1018 => 'Server Is Busy',
            1020 => 'IP Not Allowed',
            2001 => 'Agent Not Found',
            2002 => 'User Not Found',
            2003 => 'Game Not Found',
            2005 => 'Agent Point Not Enough',
            2006 => 'User Balance Not Enough',
            2007 => 'Provider Not Found',
            2011 => 'Duplicate Bonus Call',
            2012 => 'Bonus Call Already Ended',
            2013 => 'Round Not Found',
            2014 => 'Currency Not Supported'
        ];
        
        $code = $response['code'] ?? -1;
        $msg = $response['message'] ?? 'Unknown error';
        
        if (isset($errorCodes[$code])) {
            return $errorCodes[$code] . ($msg ? ': ' . $msg : '');
        }
        
        return $msg ?: 'Unknown error (code: ' . $code . ')';
    }
}