<?php

namespace App\Http\Controllers\Gateway;

use Carbon\Carbon;
use App\Models\Plan;
use App\Models\User;
use App\Models\Board;
use App\Models\Deposit;
use App\Models\Gateway;
use App\Models\Transaction;
use Illuminate\Http\Request;
use App\Models\GatewayCurrency;
use App\Rules\FileTypeValidate;
use App\Models\AdminNotification;
use App\Http\Controllers\Controller;
use App\Http\Services\WalletServices;
use App\Http\Component\AppController;
use App\Http\Services\PaymentServices;
use Illuminate\Support\Facades\Validator;

class PaymentController extends AppController
{
    private $wallet;
    private $activeTemplate;

    public function __construct(
        WalletServices $wallet
    )
    {
        $this->activeTemplate = activeTemplate();
        $this->wallet = $wallet;
    }

    public function callback($status = null, $id = null)
    {

        try {

            // Exception
            if (!$id || !$status) throw new \Exception("Action not valid");

            // Find Transaction
            $data = Deposit::with('gateway')->where('trx', $id)->first();

            // Verify transaction
            if (!$data && !session()->has('notify')) throw new \Exception("Transaction id $id not exist");

            $notify[] = ['success', 'You have deposit request has been taken.'];
            return redirect()->route('user.transaction')->withNotify($notify);

        } catch (\Exception $th) {
            //throw $th;
            $notify[] = ["error", $th->getMessage()];
            return redirect()->route('user.transaction')->withNotify($notify);
        }
    }

    public function deposit($currency = null)
    {
        $user = auth()->user();

        /*$pend = Deposit::where('user_id', $user->id)->where('status', '2')->first();
        if ($pend) {
            $notify[] = ['error', 'You currently have a pending deposit, please wait for confirmation or cancellation and try again'];
            return back()->withNotify($notify);
        }*/

        $currency_session = ($currency) ? $currency : config('settings')->cur_text;
        $currency = currency($currency_session);

        if(!$currency) return back()->withNotify([['error', 'Currency not support']]);

        $userBalance = $this->wallet->getBalance($user, $currency->cur_text);

        if($userBalance instanceOf \Exception) {
            $notify[] = ['error', $userBalance->getMessage()];
            return back()->withNotify($notify);
        }

        $gatewayCurrency = GatewayCurrency::whereHas('method', function ($gate) {
            $gate->where('status', 1);
        })->with('method')->orderby('method_code')->get();

        //$gatewayCurrency = Gateway::where(['status' => 1])->orderby('name', 'desc')->get();

        // Recharge not available
        if(!$gatewayCurrency) {
            $notify[] = ['error', 'Sorry recharge not available'];
            return back()->withNotify($notify);
        }

        $plans = Plan::where('platform', config('settings')->active_template)->where('min_amount', '>', 0)->where(['p_status' => 'to_buy', 'status' => 1, 'cur_text' => $currency->cur_text])->get();

        //return $mainGatewayCurrency;
        $pageTitle = 'Online Recharge';
        return view($this->activeTemplate . 'user.deposit.add', compact('userBalance', 'currency', 'gatewayCurrency', 'pageTitle', 'plans'));
    }

    public function cancelDeposit($id)
    {
        $deposit = Deposit::where('user_id', auth()->user()->id)->where('id', $id)->first();

        if (is_null($deposit)) {
            $notify[] = ['error', 'Invalid request, record not found'];
            return redirect()->route('user.deposit.history')->withNotify($notify);
        }

		$deposit->admin_feedback = 'User cancelled request [self action]';
		$deposit->status = 3;
		$deposit->save();

        $notify[] = ['success', 'Previous deposit request is now cancelled'];
		return redirect()->route('user.deposit')->withNotify($notify);
    }


