<?php

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

class WalletService
{
    private $pdo;
    private $walletRepository;

    public function __construct(
        PDO $pdo,
        WalletRepository $walletRepository
    ) {
        $this->pdo = $pdo;
        $this->walletRepository = $walletRepository;
    }

    public function getBalance($userId)
    {
        $this->walletRepository->createIfMissing($userId);

        $wallet = $this->walletRepository->findByUserId($userId);

        return $wallet
            ? number_format((float) $wallet['balance'], 2, '.', '')
            : '0.00';
    }

    public function credit($userId, $amount)
    {
        return $this->changeBalance($userId, $amount, 'credit');
    }

    public function debit($userId, $amount)
    {
        return $this->changeBalance($userId, $amount, 'debit');
    }

    private function changeBalance($userId, $amount, $mode)
    {
        if (!is_numeric($amount) || (float) $amount <= 0) {
            throw new InvalidArgumentException(
                'Nominal wallet tidak valid.'
            );
        }

        $amount = (float) number_format(
            (float) $amount,
            2,
            '.',
            ''
        );

        $startedHere = !$this->pdo->inTransaction();

        try {
            if ($startedHere) {
                $this->pdo->beginTransaction();
            }

            $this->walletRepository->createIfMissing($userId);

            $wallet = $this->walletRepository->lockByUserId($userId);

            if (!$wallet) {
                throw new RuntimeException(
                    'Wallet user tidak ditemukan.'
                );
            }

            $balanceBefore = (float) $wallet['balance'];

            if ($mode === 'debit') {
                if ($balanceBefore < $amount) {
                    throw new RuntimeException(
                        'Saldo lokal tidak mencukupi.'
                    );
                }

                $balanceAfter = $balanceBefore - $amount;
            } else {
                $balanceAfter = $balanceBefore + $amount;
            }

            $updated = $this->walletRepository->updateBalance(
                $userId,
                $balanceAfter,
                (int) $wallet['version']
            );

            if (!$updated) {
                throw new RuntimeException(
                    'Wallet berubah oleh proses lain. Silakan ulangi.'
                );
            }

            if ($startedHere) {
                $this->pdo->commit();
            }

            return array(
                'success' => true,
                'balance_before' => number_format(
                    $balanceBefore,
                    2,
                    '.',
                    ''
                ),
                'balance_after' => number_format(
                    $balanceAfter,
                    2,
                    '.',
                    ''
                ),
                'amount' => number_format($amount, 2, '.', ''),
            );
        } catch (Throwable $e) {
            if ($startedHere && $this->pdo->inTransaction()) {
                $this->pdo->rollBack();
            }

            throw $e;
        }
    }
}
