<?php

namespace Yajny\Member\BalanceTransactions;

use Illuminate\Support\Facades\Log;
use Yajny\Member\Entities\BalanceTransactions\MappedTransaction;
use Yajny\Member\Entities\BalanceTransactions\ProcessedTransaction;
use Yajny\Member\Entities\BalanceTransactions\RawTransaction;
use Yajny\Member\Entities\BalanceTransactions\TransactionSourceTypes;
use Yajny\Member\Entities\BalanceTransactions\UserBalanceCalculationObject;
use Yajny\Member\Exceptions\InvalidTransactionSourceTypeException;
use Yajny\Member\Models\BalanceTransaction;
use Yajny\Member\Models\Member;
use Yajny\Member\Models\ProcessedBalanceTransaction;

abstract class BalanceTransactionFactory
{
    protected $transactionAmount;
    protected $userID;
    protected $user;
    public static $eligibleSourceTypes = [
        TransactionSourceTypes::CASHBACK,
        TransactionSourceTypes::BONUS,
        TransactionSourceTypes::NEWBONUS,
        TransactionSourceTypes::PAYOUT
    ];

    public function __construct($userID, $transactionAmount)
    {
        $this->userID = $userID;
        $this->transactionAmount = $transactionAmount;
        $this->user = Member::findOrFail($userID);
    }

    public abstract function initTransaction(): RawTransaction;

    public abstract static function mapRawTransactionToTransaction(UserBalanceCalculationObject $userBalance, RawTransaction $rawTransaction): MappedTransaction;

    public function isEligible($sourceType): bool
    {
        return $this->eligibleSourceType == $sourceType;
    }

    /**
     * @param UserBalanceCalculationObject $userBalance
     * @param RawTransaction $rawTransaction
     * @return MappedTransaction
     * @throws InvalidTransactionSourceTypeException
     */
    public function mapTransaction(UserBalanceCalculationObject $userBalance, RawTransaction $rawTransaction): MappedTransaction
    {
        foreach (self::$eligibleSourceTypes as $sourceType) {
            if ($this->isEligible($sourceType))
                return $this->mapRawTransactionToTransaction($userBalance, $rawTransaction);
        }

        throw new InvalidTransactionSourceTypeException();
    }

    public final function saveTransaction($unprocessed_transaction_id = null)
    {
        $rawTransaction = $this->initTransaction();
        $userBalance = new UserBalanceCalculationObject($this->userID, $this->user->pending_balance, $this->user->confirmed_balance);
        try {
            $unprocessedFlag = true;
            if ($unprocessed_transaction_id) {
                $processedTransactionObject = ProcessedBalanceTransaction::find($unprocessed_transaction_id);
                $unprocessedFlag = false;
            } else {
                $processedTransaction = new ProcessedTransaction($rawTransaction);
                $processedTransactionObject = new ProcessedBalanceTransaction($processedTransaction->toArray());
                $processedTransactionObject->save();
            }

            $balanceTransaction = $this->mapTransaction($userBalance, $rawTransaction);

            if (!$unprocessedFlag || !$processedTransactionObject->hasUnprocessed()) {
                $balanceTransactionObject = new BalanceTransaction($balanceTransaction->toArray());
                $balanceTransactionObject->saveTransaction();
                $processedTransactionObject->is_processed = 1;
                $processedTransactionObject->save();
            }
        } catch (\Exception $exception) {
            Log::error('BalanceTransactionFactory::saveTransaction Error - ' . $exception->getMessage(), [
                'exception' => $exception,
                'raw_transactions' => $rawTransaction->toArray()
            ]);
        }
    }
}