    public function depositInsert(Request $request, PaymentServices $payment)
    {
        // Validate
        $validator = Validator::make($request->all(),[
            'amount' => 'required|numeric|gt:0',
            'method' => 'required|string',
            'recharge_currency' => ''
        ]);

        // Failed to validate
        if ($validator->fails()) {
            $errors = $validator->errors()->all();
            $notify[] = ['error', implode(' ', $errors)];
            return $this->handleResponse($request, implode(' ', $errors), 401, $notify);
        }

        $user = auth()->user();

        $currency_session = ($request->recharge_currency) ? $request->recharge_currency : config('settings')->cur_text;
        $currency = currency($currency_session);

        $userBalance = $this->wallet->getBalance($user, $currency->cur_text);

        if($userBalance instanceOf \Exception) {
            $notify[] = ['error',$userBalance->getMessage()];
            return $this->handleResponse($request,$userBalance->getMessage(), 401, $notify);
        }

        $gate = GatewayCurrency::where(['method_code' => $request->method])->whereHas('method', function ($gate) {
            $gate->where('status', 1);
        })->first();
        if (!$gate) {
            $notify[] = ['error', 'Invalid gateway'];
            return $this->handleResponse($request, 'Invalid gateway', 401, $notify);
        }

        $gate_amount = $request->amount;

        if ($gate->min_amount > $gate_amount || $gate->max_amount < $gate_amount) {
            $notify[] = ['error', 'Minimum deposit  '.$gate_amount.'is '. getAmount($gate->min_amount) .' and maximum deposit is '. getAmount($gate->max_amount) .''];
            return $this->handleResponse($request, 'Minimum deposit is '. getAmount($gate->min_amount) .' and maximum deposit is '. getAmount($gate->max_amount) .'', 401, $notify);
        }

		$hasDeposit = Deposit::select('trx','method_code','initiate')->where('user_id', $user->id)->where('status', 0)->latest()->first();
        if ($hasDeposit) {

			session()->put('Track', $hasDeposit->trx);
            $notify[] = ['error', 'Still have pending deposit request not expired'];

			if( Carbon::parse($hasDeposit->initiate)->timestamp > Carbon::now()->timestamp ){
				if ($hasDeposit->method_code >= 1000) {
					return $this->handleResponseRedirect($request, 'Still have pending deposit request not expired', 401, route('user.deposit.manual.confirm'), 'user.deposit.manual.confirm', $notify, []);
				}else{
					return $this->handleResponseRedirect($request, 'Still have pending deposit request not expired', 401, route('user.deposit.confirm'), 'user.deposit.confirm', $notify, []);
				}
			}else{
				$notify[] = ['error', 'You currently have a pending deposit, please wait for confirmation or cancellation and try again'];
				return $this->handleResponseRedirect($request, 'You currently have a pending deposit, please wait for confirmation or cancellation and try again', 401, route('user.deposit.history'), 'user.deposit.history', $notify, []);
			}
        }

        $charge = $gate->fixed_charge + ($gate_amount * $gate->percent_charge / 100);
        $payable = $gate_amount + $charge;
        $final_amo = $payable * $gate->rate;

        $bankData = '';
        $tryData = 0;
        $linkData = ($gate->method_code <= 1000) ? url(route('user.deposit.confirm')) : url(route('user.deposit.manual.confirm'));
        $statusData = 0;
        $reference = getTrx();
        $details = null;
        $trx_source = null;

        // Auto Deposit
        if( in_array( $gate->gateway_alias, config('gateway.payment') ) && config('settings')->auto_deposit && $gate->currency == 'NGN')
		{
            // Charge Payment
            $chargePayment = $payment->charge($reference, strtoupper($gate->currency), $final_amo, $gate->method->alias);

            // Exception
            if($chargePayment['status'] == false) {
                $notify[] = ['error', $chargePayment['message']];
                return $this->handleResponse($request, $chargePayment['message'], 401, $notify);
            }

            $tryData = 1;
            $statusData = 2;
            $linkData = $chargePayment['data']['link'];
            $trx_source = $chargePayment['data']['order_ref'];
            $details = json_encode($chargePayment['data']);
        }

        $data = new Deposit();
		if( $gate->method_code >= 1000 ){
			$data->initiate 	= Carbon::now()->addMinutes(config('settings')->payment_window);
		}
        $data->user_id = $user->id;
        $data->method_code = $gate->method_code;
        $data->method_amount = $gate_amount;
        $data->method_currency = strtoupper($gate->currency);
        $data->currency = strtoupper($currency->cur_text);
        $data->amount = $request->amount;
        $data->charge = $charge;
        $data->rate = 1;
        $data->final_amo = $final_amo;
        $data->trx = $reference;
        $data->status = $statusData;
        $data->btc_amo = 0;
        $data->try = $tryData;
        $data->pay_link = $linkData;
        $data->trx_source = $trx_source;
        $data->detail = $details;
        $data->save();
        session()->put('Track', $data->trx);

        // Update Transaction
        self::addUpdateTransaction('deposit', 'Deposit Via ' . $data->gatewayCurrency()->name, $data->trx, $statusData);

        return $this->handleResponseRedirect($request, 'Please pay your order via new tabs opened', 200, $linkData);
    }


