<?php

defined('APP_BOOTSTRAPPED') or exit('No direct access.');

class SgxGameApiAdapter extends AbstractGameApiAdapter
{
    private $agentCode;
    private $signature;

    public function __construct(array $config)
    {
        parent::__construct($config);

        $this->agentCode = trim(
            isset($config['agent_code'])
                ? (string) $config['agent_code']
                : ''
        );

        $this->signature = trim(
            isset($config['signature'])
                ? (string) $config['signature']
                : ''
        );

        if ($this->agentCode === '') {
            throw new GameApiException(
                'SGX agent code belum dikonfigurasi.',
                500
            );
        }

        if ($this->signature === '') {
            throw new GameApiException(
                'SGX signature belum dikonfigurasi.',
                500
            );
        }
    }

    public function getApiName()
    {
        return 'sgx';
    }

    /*
    |--------------------------------------------------------------------------
    | Provider list
    |--------------------------------------------------------------------------
    */

    public function providerList()
    {
        $request = $this->postSgx(
            $this->authPayload('provider_list')
        );

        $response = $request['body'];

        $this->assertSuccess(
            $response,
            'SGX provider_list gagal.'
        );

        $rawProviders = isset(
            $response['data']['providers']
        )
            ? $response['data']['providers']
            : array();

        if (!is_array($rawProviders)) {
            throw new GameApiException(
                'Format provider SGX tidak valid.',
                502
            );
        }

        $providers = array();

        foreach ($rawProviders as $provider) {
            if (!is_array($provider)) {
                continue;
            }

            $code = strtoupper(trim(
                isset($provider['provider_code'])
                    ? (string) $provider['provider_code']
                    : ''
            ));

            $name = trim(
                isset($provider['provider_name'])
                    ? (string) $provider['provider_name']
                    : ''
            );

            if ($code === '' || $name === '') {
                continue;
            }

            $providers[] = array(
                'api_provider' => 'sgx',
                'provider_code' => $code,
                'provider_name' => $name,
                'provider_type' => strtoupper(trim(
                    isset($provider['provider_type'])
                        ? (string) $provider['provider_type']
                        : ''
                )),
                'provider_image' => $this->normalizeImage(
                    isset($provider['provider_image'])
                        ? $provider['provider_image']
                        : ''
                ),
                'provider_status' => (
                    isset($provider['provider_status']) &&
                    (int) $provider['provider_status'] === 1
                ) ? 1 : 0,
            );
        }

        return array(
            'success' => true,
            'api' => 'sgx',
            'message' => $this->responseMessage(
                $response
            ),
            'data' => $providers,
            'total' => count($providers),
            'connection' => $request['connection'],
        );
    }

    /*
    |--------------------------------------------------------------------------
    | Game list
    |--------------------------------------------------------------------------
    */

    public function gameList($providerCode)
    {
        $providerCode = $this->validateProviderCode(
            $providerCode
        );

        $payload = $this->authPayload('game_list');
        $payload['provider_code'] = $providerCode;

        $request = $this->postSgx($payload);
        $response = $request['body'];

        $this->assertSuccess(
            $response,
            'SGX game_list gagal.'
        );

        $rawGames = isset($response['data']['games'])
            ? $response['data']['games']
            : array();

        if (!is_array($rawGames)) {
            throw new GameApiException(
                'Format game SGX tidak valid.',
                502
            );
        }

        $games = array();

        foreach ($rawGames as $game) {
            if (!is_array($game)) {
                continue;
            }

            $gameCode = trim(
                isset($game['game_code'])
                    ? (string) $game['game_code']
                    : ''
            );

            $gameName = trim(
                isset($game['game_name'])
                    ? (string) $game['game_name']
                    : ''
            );

            if ($gameCode === '' || $gameName === '') {
                continue;
            }

            $games[] = array(
                'api_provider' => 'sgx',
                'game_code' => $gameCode,
                'game_name' => $gameName,
                'game_provider' => strtoupper(trim(
                    isset($game['game_provider'])
                        ? (string) $game['game_provider']
                        : $providerCode
                )),
                'game_type' => strtoupper(trim(
                    isset($game['game_type'])
                        ? (string) $game['game_type']
                        : ''
                )),
                'game_image' => $this->normalizeImage(
                    isset($game['game_image'])
                        ? $game['game_image']
                        : ''
                ),
                'game_status' => (
                    isset($game['game_status']) &&
                    (int) $game['game_status'] === 1
                ) ? 1 : 0,
            );
        }

        return array(
            'success' => true,
            'api' => 'sgx',
            'provider_code' => $providerCode,
            'message' => $this->responseMessage(
                $response
            ),
            'data' => $games,
            'total' => count($games),
            'connection' => $request['connection'],
        );
    }

