<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Stripe\Stripe;
use Stripe\Customer;
use Stripe\PaymentIntent;
use Stripe\PaymentMethod;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Auth;
use App\Models\Transaction;

class BillingController extends Controller
{
	protected $stripe;
	protected $user;


	public function __construct() {
		$this->stripe = new \Stripe\StripeClient(config('services.stripe.secret'));
		Stripe::setApiKey(config('services.stripe.secret'));
		$this->user = Auth::user();
	}

	protected function attempt($packs) {
		$user = Auth::user();

		foreach($packs as $value) {
			if ($value < 0) {
				abort(422);
			}
		}

		$amount = $packs[ 'credits10' ]*1099 +
			$packs[ 'credits50' ]*5499 + $packs[ 'credits100' ]*10999
			+ $packs[ 'credits1000' ]*101000;

		$transaction = new Transaction;
		$transaction->credits = $packs['credits10']*1000 +
			$packs['credits50']*5000 +
			$packs['credits100']*10000 +
			$packs['credits1000']*100000;
		$transaction->credits_extra =
			$packs['credits50']*500 +
			$packs['credits100']*1000 +
			$packs['credits1000']*15000;

		$transaction->user_id = $user->id;
		$transaction->charge = $amount;
		$transaction->status = 'processing';
		$transaction->completed = false;
		$transaction->save();
		return $transaction;
	}

	//Expects an array 'packs' representing the amount of each multiple of credits.
	//Should validate that all amounts are positive integers in a reasonable range
	public function stripeSecret(Request $request) {
		$user = Auth::user();
		$transaction = $this->attempt($request->packs);

		$intent = PaymentIntent::create([
			'amount' => $amount,
			'currency' => 'usd',
			'customer' => $user->customer_id,
			'description' => "You have received $total_credits credits.",
			'receipt_email' => Auth::user()->email,
			'metadata' => ['transaction_id' => $transaction->id]
		]);
		$transaction->intent_id = $intent->id;

		//Save the card as a default if none is set and it was selected
		if ($user->payment_method == null && $request->card) {
			$this->changeDefaultCard($request->card);
		}

		$transaction->save();
		return $intent->client_secret;
	}

	public function getCards() {
		return PaymentMethod::all([
			'customer' => Auth::user()->customer_id,
			'type' => 'card'
		]);
	}

	//Adds correct credit amount to the charged user, precise to two decimal places 
	public function chargeEvent(Request $request) {
		$event = \Stripe\Event::constructFrom($request->all());
		$charge = $event->data->object;
		$transaction = Transaction::where('intent_id', $charge->payment_intent)->first();

		if ($event->type == 'charge.succeeded') {
			$this->creditUser($transaction->id);
		} else {
			$transaction->status = $charge->status;
			$transaction->save();
		}
	}

	public function changeDefaultCard(String $card) {
		$user = Auth::user();
		$user->payment_method = $card;
		$user->save();
		$cards = $this->getCards();

		return PaymentMethod::all([
			'customer' => Auth::user()->customer_id,
			'type' => 'card'
		]);
	}

	public function deleteCard(Request $request) {
		$this->stripe->paymentMethods->detach($request->card);

		$user = Auth::user();
		if ($request->card == $user->payment_method) {
			$user->payment_method = null;
			$user->save();
		}
		return ($this->getCards());
	}

	//Receives a request with a packs. It is an array of each type of credit
	//amount to be bought
	public function payeer(Request $request) {
		$user = Auth::user();
		$transaction = $this->attempt($request->packs);
		$shopid = config('services.payeer.id');
		$secret = config('services.payeer.secret');
		$param_key = config('services.payeer.param_key');
		$total = $transaction->credits/100 + $transaction->credits_extra/100;
		$description =  base64_encode("You will receive $total credits.");

		$arHash = [$shopid, $transaction->id, $transaction->charge/100, 'USD',
			$description];

		$params = ['reference' => ['transaction_id' => $transaction->id]];
		$key = md5($param_key.$transaction->id);
		$encodedParams = @urlencode(base64_encode(openssl_encrypt(
			json_encode($params), 'AES-256-CBC', $key, OPENSSL_RAW_DATA
		)));
		$arHash[] = $encodedParams;
		$arHash[] = $secret;

		$signature = strtoupper(hash('sha256', implode(':', $arHash)));
		$user->paying = true; $user->save();

		return [ 'signature' => $signature, 'params' => $encodedParams, 'shop'
			=> $shopid, 'transaction' => $transaction->id, 'amount' =>
			$transaction->charge/100, 'description' => $description ];
	}