    public function depositPreview()
    {

        $track = session()->get('Track');
        $data = Deposit::where('trx', $track)->where('status', 0)->orderBy('id', 'DESC')->firstOrFail();
        $pageTitle = 'Payment Preview';
        return view($this->activeTemplate . 'user.payment.preview', compact('data', 'pageTitle'));
    }


    public function depositConfirm()
    {
        $track = session()->get('Track');

        $deposit = Deposit::where('trx', $track)->where('status', 0)->orderBy('id', 'DESC')->with('gateway')->first();

        if (is_null($deposit)) {
            $notify[] = ['error', 'Invalid Deposit Request'];
            return redirect()->route('user.deposit')->withNotify($notify);
        }

        if ($deposit->method_code >= 1000) {
            $notify[] = ['error', 'Complete pending deposit request'];
            return redirect()->route('user.deposit.manual.confirm')->withNotify($notify);
        }

		if( $deposit->initiate ){

			$send['id']   		= $deposit->id;
			$send['actual'] 	= $deposit->amount;
			$send['amount'] 	= $deposit->btc_amo;
			$send['initiate'] 	= $deposit->initiate;
			$send['sendto'] 	= $deposit->btc_wallet;
			$send['img'] 		= cryptoQR($deposit->btc_wallet);
			$send['currency'] 	= "$deposit->method_currency";
			$send['view'] 		= 'user.payment.'.$deposit->gateway->alias;

			if( $deposit->gateway->alias == 'flutterwave' ){
				$flutterAcc = json_decode($deposit->gatewayCurrency()->gateway_parameter);
				$send['API_publicKey'] = $flutterAcc->public_key;
				$send['encryption_key'] = $flutterAcc->encryption_key;
				$send['virtual_account'] = $flutterAcc->virtual_account;
				$send['customer_email'] = auth()->user()->email;
				$send['customer_phone'] = auth()->user()->mobile;
				$send['txref'] = $deposit->trx;
				$send['notify_url'] = url('ipn/flutterwave');
			}

			$data = (object)($send);

		}else{

			$dirName = $deposit->gateway->alias;
			$new = __NAMESPACE__ . '\\' . $dirName . '\\ProcessController';

			$data = $new::process($deposit);
			$data = json_decode($data);


			if (isset($data->error)) {
				$notify[] = ['error', $data->message];
				return redirect()->route(gatewayRedirectUrl())->withNotify($notify);
			}
			if (isset($data->redirect)) {
				return redirect($data->redirect_url);
			}

			// for Stripe V3
			if (@$data->session) {
				$deposit->btc_wallet = $data->session->id;
				$deposit->save();
			}
		}

        $pageTitle = 'Payment Confirm';
        return view($this->activeTemplate . $data->view, compact('data', 'pageTitle', 'deposit'));
    }


