<?php

namespace App\Http\Controllers;

use App\Enums\PaymentStatusEnum;
use App\Exceptions\PaymentProcessingException;
use App\Facades\Flash;
use App\Models\Payment;
use App\Services\Payment\Interfaces\PaymentInterface;
use App\Traits\PaymentHelper;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class PaymentController extends Controller
{
    use PaymentHelper;

    protected $gateway;

    public function __construct(PaymentInterface $paymentInterface)
    {
        $this->gateway = $paymentInterface->getService(request()->route('gateway'));
    }

    public function webhook(Request $request)
    {
        DB::beginTransaction();
        try {
            if (! $this->gateway) {
                throw new PaymentProcessingException(__('Gateway not found'));
            }

            $result = $this->gateway->handleWebhook($request);

            $payment = $request['payment'];

            if ($this->isDatabaseAlreadyUpdated($payment)) {
                return response()->json([
                    'status' => 'success',
                ]);
            }

            switch ($result['status']) {
                case $this->gateway::SUCCESS:
                    $this->updatePaidPayment($payment);

                    return response()->json([
                        'status' => 'success',
                    ]);
                case $this->gateway::FAILED:
                    $this->updateFailedPayment($payment);

                    return response()->json([
                        'status' => 'failed',
                    ]);
                case $this->gateway::CANCELLED:
                    $this->updateCancelledPayment($payment);

                    return response()->json([
                        'status' => 'cancelled',
                    ]);
                default:
                    throw new PaymentProcessingException(__('Invalid payment status'), code: 2);
                    break;
            }

        } catch (PaymentProcessingException $e) {
            DB::rollBack();
            Log::info($e->getMessage());

            return response()->json([
                'status' => 'error',
                'message' => $e->getMessage(),
            ], $e->getCode() === 1 ? 200 : 500);
        }
    }

    public function returnFunction()
    {
        $status = request()->get('status');
        if (! $status) {
            // this is for toyyibpay payment, status_id will get 1=success and 2=pending and 3=fail
            $status = request()->get('status_id');

            if (! $status) {
                $status = request()->get('redirect_status');
            }
        }

        if (! $status) {
            $status = $this->checkThePaymentStatus();
        }

        if (in_array($status, ['success', 'successful', '1', 'succeeded'])) {
            return $this->success();
        }

        if (in_array($status, ['failed', '3'])) {
            return $this->failed();
        }

        if ($status == 'cancel') {
            return $this->cancelled();
        }

        return redirect()->back()->withError(__('Payment not found!'));
    }

    public function checkThePaymentStatus()
    {
        if (! $this->gateway) {
            throw new PaymentProcessingException(__('Gateway not found'));
        }

        return $this->gateway->checkOrderStatus(request()->get('pid'));
    }

    public function success()
    {
        $request = request();

        try {
            if (! $this->gateway) {
                throw new PaymentProcessingException(__('Gateway not found'));
            }

            $paymentIdentifier = $this->gateway->handleSuccess($request);

            $payment = Payment::where('payment_identifier', $paymentIdentifier)->first();

            if ($this->isDatabaseAlreadyUpdated($payment)) {
                return redirect()->away($this->getFrontendRedirectUrl(1, status: $this->gateway::SUCCESS));
            }

            DB::beginTransaction();

            $this->quizDepositHandler($payment, PaymentStatusEnum::PAID);

            $this->updatePaymentStatus($payment, PaymentStatusEnum::PAID);

            DB::commit();

            return redirect()->away($this->getFrontendRedirectUrl(1, status: $this->gateway::SUCCESS));
        } catch (PaymentProcessingException $e) {
            DB::rollBack();
            Log::info($e->getMessage());

            if ($e->getCode() === 1) {
                Flash::success($e->getMessage());
            } else {
                Flash::error($e->getMessage());

                return view('client.payment.status.cancel');
            }

            return redirect()->away($this->getFrontendRedirectUrl($e->getCode(), $this->gateway::CANCELLED));
        }
    }

    public function failed()
    {
        try {
            if (! $this->gateway) {
                throw new PaymentProcessingException(__('Gateway not found'));
            }

            $pid = $this->gateway->handleFailed(request());

            $payment = Payment::where('payment_identifier', $pid)->first();

            if ($this->isDatabaseAlreadyUpdated($payment)) {
                throw new PaymentProcessingException(__('Payment is not successful'));
            }

            DB::beginTransaction();

            $this->quizDepositHandler($payment, PaymentStatusEnum::CANCELLED);

            $this->updatePaymentStatus($payment, PaymentStatusEnum::CANCELLED);

            DB::commit();

            return redirect()->away($this->getFrontendRedirectUrl(1, $this->gateway::CANCELLED));
        } catch (PaymentProcessingException $e) {
            DB::rollBack();

            return redirect()->away($this->getFrontendRedirectUrl($e->getCode(), $this->gateway::CANCELLED));
        }
    }

    private function cancelled()
    {
        try {
            if (! $this->gateway) {
                throw new PaymentProcessingException(__('Gateway not found'));
            }

            $pid = $this->gateway->handleFailed(request());
            $payment = Payment::where('payment_identifier', $pid)->first();

            if ($this->isDatabaseAlreadyUpdated($payment)) {
                throw new PaymentProcessingException(__('Payment is not successful'));
            }

            DB::beginTransaction();

            $this->quizDepositHandler($payment, PaymentStatusEnum::CANCELLED);
            $this->updatePaymentStatus($payment, PaymentStatusEnum::CANCELLED);
            DB::commit();

            return redirect()->away($this->getFrontendRedirectUrl(1, status: $this->gateway::CANCELLED));
        } catch (PaymentProcessingException $e) {
            DB::rollBack();

            return redirect()->away($this->getFrontendRedirectUrl($e->getCode(), $this->gateway::CANCELLED));
        }
    }

    public function squareCurrency(PaymentInterface $paymentInterface)
    {
        try {
            $square = $paymentInterface->getService('square');

            return response()->json([
                'status' => 'success',
                'currency' => $square->getCurrency(),
            ]);
        } catch (PaymentProcessingException $e) {
            return response()->json([
                'status' => 'error',
                'message' => $e->getMessage(),
            ]);
        }
    }

    public function amazonReview(PaymentInterface $paymentInterface)
    {

        try {
            $amazon = $paymentInterface->getService('amazon');

            $reviewUrl = $amazon->review();

            return redirect($reviewUrl);
        } catch (PaymentProcessingException $e) {

            return view('client.payment.status.failed');
        }
    }

    private function getFrontendRedirectUrl($code, $status)
    {
        $frontend_url = config('application_info.frontend_url');

        $status = $code != 1 ? 'failed' : $status;

        return "$frontend_url/checkout?status=$status";
    }
}