	//This needs to check the ip of the sender
	public function processPayeer(Request $request) {
		$allowed = ['185.71.65.92', '185.71.65.189', '149.202.17.210'];
		$ipAddress = $request->ip();

		if (!in_array($ipAddress, $allowed)){
			abort(401);
		}

		Log::debug('Processing Payeer payment');
		Log::debug($request);

		$secret = config('services.payeer.secret');
		$arHash = [$request->m_operation_id,
			$request->m_operation_ps,
			$request->m_operation_day,
			$request->m_operation_pay_date,
			$request->m_shop,
			$request->m_orderid,
			$request->m_amount,
			$request->m_curr,
			$request->m_desc,
			$request->m_status
		];

		if (isset($request->m_params)) {
			$arHash[] = $request->m_params;
		}

		$arHash[] = $secret;

		$signature = strtoupper(hash('sha256', implode(':', $arHash)));
		if ($signature == $request->m_sign && $request->m_status == 'success'){
			$this->creditUser((int) $request->m_orderid);
			return $request->m_orderid.'|success';
		} else {
			$transaction = Transaction::find($request->orderid);
			$transaction->status = 'error';
			$transaction->save();
			return $request->m_orderid.'|error';
		}
	}

	//Credits the user of a given transaction id
	public function creditUser($transaction_id) {
		$transaction = Transaction::find($transaction_id);

		if ($transaction->completed) {
			abort(422, 'Bad transaction ID');
		}

		$user = $transaction->user;
		$user->credits = $user->credits + $transaction->credits +
			$transaction->credits_extra;
		$transaction->status = 'completed';
		$transaction->completed = true;

		$user->save();
		$transaction->save();
	}

	public function pm(Request $request) {
		$user = Auth::user();
		$account = config('services.pm.account');
		$transaction = $this->attempt($request->packs);
		$total = $transaction->credits/100 +
			$transaction->credits_extra/100;
		$description =  "You will receive $total credits.";

		$user->paying = true; $user->save();

		return ['account' => $account, 'transaction' => $transaction->id,
			'amount' => $transaction->charge/100, 'description' => $description];
	}

	//Handler run after PM payment succeds
	public function processPM(Request $request) {
		$allowed = ['77.109.141.170', '91.205.41.208', '94.242.216.60',
			'78.41.203.75'];
		$transaction = Transaction::find($request->PAYMENT_ID);
		$secret = config('services.pm.secret');

		//Check that sender is PM and account the amount was paid to is mine.
		if (!in_array($request->ip(), $allowed)){
			abort(401);
		} else if ($request->PAYEE_ACCOUNT != config('services.pm.account')) {
			abort(422);
		} else if (!$transaction->complete) {
			abort(422);
		}

		Log::debug('Processing PM payment');
		Log::debug($request);

		//Would need to be changed if baggage fields are used
		$arHash = [$request->PAYMENT_ID,
			$request->PAYEE_ACCOUNT,
			$request->PAYMENT_AMOUNT,
			$request->PAYMENT_UNITS,
			$request->PAYMENT_BATCH_NUMBER,
			$request->PAYER_ACCOUNT,
			strtoupper(md5($secret)),
			$request->TIMESTAMPGMT,
		];

		$signature = strtoupper(md5(implode(':', $arHash)));

		if ($signature == $request->V2_HASH){
			$this->creditUser((int) $transaction->id);
		} else {
			abort(422, 'Bad hash');
		}
	}

	public function completePM(Request $request) {
		return redirect('/panel/#transaction-complete');
	}

	public function failPM(Request $request) {
		return redirect('/panel/#transaction-failed');
	}

}