    /*
    |--------------------------------------------------------------------------
    | Create user
    |--------------------------------------------------------------------------
    | DUPLICATED_USER dinormalisasi sebagai sukses idempotent karena akun
    | provider berarti sudah tersedia.
    */

    public function createUser(
        $playerCode,
        array $options = array()
    ) {
        $playerCode = $this->validatePlayerCode(
            $playerCode
        );

        $payload = $this->authPayload('user_create');
        $payload['user_code'] = $playerCode;

        $request = $this->postSgx($payload);
        $response = $request['body'];

        $status = $this->responseStatus($response);
        $message = $this->responseMessage($response);

        if (
            $status === 403 &&
            strtoupper($message) === 'DUPLICATED_USER'
        ) {
            return array(
                'success' => true,
                'api' => 'sgx',
                'player_code' => $playerCode,
                'balance' => null,
                'already_exists' => true,
                'message' => 'DUPLICATED_USER',
                'connection' => $request['connection'],
            );
        }

        $this->assertSuccess(
            $response,
            'SGX user_create gagal.'
        );

        $data = $this->responseData($response);

        return array(
            'success' => true,
            'api' => 'sgx',
            'player_code' => isset($data['user_code'])
                ? (string) $data['user_code']
                : $playerCode,
            'balance' => $this->formatMoney(
                isset($data['user_balance'])
                    ? $data['user_balance']
                    : 0
            ),
            'already_exists' => false,
            'message' => $message,
            'connection' => $request['connection'],
        );
    }

    /*
    |--------------------------------------------------------------------------
    | Agent info
    |--------------------------------------------------------------------------
    */

    public function getAgentInfo()
    {
        $request = $this->postSgx(
            $this->authPayload('agent_info')
        );

        $response = $request['body'];

        $this->assertSuccess(
            $response,
            'SGX agent_info gagal.'
        );

        $data = $this->responseData($response);

        return array(
            'success' => true,
            'api' => 'sgx',
            'agent_code' => isset($data['agent_code'])
                ? (string) $data['agent_code']
                : $this->agentCode,
            'balance' => $this->formatMoney(
                isset($data['balance'])
                    ? $data['balance']
                    : 0
            ),
            'message' => $this->responseMessage(
                $response
            ),
            'connection' => $request['connection'],
        );
    }

    /*
    |--------------------------------------------------------------------------
    | User info / balance
    |--------------------------------------------------------------------------
    */

    public function getBalance($playerCode)
    {
        $playerCode = $this->validatePlayerCode(
            $playerCode
        );

        $payload = $this->authPayload('user_info');
        $payload['user_code'] = $playerCode;

        $request = $this->postSgx($payload);
        $response = $request['body'];

        $this->assertSuccess(
            $response,
            'SGX user_info gagal.'
        );

        $data = $this->responseData($response);

        return array(
            'success' => true,
            'api' => 'sgx',
            'player_code' => isset($data['user_code'])
                ? (string) $data['user_code']
                : $playerCode,
            'balance' => $this->formatMoney(
                isset($data['balance'])
                    ? $data['balance']
                    : 0
            ),
            'message' => $this->responseMessage(
                $response
            ),
            'connection' => $request['connection'],
        );
    }

    /*
    |--------------------------------------------------------------------------
    | Deposit
    |--------------------------------------------------------------------------
    | Dokumentasi SGX tidak mencantumkan parameter idempotency. transactionId
    | tetap divalidasi dan dicatat lokal, tetapi tidak dikirim ke provider.
    */

    public function deposit(
        $playerCode,
        $amount,
        $transactionId,
        array $options = array()
    ) {
        return $this->transferBalance(
            'user_deposit',
            $playerCode,
            $amount,
            $transactionId
        );
    }

    /*
    |--------------------------------------------------------------------------
    | Withdraw
    |--------------------------------------------------------------------------
    */

    public function withdraw(
        $playerCode,
        $amount,
        $transactionId,
        array $options = array()
    ) {
        return $this->transferBalance(
            'user_withdraw',
            $playerCode,
            $amount,
            $transactionId
        );
    }

    /*
    |--------------------------------------------------------------------------
    | Launch game
    |--------------------------------------------------------------------------
    */

