<?php

namespace App\Http\Controllers\User;

use App\Helpers\MsgState;
use App\Models\Transaction;

use Illuminate\Http\Request;
use App\Enums\TransactionType;
use App\Enums\TransactionStatus;

use App\Filters\TransactionFilter;
use App\Http\Controllers\Controller;
use App\Services\Withdraw\WithdrawProcessor;
use App\Exceptions\User\Transaction\DepositTryMethodException;
use Illuminate\Validation\ValidationException;
use App\Services\Transaction\TransactionProcessor;
use App\Exceptions\User\Transaction\DepositWrongException;
use App\Exceptions\User\Transaction\DepositNoRateException;
use App\Exceptions\User\Transaction\WithdrawWrongException;
use App\Http\Requests\User\Transaction\DepositPreviewRequest;
use App\Http\Requests\User\Transaction\WithdrawPreviewRequest;
use App\Exceptions\User\Transaction\DepositInvalidMethodException;
use App\Exceptions\User\Transaction\TnxCancelTimeoutException;
use App\Exceptions\User\Transaction\TnxErrorException;
use App\Exceptions\User\Transaction\TnxInvalidActionException;
use App\Exceptions\User\Transaction\TnxInvalidStatusException;
use App\Exceptions\User\Transaction\WithdrawInvalidMethodException;
use App\Exceptions\User\Transaction\WithdrawInvalidAccountException;
use App\Exceptions\User\Transaction\WithdrawAmountValidationException;
use App\Exceptions\User\Transaction\WithdrawInvalidActionException;
use App\Exceptions\User\Transaction\WithdrawNoRateException;
use App\Services\Transaction\User\TransactionService as UserTransactionService;

class TransactionController extends Controller
{
    // private $exchangeRateApi;
    private $userTransactionService;

    public function __construct(UserTransactionService $userTransactionService)
    {
        // $this->exchangeRateApi = new ExchangeRateApi();
        $this->userTransactionService = $userTransactionService;
    }

    /**
     * @param Request $request
     * @param TransactionFilter $filter
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
     * @throws \ReflectionException
     * @version 1.0.0
     * @since 1.0
     */
    public function list(Request $request, TransactionFilter $filter)
    {
        $data = $this->userTransactionService->getTnxList($request, $filter);

        return view('user.transaction.list', $data);
    }

    /**
     * @param $name
     * @return mixed
     * @version 1.0.0
     * @since 1.0
     */
    private function getSupportContext($name = null)
    {
        return MsgState::helps('support');
    }

    /**
     * @param $activeMethods
     * @return array
     * @version 1.0.0
     * @since 1.0
     */
    private function validateDepositDefault($activeMethods)
    {
        if (sys_settings('deposit_disable_request') == 'yes') {
            return MsgState::of('disable', 'deposit');
        }

        if (blank($activeMethods)) {
            return MsgState::of('no-method', 'deposit');
        }

        if (!auth()->user()->is_verified) {
            return MsgState::of('verify-email', 'account');
        }

        if (kyc_required('deposit', true)) {
            return module_msg_of('deposit', 'error', 'BasicKYC');
        }

        if ($limit_queue = sys_settings('deposit_limit_request')) {
            $pending_queue = Transaction::where('user_id', auth()->id())
                ->where('type', TransactionType::DEPOSIT)->where('status', TransactionStatus::PENDING)->count();

            if ($pending_queue >= $limit_queue) {
                return MsgState::of('limit', 'deposit');
            }
        }
        return [];
    }

    /**
     * @param $status
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
     * @version 1.0.0
     * @since 1.0
     */
    public function depositPaymentMethod($status = null)
    {
        $activeMethods = $this->userTransactionService->getPaymentMethodList()->keyBy('slug');

        $errors = $this->validateDepositDefault($activeMethods);

        return view('user.transaction.deposit-method', compact('activeMethods', 'errors'));
    }

    /**
     * @param Request $request
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
     * @version 1.0.0
     * @since 1.0
     */
    public function depositAmount(Request $request)
    {
        try {
            $pm = $request->get('deposit_method');
            $data = $this->userTransactionService->depositAmount($pm);

            return view('user.transaction.deposit-amount', $data);
        } catch (DepositInvalidMethodException $e) {
            $errors = MsgState::of('invalid-method', 'deposit');

            return view('user.transaction.error-state', $errors);
        }
    }

