<?php

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

class GameSessionService
{
    private $pdo;
    private $userRepository;
    private $gameSessionRepository;

    public function __construct(
        PDO $pdo,
        UserRepository $userRepository,
        GameSessionRepository $gameSessionRepository
    ) {
        $this->pdo = $pdo;
        $this->userRepository = $userRepository;
        $this->gameSessionRepository = $gameSessionRepository;
    }

    public function launch(
        $userId,
        $apiProvider,
        $providerCode,
        $gameCode,
        array $options = array()
    ) {
        $apiProvider = strtolower(
            trim((string) $apiProvider)
        );

        if (
            !in_array(
                $apiProvider,
                array('sgx', 'nexus'),
                true
            )
        ) {
            throw new InvalidArgumentException(
                'API provider tidak valid.'
            );
        }

        $providerCode = strtoupper(
            trim((string) $providerCode)
        );

        if ($providerCode === '') {
            throw new InvalidArgumentException(
                'Provider code wajib diisi.'
            );
        }

        $playerCode = $this->userRepository->getPlayerCode(
            $userId,
            $apiProvider
        );

        if (
            $playerCode === null ||
            $playerCode === ''
        ) {
            throw new GameApiException(
                'Akun provider user belum tersedia.',
                404
            );
        }

        try {
            $this->pdo->beginTransaction();

            $active = $this->gameSessionRepository
                ->findActiveByUserId(
                    $userId,
                    true
                );

            if ($active) {
                throw new RuntimeException(
                    'Masih ada sesi game aktif.'
                );
            }

            $sessionId = $this->createSessionId(
                $apiProvider,
                $userId
            );

            $this->gameSessionRepository->create(
                array(
                    'session_id' => $sessionId,
                    'user_id' => $userId,
                    'api_provider' => $apiProvider,
                    'provider_code' => $providerCode,
                    'game_code' => trim(
                        (string) $gameCode
                    ),
                    'amount_sent' => isset(
                        $options['amount_sent']
                    )
                        ? $options['amount_sent']
                        : 0,
                    'status' => 'opening',
                )
            );

            $this->pdo->commit();
        } catch (Throwable $e) {
            if ($this->pdo->inTransaction()) {
                $this->pdo->rollBack();
            }

            throw $e;
        }

        /*
         * Adapter Nexus membutuhkan provider_code
         * di dalam options.
         */
        $options['provider_code'] = $providerCode;

        try {
            $result = GameApi::adapter(
                $apiProvider
            )->launchGame(
                $playerCode,
                $gameCode,
                $options
            );

            $this->gameSessionRepository->updateStatus(
                $sessionId,
                'active',
                isset($result['provider_reference'])
                    ? $result['provider_reference']
                    : null
            );

            $result['session_id'] = $sessionId;

            return $result;
        } catch (Throwable $e) {
            $this->gameSessionRepository->updateStatus(
                $sessionId,
                'failed'
            );

            throw $e;
        }
    }

    /*
    |--------------------------------------------------------------------------
    | Close sementara
    |--------------------------------------------------------------------------
    | Belum menarik balance provider. Jangan gunakan sebagai final close-game
    | sampai alur balance -> withdraw -> wallet ledger selesai dibuat.
    */

    public function close($userId)
    {
        $active = $this->gameSessionRepository
            ->findActiveByUserId(
                $userId,
                false
            );

        if (!$active) {
            return array(
                'success' => true,
                'message' =>
                    'Tidak ada sesi game aktif.',
            );
        }

        $this->gameSessionRepository->updateStatus(
            $active['session_id'],
            'closed'
        );

        return array(
            'success' => true,
            'message' =>
                'Sesi game ditutup secara lokal.',
            'session_id' =>
                $active['session_id'],
            'warning' =>
                'Saldo provider belum ditarik oleh method close sementara.',
        );
    }

    private function createSessionId(
        $apiProvider,
        $userId
    ) {
        return strtoupper($apiProvider) .
            '-SESSION-' .
            (int) $userId . '-' .
            gmdate('YmdHis') . '-' .
            bin2hex(random_bytes(8));
    }
}