    public function launchGame(
        $playerCode,
        $gameCode,
        array $options = array()
    ) {
        $playerCode = $this->validatePlayerCode(
            $playerCode
        );

        $gameCode = $this->validateGameCode(
            $gameCode
        );

        $payload = $this->authPayload('game_launch');
        $payload['user_code'] = $playerCode;
        $payload['game_code'] = $gameCode;

        $request = $this->postSgx($payload);
        $response = $request['body'];

        $this->assertSuccess(
            $response,
            'SGX game_launch gagal.'
        );

        $launchUrl = isset($response['launch_url'])
            ? trim((string) $response['launch_url'])
            : '';

        if ($launchUrl === '') {
            throw new GameApiException(
                'SGX tidak mengirim launch_url.',
                502
            );
        }

        return array(
            'success' => true,
            'api' => 'sgx',
            'player_code' => $playerCode,
            'game_code' => $gameCode,
            'launch_url' => $this->validateWebUrl(
                $launchUrl,
                'Launch URL'
            ),
            'provider_reference' => null,
            'message' => $this->responseMessage(
                $response
            ),
            'connection' => $request['connection'],
        );
    }

    /*
    |--------------------------------------------------------------------------
    | Game history
    |--------------------------------------------------------------------------
    | SGX tidak mendokumentasikan filter user_code. Adapter mengambil history
    | agent lalu menyaring user_code secara lokal.
    |
    | Filter opsional:
    | - limit: default 300, maksimum 500
    | - all_users: true untuk tidak menyaring player
    */

    public function getProviderHistory(
        $playerCode,
        array $filters = array()
    ) {
        $playerCode = $this->validatePlayerCode(
            $playerCode
        );

        $limit = isset($filters['limit'])
            ? (int) $filters['limit']
            : 300;

        if ($limit < 1 || $limit > 500) {
            throw new GameApiException(
                'Limit history SGX harus 1 sampai 500.',
                422
            );
        }

        $payload = $this->authPayload('game_history');
        $payload['limit'] = $limit;

        $request = $this->postSgx($payload);
        $response = $request['body'];

        $status = $this->responseStatus($response);
        $message = $this->responseMessage($response);

        if (
            $status === 404 &&
            strtoupper($message) === 'DATA_NOT_FOUND'
        ) {
            return array(
                'success' => true,
                'api' => 'sgx',
                'player_code' => $playerCode,
                'data' => array(),
                'total' => 0,
                'message' => 'DATA_NOT_FOUND',
                'connection' => $request['connection'],
            );
        }

        $this->assertSuccess(
            $response,
            'SGX game_history gagal.'
        );

        $data = $this->responseData($response);
        $records = isset($data['data'])
            ? $data['data']
            : array();

        if (!is_array($records)) {
            throw new GameApiException(
                'Format game_history SGX tidak valid.',
                502
            );
        }

        $allUsers = !empty($filters['all_users']);
        $filtered = array();

        foreach ($records as $record) {
            if (!is_array($record)) {
                continue;
            }

            if ($allUsers) {
                $filtered[] = $record;
                continue;
            }

            $recordUser = isset($record['user_code'])
                ? (string) $record['user_code']
                : '';

            if ($recordUser === $playerCode) {
                $filtered[] = $record;
            }
        }

        return array(
            'success' => true,
            'api' => 'sgx',
            'player_code' => $playerCode,
            'data' => $filtered,
            'total' => count($filtered),
            'message' => $message,
            'connection' => $request['connection'],
        );
    }

    /*
    |--------------------------------------------------------------------------
    | Agent-wide game history
    |--------------------------------------------------------------------------
    */

    public function getAgentHistory($limit = 300)
    {
        $limit = (int) $limit;

        if ($limit < 1 || $limit > 500) {
            throw new GameApiException(
                'Limit history SGX harus 1 sampai 500.',
                422
            );
        }

        $payload = $this->authPayload('game_history');
        $payload['limit'] = $limit;

        $request = $this->postSgx($payload);
        $response = $request['body'];

        $status = $this->responseStatus($response);
        $message = $this->responseMessage($response);

        if (
            $status === 404 &&
            strtoupper($message) === 'DATA_NOT_FOUND'
        ) {
            return array(
                'success' => true,
                'api' => 'sgx',
                'data' => array(),
                'total' => 0,
                'message' => 'DATA_NOT_FOUND',
                'connection' => $request['connection'],
            );
        }

        $this->assertSuccess(
            $response,
            'SGX game_history gagal.'
        );

        $data = $this->responseData($response);
        $records = isset($data['data'])
            ? $data['data']
            : array();

        if (!is_array($records)) {
            throw new GameApiException(
                'Format game_history SGX tidak valid.',
                502
            );
        }

        return array(
            'success' => true,
            'api' => 'sgx',
            'data' => $records,
            'total' => count($records),
            'message' => $message,
            'connection' => $request['connection'],
        );
    }