    /**
     * @param Request $request
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
     * @throws ValidationException
     * @version 1.0.0
     * @since 1.0
     */
    public function depositPreview(DepositPreviewRequest $request)
    {
        try {
            $data = $this->userTransactionService->depositPreview($request);
            return view('user.transaction.deposit-preview', $data);
        } catch (DepositWrongException $e) {
            $errors = MsgState::of('wrong', 'deposit');
            return view('user.transaction.error-state', $errors);
        } catch (DepositInvalidMethodException $e) {
            $errors = MsgState::of('invalid-method', 'deposit');
            return view('user.transaction.error-state', $errors);
        } catch (DepositNoRateException $e) {
            $errors = MsgState::of('no-rate', 'deposit');
            return view('user.transaction.error-state', $errors);
        }
    }

    /**
     * @param Request $request
     * @return mixed
     * @throws ValidationException
     * @version 1.0.0
     * @since 1.0
     */
    public function depositConfirm(Request $request)
    {
        try {
            return $this->userTransactionService->depositConfirm($request);
        } catch (DepositTryMethodException $e) {
            $errors = MsgState::of('try-method', 'deposit');
            return view('user.transaction.error-state', $errors);
        }
    }

    /**
     * @param $methods
     * @return array
     * @version 1.0.0
     * @since 1.0
     */
    private function validateWithdrawDefault($methods)
    {
        if (sys_settings('withdraw_disable_request') == 'yes') {
            return MsgState::of('disable', 'withdraw');
        }

        if (blank($methods)) {
            return MsgState::of('no-method', 'withdraw');
        }

        if (!$this->userTransactionService->hasUserAccounts()) {
            return MsgState::of('add-method', 'account');
        }

        if (!$this->userTransactionService->hasAccountBalance()) {
            return MsgState::of('no-fund', 'account');
        }

        if (kyc_required('withdraw', true)) {
            return module_msg_of('withdraw', 'error', 'BasicKYC');
        }

        if ($limit_queue = sys_settings('withdraw_limit_request')) {
            $pending_queue = Transaction::where('user_id', auth()->id())
                ->where('type', TransactionType::WITHDRAW)->where('status', TransactionStatus::PENDING)->count();

            if ($pending_queue >= $limit_queue) {
                return MsgState::of('limit', 'withdraw');
            }
        }
        return [];
    }

    /**
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
     * @version 1.0.0
     * @since 1.0
     */
    public function showWithdrawMethod()
    {
        $activeMethods = $this->userTransactionService->getWithdrawMethodList()->keyBy('slug');

        $balance = $this->userTransactionService->getAccountBalance(AccType('main'), true);
        $errors = $this->validateWithdrawDefault($activeMethods);

        return view('user.transaction.withdraw-method', compact('activeMethods', 'balance', 'errors'));
    }

    /**
     * @param Request $request
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
     * @throws ValidationException
     * @version 1.0.0
     * @since 1.0
     */
    public function withdrawAmount(Request $request)
    {
        try {
            $data = $this->userTransactionService->withdrawAmount($request->get('withdraw_method', user_temp_meta('withdraw_method.slug')));
            return view('user.transaction.withdraw-amount', $data);
        } catch (WithdrawInvalidMethodException $e) {
            return view('user.transaction.error-state', MsgState::of('invalid-method', 'withdraw'));
        } catch (WithdrawInvalidAccountException $e) {
            $errors = $e->getData();
            return view('user.transaction.error-state', $errors);
        }
    }

    /**
     * @param Request $request
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
     * @throws ValidationException
     * @version 1.0.0
     * @since 1.0
     */
    public function withdrawPreview(WithdrawPreviewRequest $request)
    {
        try {
            $data = $this->userTransactionService->withdrawPreview($request);
            return view('user.transaction.withdraw-preview', $data);
        } catch (WithdrawWrongException $e) {
            $errors = $e->getData();
            return view('user.transaction.error-state', $errors);
        } catch (WithdrawAmountValidationException $e) {
            $errors = $e->getData();
            return view('user.transaction.error-state', $errors);
        } catch (WithdrawNoRateException $e) {
            $errors = MsgState::of('no-rate', 'withdraw');
            return view('user.transaction.error-state', $errors);
        }
    }