    /**
     * Deposit update transaction
     */
    public static function userDataUpdate( Deposit $deposit ) {
        try {

            // Verify is transaction exisit
            if (!$deposit) throw new \Exception('Transaction not found');

            // Verify if transaction status not complete
            if (in_array($deposit->status, [1, 3])) throw new \Exception('Transaction was already complete');

            // Update transaction details
            $deposit->status = 1;
            $deposit->save();

			$uBalance = 'balance';
			if($deposit->currency != 'NGN'){
				$uBalance = 'usdt_balance';
			}

            $user = User::find($deposit->user_id);
            // Update Balance
			//$wallet->creditBalance($user, $deposit->currency, $deposit->amount);

            // Update user balance
			$user->$uBalance = $user->$uBalance + $deposit->amount;
            $user->save();

            $transaction = Transaction::where('trx', $reference)->first();

            // Verify transaction
            if($transaction) {
				$transaction->status = 1;
				$transaction->save();

                return $user;
            }

            $transaction = new Transaction();
            $transaction->user_id = $deposit->user_id;
            $transaction->currency = $deposit->currency;
            $transaction->amount = $deposit->amount;
            $transaction->post_balance = $user->$uBalance;
            $transaction->charge = $deposit->charge;
            $transaction->trx_type = '+';
            $transaction->type = 'deposit';
            $transaction->details = 'Deposit Via ' . $deposit->gatewayCurrency()->name;
            $transaction->trx = $deposit->trx;
            $transaction->save();

            return $user;

        } catch (\Exception $th) {
            return $th;
        }
    }


    public static function addUpdateTransaction(
        string $type,
        string $desc = null,
        string $reference,
        int $status = 1
    ) {
        try {

            $deposit = Deposit::where('trx', $reference)->first();

            $user = User::find($deposit->user_id);

            // Find Transaction
            $transaction = Transaction::where('trx',$deposit->trx)->first();

            // Verify transaction
            if($transaction) {
                //
                $transaction->status = $status;
                $transaction->save();

                return $transaction;
            }

            $transaction = new Transaction();
            $transaction->user_id = $deposit->user_id;
            $transaction->currency = $deposit->currency;
            $transaction->amount = $deposit->amount;
            $transaction->post_balance = $user->balance;
            $transaction->charge = $deposit->charge;
            $transaction->type = $type;
            $transaction->trx_type = '+';
            $transaction->details = $desc;
            $transaction->trx = $deposit->trx;
            $transaction->status = $status;
            $transaction->save();

            return $transaction;
        } catch (\Exception $th) {
            //throw $th;
            return $th;
        }
    }

    public function manualDepositConfirm()
    {
        $track = session()->get('Track');
        $data = Deposit::with('gateway')->where('status', 0)->where('trx', $track)->first();
        if (!$data) {
            //throw new \Exception($track);
            return redirect()->route(gatewayRedirectUrl());
        }
        $pageTitle = 'Recharge Payment';
        $method = $data->gatewayCurrency();
        return view($this->activeTemplate . 'user.manual_payment.manual_confirm', compact('data', 'pageTitle', 'method'));
        abort(404);
    }

    public function shipayDepositConfirm()
    {
        $track = session()->get('Track');
        $data = Deposit::with('gateway')->where('status', 0)->where('trx', $track)->first();
        if (!$data) {
            return redirect()->route(gatewayRedirectUrl());
        }
        if ($data->method_code > 999) {

            $pageTitle = 'Order Payment #'.$data->trx;
            $method = $data->gatewayCurrency();
            return view($this->activeTemplate . 'user.deposit.method.shipay.manual_confirm', compact('data', 'pageTitle', 'method'));
        }
        abort(404);
    }