    /*
    |--------------------------------------------------------------------------
    | Internal helpers
    |--------------------------------------------------------------------------
    */

    private function transferBalance(
        $command,
        $playerCode,
        $amount,
        $transactionId
    ) {
        $playerCode = $this->validatePlayerCode(
            $playerCode
        );

        $amount = $this->normalizeAmount($amount);

        $transactionId = $this->validateTransactionId(
            $transactionId
        );

        $payload = $this->authPayload($command);
        $payload['user_code'] = $playerCode;
        $payload['amount'] = (float) $amount;

        $request = $this->postSgx($payload);
        $response = $request['body'];

        $this->assertSuccess(
            $response,
            'SGX ' . $command . ' gagal.'
        );

        $data = $this->responseData($response);

        return array(
            'success' => true,
            'api' => 'sgx',
            'operation' => $command === 'user_deposit'
                ? 'deposit'
                : 'withdraw',
            'player_code' => $playerCode,
            'transaction_id' => $transactionId,
            'amount' => $amount,
            'agent_balance' => $this->formatMoney(
                isset($data['agent_balance'])
                    ? $data['agent_balance']
                    : 0
            ),
            'user_balance' => $this->formatMoney(
                isset($data['user_balance'])
                    ? $data['user_balance']
                    : 0
            ),
            'balance' => $this->formatMoney(
                isset($data['user_balance'])
                    ? $data['user_balance']
                    : 0
            ),
            /*
             * Tidak ada provider transaction reference pada dokumentasi SGX.
             */
            'provider_reference' => null,
            'message' => $this->responseMessage(
                $response
            ),
            'connection' => $request['connection'],
        );
    }

    private function postSgx(array $payload)
    {
        /*
         * SGX dapat menggunakan kode HTTP yang sama dengan field status.
         * Kode ini diterima agar body JSON bisa dinormalisasi oleh adapter.
         */
        return $this->postJson(
            $payload,
            array(400, 403, 404, 429, 503)
        );
    }

    private function authPayload($command)
    {
        return array(
            'command' => (string) $command,
            'agent_code' => $this->agentCode,
            'signature' => $this->signature,
        );
    }

    private function assertSuccess(
        array $response,
        $fallbackMessage
    ) {
        $status = $this->responseStatus($response);

        if ($status === 200) {
            return;
        }

        $message = $this->responseMessage($response);

        if ($message === '') {
            $message = (string) $fallbackMessage;
        }

        $details = array(
            'api' => 'sgx',
            'provider_status' => $status,
        );

        if (
            isset($response['data']) &&
            is_array($response['data'])
        ) {
            foreach (
                array(
                    'reason',
                    'block_seconds',
                    'remaining_seconds',
                ) as $field
            ) {
                if (isset($response['data'][$field])) {
                    $details[$field] =
                        $response['data'][$field];
                }
            }
        }

        throw new GameApiException(
            $message,
            $this->mapProviderStatusToCode($status),
            $details
        );
    }

    private function responseStatus(array $response)
    {
        return isset($response['status'])
            ? (int) $response['status']
            : 0;
    }

    private function responseMessage(array $response)
    {
        return isset($response['message'])
            ? trim((string) $response['message'])
            : '';
    }

    private function responseData(array $response)
    {
        return (
            isset($response['data']) &&
            is_array($response['data'])
        ) ? $response['data'] : array();
    }

    private function mapProviderStatusToCode($status)
    {
        $status = (int) $status;

        if (
            in_array(
                $status,
                array(400, 403, 404, 429, 503),
                true
            )
        ) {
            return $status;
        }

        return 502;
    }

    private function formatMoney($value)
    {
        if (!is_numeric($value)) {
            return '0.00';
        }

        return number_format(
            (float) $value,
            2,
            '.',
            ''
        );
    }

    private function validateWebUrl(
        $url,
        $label
    ) {
        $url = trim((string) $url);

        if (
            $url === '' ||
            filter_var(
                $url,
                FILTER_VALIDATE_URL
            ) === false
        ) {
            throw new GameApiException(
                $label . ' tidak valid.',
                502
            );
        }

        $scheme = strtolower(
            (string) parse_url(
                $url,
                PHP_URL_SCHEME
            )
        );

        /*
         * Dokumentasi SGX memperlihatkan launch_url HTTP.
         */
        if (
            !in_array(
                $scheme,
                array('http', 'https'),
                true
            )
        ) {
            throw new GameApiException(
                $label .
                ' menggunakan protokol tidak valid.',
                502
            );
        }

        return $url;
    }
}