    /**
     * @param Request $request
     * @return mixed
     * @throws ValidationException
     * @version 1.0.0
     * @since 1.0
     */
    public function withdrawConfirm(Request $request)
    {
        $transaction = $this->userTransactionService->withdrawConfirm($request);
        return view('user.transaction.withdraw-confirm', compact('transaction'));
    }

    /**
     * @param $status
     * @param $tnx null
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\View\View
     * @version 1.0.0
     * @since 1.0
     */
    public function depositComplete(Request $request, $status, $tnx = null)
    {
        $tnx = get_hash($tnx);
        $transaction = Transaction::loggedUser()->find($tnx);
        return $this->finalTransactionUpdate($transaction, $status, $request);
    }

    /**
     * @param $status
     * @param $tnx null
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\View\View
     * @version 1.0.0
     * @since 1.2.0
     */
    public function actionTransactionStatus(Request $request, $status, $tnx = null)
    {
        $tnx = get_hash($tnx);
        $transaction = Transaction::loggedUser()->find($tnx);
        return $this->finalTransactionUpdate($transaction, $status, $request);
    }

    public function viewTransactionDetails(Request $request)
    {
        $id = get_hash($request->get("id"));
        $transaction = Transaction::loggedUser()->find($id);

        if (blank($transaction)) {
            throw ValidationException::withMessages(["invalid" => __("Invalid transaction id or not found.")]);
        }

        if ($transaction->type == TransactionType::INVESTMENT && $transaction->status == TransactionStatus::PENDING) {
            if ($request->ajax()) {
                return response()->json(view('user.transaction.details', compact('transaction'))->render());
            }
            return abort(404);
        }

        if ($transaction->type == TransactionType::REFERRAL) {
            if ($request->ajax()) {
                return response()->json(view('user.transaction.details-referral', compact('transaction'))->render());
            }
            return abort(404);
        }

        if ($transaction->type == TransactionType::TRANSFER) {
            if ($request->ajax()) {
                return response()->json(view('user.transaction.details', compact('transaction'))->render());
            }
            return abort(404);
        }

        if ($transaction->status != TransactionStatus::PENDING) {
            if ($request->ajax()) {
                return response()->json(view('user.transaction.details', compact('transaction'))->render());
            }
            return abort(404);
        }

        if ($transaction->type == TransactionType::DEPOSIT) {
            $transactionProcessor = new TransactionProcessor();
            return $transactionProcessor->getTransactionDetailsView($transaction);
        } elseif ($transaction->type == TransactionType::WITHDRAW) {
            $withdrawProcessor = new WithdrawProcessor();
            return $withdrawProcessor->getWithdrawDetailsView($transaction);
        }
    }

    private function finalTransactionUpdate($transaction, $status, $request, $online = false)
    {
        try {
            $data = $this->userTransactionService->finalTransactionUpdate($transaction, $status, $online);
            return view('user.transaction.status-action', $data);
        } catch (TnxErrorException $e) {
            return redirect()->route('dashboard')->withErrors(['tnx_error' => $e->getMessage()]);
        } catch (DepositWrongException $e) {
            if ($request->ajax()) {
                $errors = MsgState::of('wrong', 'deposit');
                return view('user.transaction.error-state', $errors);
            } else {
                return view('user.transaction.status-action', [
                    'transaction' => $transaction,
                    'contentBlade' => 'deposit-wrong',
                    'status' => $status,
                ]);
            }
        } catch (TnxInvalidActionException $e) {
            $errors = $e->getData();
            return view('user.transaction.errors', compact('errors'));
        } catch (TnxInvalidStatusException $e) {
            return redirect()->route('dashboard');
        } catch (WithdrawInvalidActionException $e) {
            $errors = $e->getData();
            return view('user.transaction.errors', compact('errors'));
        } catch (TnxCancelTimeoutException $e) {
            $errors = $e->getData();
            return view('user.transaction.errors', compact('errors'));
        }
    }

    public function onlineDepositComplete(Request $request, $status, $tnx)
    {
        $transaction = Transaction::loggedUser()->where('tnx', $tnx)->first();
        return $this->finalTransactionUpdate($transaction, $status, $request, true);
    }
}