    public function manualDepositUpdate(Request $request)
    {
        $track = session()->get('Track');
        $data = Deposit::with('gateway')->where('status', 0)->where('trx', $track)->first();
        if (!$data) {
            return redirect()->route(gatewayRedirectUrl());
        }

        $params = json_decode($data->gatewayCurrency()->gateway_parameter);

        $rules = [];
        $inputField = [];
        $verifyImages = [];

        if ($params != null) {
            foreach ($params as $key => $custom) {
                $rules[$key] = [$custom->validation];
                if ($custom->type == 'file') {
                    array_push($rules[$key], 'image');
                    array_push($rules[$key], new FileTypeValidate(['jpg', 'jpeg', 'png']));
                    array_push($rules[$key], 'max:2048');

                    array_push($verifyImages, $key);
                }
                if ($custom->type == 'text') {
                    array_push($rules[$key], 'max:191');
                }
                if ($custom->type == 'textarea') {
                    array_push($rules[$key], 'max:300');
                }
                $inputField[] = $key;
            }
        }
        $this->validate($request, $rules);


        $directory = date("Y") . "/" . date("m") . "/" . date("d");
        $path = imagePath()['verify']['deposit']['path'] . '/' . $directory;
        $collection = collect($request);
        $reqField = [];
        if ($params != null) {
            foreach ($collection as $k => $v) {
                foreach ($params as $inKey => $inVal) {
                    if ($k != $inKey) {
                        continue;
                    } else {
                        if ($inVal->type == 'file') {
                            if ($request->hasFile($inKey)) {
                                try {
                                    $reqField[$inKey] = [
                                        'field_name' => $directory . '/' . uploadImage($request[$inKey], $path),
                                        'type' => $inVal->type,
                                    ];
                                } catch (\Exception $exp) {
                                    $notify[] = ['error', 'Could not upload your ' . $inKey];
                                    return back()->withNotify($notify)->withInput();
                                }
                            }
                        } else {
                            $reqField[$inKey] = $v;
                            $reqField[$inKey] = [
                                'field_name' => $v,
                                'type' => $inVal->type,
                            ];
                        }
                    }
                }
            }
            $data->detail = $reqField;
        } else {
            $data->detail = null;
        }


        $data->status = 2; // pending
        $data->save();

        /*$transaction = new Transaction();
        $transaction->user_id = $data->user_id;
        $transaction->type =  'deposit';
        $transaction->currency = $data->method_currency;
        $transaction->amount = $data->amount;
        //$transaction->post_balance = $userBalance;
        $transaction->charge = $data->charge;
        $transaction->trx_type = '+';
        $transaction->details = showAmount($data->final_amo) . ' ' . $data->method_currency . " Recharge";
        $transaction->trx =  $data->trx;
        $transaction->status =  2;
        $transaction->save();*/

        // Update Transaction
        self::addUpdateTransaction('deposit', null, $data->trx, 2);


        $adminNotification = new AdminNotification();
        $adminNotification->user_id = $data->user->id;
        $adminNotification->title = 'Deposit request from ' . $data->user->username;
        $adminNotification->click_url = urlPath('admin.deposit.details', $data->id);
        $adminNotification->save();

        $message = Split_Hide_Name($data->user->username) . " deposited " . amount_format($data->final_amo);
        $board = new Board();
        $board->messages = $message;
        $board->save();

        notify($data->user, 'DEPOSIT_REQUEST', [
            'method_name' => $data->gatewayCurrency()->name,
            'method_currency' => $data->method_currency,
            'method_amount' => showAmount($data->final_amo),
            'amount' => showAmount($data->amount),
            'charge' => showAmount($data->charge),
            'currency' => config('settings')->cur_text,
            'rate' => showAmount($data->rate),
            'trx' => $data->trx
        ]);

        $notify[] = ['success', 'You have deposit request has been taken.'];
        return redirect()->route('user.transaction')->withNotify($notify);
    }

    public function walletSwap($currency = null)
    {
        $user = User::find(\auth()->user()->id);

        $currency_session = ($currency) ? $currency : config('settings')->cur_text;
        $currency = currency($currency_session);

        if(!$currency) return back()->withNotify([['error', 'Currency not support']]);

        $userBalance = $this->wallet->getBalance($user, $currency->cur_text);

        if($userBalance instanceOf \Exception) {
            $notify[] = ['error', $userBalance->getMessage()];
            return back()->withNotify($notify);
        }

        $gatewayCurrency = GatewayCurrency::where(['currency' => $currency->cur_text])->orderby('name')->get();

        // Recharge not available
        if(!$gatewayCurrency) {
            $notify[] = ['error', 'Sorry recharge not available'];
            return back()->withNotify($notify);
        }

        $plans = Plan::where('platform', config('settings')->active_template)->where('min_amount', '>', 0)->where(['status' => 1, 'cur_text' => $currency->cur_text])->get();

        //return $mainGatewayCurrency;
        $pageTitle = 'Swap Wallet';
        return view($this->activeTemplate . 'user.deposit.swap', compact('userBalance', 'currency', 'gatewayCurrency', 'pageTitle', 'plans'));
    }


}
