<?php

namespace App\Http\Controllers\Customer;

use App\Http\Requests\SenderID\PayPaymentRequest;
use App\Library\Tool;
use App\Models\Invoices;
use App\Models\PaymentMethods;
use App\Models\PhoneNumbers;
use App\Models\PlansSendingServer;
use App\Models\SendingServer;
use App\Repositories\Contracts\PhoneNumberRepository;
use Carbon\Carbon;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\View\View;
use JetBrains\PhpStorm\NoReturn;
use Twilio\Exceptions\ConfigurationException;
use Twilio\Exceptions\RestException;
use Twilio\Exceptions\TwilioException;
use Twilio\Rest\Client;

class NumberController extends CustomerBaseController
{

    protected $numbers;


    /**
     * PhoneNumberController constructor.
     *
     * @param  PhoneNumberRepository  $numbers
     */

    public function __construct(PhoneNumberRepository $numbers)
    {
        $this->numbers = $numbers;
    }

    /**
     * @return Application|Factory|View
     * @throws AuthorizationException
     */
    public function index()
    {

        $this->authorize('view_numbers');

        $breadcrumbs = [
                ['link' => url('dashboard'), 'name' => __('locale.menu.Dashboard')],
                ['link' => url('dashboard'), 'name' => __('locale.menu.Sending')],
                ['name' => __('locale.menu.Numbers')],
        ];

        $plan_id = Auth::user()->customer->activeSubscription()->plan_id;

        if (Auth::user()->customer->getOption('create_sending_server') == 'yes') {
            if (PlansSendingServer::where('plan_id', $plan_id)->count()) {

                $sending_server = SendingServer::where('user_id', Auth::user()->id)->where('status', true)->where('settings', 'Twilio')->first();

                if ( ! $sending_server) {
                    $sending_server_ids = PlansSendingServer::where('plan_id', $plan_id)->pluck('sending_server_id')->toArray();
                    $sending_server     = SendingServer::where('settings', 'Twilio')->where('status', true)->whereIn('id', $sending_server_ids)->first();
                }
            } else {
                $sending_server_ids = PlansSendingServer::where('plan_id', $plan_id)->pluck('sending_server_id')->toArray();
                $sending_server     = SendingServer::where('settings', 'Twilio')->where('status', true)->whereIn('id', $sending_server_ids)->first();
            }
        } else {
            // If customer don't have permission creating sending servers
            $sending_server_ids = PlansSendingServer::where('plan_id', $plan_id)->pluck('sending_server_id')->toArray();
            $sending_server     = SendingServer::where('settings', 'Twilio')->where('status', true)->whereIn('id', $sending_server_ids)->first();
        }

        return view('customer.Numbers.index', compact('breadcrumbs', 'sending_server'));
    }


    /**
     * @param  Request  $request
     *
     * @return void
     * @throws AuthorizationException
     */
    #[NoReturn] public function search(Request $request)
    {

        $this->authorize('view_numbers');

        $columns = [
                0 => 'responsive_id',
                1 => 'uid',
                2 => 'uid',
                3 => 'number',
                4 => 'price',
                5 => 'status',
                6 => 'capabilities',
                7 => 'actions',
        ];

        $totalData = PhoneNumbers::where('user_id', Auth::user()->id)->count();

        $totalFiltered = $totalData;

        $limit = $request->input('length');
        $start = $request->input('start');
        $order = $columns[$request->input('order.0.column')];
        $dir   = $request->input('order.0.dir');

        if (empty($request->input('search.value'))) {
            $numbers = PhoneNumbers::where('user_id', Auth::user()->id)->offset($start)
                                   ->limit($limit)
                                   ->orderBy($order, $dir)
                                   ->get();
        } else {
            $search = $request->input('search.value');

            $numbers = PhoneNumbers::where('user_id', Auth::user()->id)->whereLike(['uid', 'number', 'price', 'status'], $search)
                                   ->offset($start)
                                   ->limit($limit)
                                   ->orderBy($order, $dir)
                                   ->get();

            $totalFiltered = PhoneNumbers::where('user_id', Auth::user()->id)->whereLike(['uid', 'number', 'price', 'status'], $search)->count();

        }

        $data = [];
        if ( ! empty($numbers)) {
            foreach ($numbers as $number) {

                $is_assigned = false;
                if ($number->status == 'assigned') {
                    $is_assigned = true;
                    $status      = '<span class="badge bg-success text-uppercase">'.__('locale.labels.assigned').'</span>';
                } elseif ($number->status == 'available') {
                    $is_assigned = true;
                    $status      = '<span class="badge bg-warning text-uppercase">'.__('locale.labels.pending').'</span>';
                } else {
                    $status = '<span class="badge bg-danger text-uppercase">'.__('locale.labels.expired').'</span>';
                }


                $nestedData['responsive_id'] = '';
                $nestedData['uid']           = $number->uid;
                $nestedData['number']        = $number->number;
                $nestedData['price']         = "<div>
                                                        <p class='text-bold-600'>".Tool::format_price($number->price, $number->currency->format)." </p>
                                                        <p class='text-muted'>".$number->displayFrequencyTime()."</p>
                                                   </div>";
                $nestedData['status']        = $status;
                $nestedData['is_assigned']   = $is_assigned;

                $nestedData['capabilities'] = $number->getCapabilities();

                $nestedData['renew_label'] = __('locale.labels.renew');
                $nestedData['renew']       = route('customer.numbers.pay', $number->uid);
                $nestedData['release']     = __('locale.labels.release');
                $data[]                    = $nestedData;

            }
        }

        $json_data = [
                "draw"            => intval($request->input('draw')),
                "recordsTotal"    => $totalData,
                "recordsFiltered" => $totalFiltered,
                "data"            => $data,
        ];

        echo json_encode($json_data);
        exit();

    }

    /**
     * show available numbers
     *
     * @return Application|Factory|\Illuminate\Contracts\View\View
     * @throws AuthorizationException
     */
    public function buy()
    {
        $this->authorize('buy_numbers');

        $breadcrumbs = [
                ['link' => url('dashboard'), 'name' => __('locale.menu.Dashboard')],
                ['link' => url('numbers'), 'name' => __('locale.menu.Numbers')],
                ['name' => __('locale.phone_numbers.buy_number')],
        ];

        return view('customer.Numbers.buy', compact('breadcrumbs'));
    }

    /**
     * @param  Request  $request
     *
     * @return void
     * @throws AuthorizationException
     */
    #[NoReturn] public function availableNumbers(Request $request)
    {

        $this->authorize('buy_numbers');

        $columns = [
                0 => 'responsive_id',
                1 => 'uid',
                2 => 'uid',
                3 => 'number',
                4 => 'price',
                5 => 'capabilities',
                6 => 'actions',
        ];

        $totalData = PhoneNumbers::where('status', 'available')->count();

        $totalFiltered = $totalData;

        $limit = $request->input('length');
        $start = $request->input('start');
        $order = $columns[$request->input('order.0.column')];
        $dir   = $request->input('order.0.dir');

        if (empty($request->input('search.value'))) {
            $numbers = PhoneNumbers::where('status', 'available')->offset($start)
                                   ->limit($limit)
                                   ->orderBy($order, $dir)
                                   ->get();
        } else {
            $search = $request->input('search.value');

            $numbers = PhoneNumbers::where('status', 'available')->whereLike(['uid', 'number', 'price'], $search)
                                   ->offset($start)
                                   ->limit($limit)
                                   ->orderBy($order, $dir)
                                   ->get();

            $totalFiltered = PhoneNumbers::where('status', 'available')->whereLike(['uid', 'number', 'price'], $search)->count();

        }

        $data = [];
        if ( ! empty($numbers)) {
            foreach ($numbers as $number) {

                $nestedData['responsive_id'] = '';
                $nestedData['uid']           = $number->uid;
                $nestedData['buy']           = __('locale.labels.buy');
                $nestedData['number']        = $number->number;
                $nestedData['price']         = "<div>
                                                        <p class='text-bold-600'>".Tool::format_price($number->price, $number->currency->format)." </p>
                                                        <p class='text-muted'>".$number->displayFrequencyTime()."</p>
                                                   </div>";
                $nestedData['checkout']      = route('customer.numbers.pay', $number->uid);
                $nestedData['capabilities']  = $number->getCapabilities();
                $data[]                      = $nestedData;

            }
        }

        $json_data = [
                "draw"            => intval($request->input('draw')),
                "recordsTotal"    => $totalData,
                "recordsFiltered" => $totalFiltered,
                "data"            => $data,
        ];

        echo json_encode($json_data);
        exit();

    }


    /**
     * @param  PhoneNumbers  $phone_number
     * @param $id
     *
     * @return JsonResponse Controller|JsonResponse
     *
     * @throws AuthorizationException
     */
    public function release(PhoneNumbers $phone_number, $id): JsonResponse
    {
        if (config('app.stage') == 'demo') {
            return response()->json([
                    'status'  => 'error',
                    'message' => 'Sorry! This option is not available in demo mode',
            ]);
        }

        $this->authorize('release_numbers');

        $this->numbers->release($phone_number, $id);

//        $sending_server = SendingServer::where('settings', 'Twilio')->where('status', true)->first();
//
//        if ($sending_server) {
//            try {
//                $number = PhoneNumbers::findByUid($id);
//                $twilio = new Client($sending_server->account_sid, $sending_server->auth_token);
//                $twilio->incomingPhoneNumbers($number->transaction_id)->delete();
//                $number->delete();
//            } catch (TwilioException|RestException $exception) {
//            }
//
//        }

        return response()->json([
                'status'  => 'success',
                'message' => __('locale.phone_numbers.number_successfully_released'),
        ]);

    }

    /**
     * batch release
     *
     * @param  Request  $request
     *
     * @return JsonResponse
     */
    public function batchAction(Request $request): JsonResponse
    {
        if (config('app.stage') == 'demo') {
            return response()->json([
                    'status'  => 'error',
                    'message' => 'Sorry! This option is not available in demo mode',
            ]);
        }

        $ids     = $request->get('ids');
        $numbers = PhoneNumbers::where('user_id', Auth::user()->id)->whereIn('uid', $ids)->cursor();

        foreach ($numbers as $number) {
            $number->user_id       = 1;
            $number->status        = 'available';
            $number->validity_date = null;

            $number->save();
        }

        return response()->json([
                'status'  => 'success',
                'message' => __('locale.phone_numbers.number_successfully_released'),
        ]);

    }


    /**
     * checkout
     *
     * @param  PhoneNumbers  $number
     *
     * @return Application|Factory|\Illuminate\Contracts\View\View|RedirectResponse
     * @throws AuthorizationException
     */
    public function pay(PhoneNumbers $number)
    {

        // $this->authorize('buy_numbers');

        $pageConfigs = [
                'bodyClass' => 'ecommerce-application',
        ];

        $breadcrumbs = [
                ['link' => url('dashboard'), 'name' => __('locale.menu.Dashboard')],
                ['link' => url('dashboard'), 'name' => __('locale.menu.Sending')],
                ['link' => url('numbers'), 'name' => __('locale.menu.Numbers')],
                ['name' => __('locale.labels.checkout')],
        ];

        if ($number->price == '0') {

            $paymentMethod = PaymentMethods::where('type', PaymentMethods::TYPE_CASH)->first();

            $invoice = Invoices::create([
                    'user_id'        => auth()->user()->id,
                    'currency_id'    => $number->currency_id,
                    'payment_method' => $paymentMethod->id,
                    'amount'         => $number->price,
                    'type'           => Invoices::TYPE_NUMBERS,
                    'description'    => __('locale.phone_numbers.payment_for_number').' '.$number->number,
                    'transaction_id' => $number->uid,
                    'status'         => Invoices::STATUS_PAID,
            ]);

            if ($invoice) {
                $current               = Carbon::now();
                $number->user_id       = auth()->user()->id;
                $number->validity_date = $current->add($number->frequency_unit, $number->frequency_amount);
                $number->status        = 'assigned';
                $number->save();


                return redirect()->route('customer.numbers.index')->with([
                        'status'  => 'success',
                        'message' => __('locale.payment_gateways.payment_successfully_made'),
                ]);
            }

            return redirect()->route('customer.numbers.pay', $number->uid)->with([
                    'status'  => 'error',
                    'message' => __('locale.exceptions.something_went_wrong'),
            ]);
        }

        $payment_methods = PaymentMethods::where('status', true)->cursor();

        return view('customer.Numbers.checkout', compact('breadcrumbs', 'pageConfigs', 'number', 'payment_methods'));
    }


    /**
     * pay sender id payment
     *
     * @param  PhoneNumbers  $number
     * @param  PayPaymentRequest  $request
     *
     * @return Application|Factory|\Illuminate\Contracts\View\View|RedirectResponse
     */
    public function payment(PhoneNumbers $number, PayPaymentRequest $request)
    {

        $data = $this->numbers->payPayment($number, $request->except('_token'));

        if (isset($data->getData()->status)) {

            if ($data->getData()->status == 'success') {

                if ($request->payment_methods == PaymentMethods::TYPE_BRAINTREE) {
                    return view('customer.Payments.braintree', [
                            'token'    => $data->getData()->token,
                            'number'   => $number,
                            'post_url' => route('customer.numbers.braintree', $number->uid),
                    ]);
                }

                if ($request->payment_methods == PaymentMethods::TYPE_STRIPE) {
                    return view('customer.Payments.stripe', [
                            'session_id'      => $data->getData()->session_id,
                            'publishable_key' => $data->getData()->publishable_key,
                            'number'          => $number,
                    ]);
                }

                if ($request->payment_methods == PaymentMethods::TYPE_AUTHORIZE_NET) {

                    $months = [1 => 'Jan', 2 => 'Feb', 3 => 'Mar', 4 => 'Apr', 5 => 'May', 6 => 'Jun', 7 => 'Jul', 8 => 'Aug', 9 => 'Sep', 10 => 'Oct', 11 => 'Nov', 12 => 'Dec'];

                    return view('customer.Payments.authorize_net', [
                            'months'   => $months,
                            'number'   => $number,
                            'post_url' => route('customer.numbers.authorize_net', $number->uid),
                    ]);
                }

                if ($request->payment_methods == PaymentMethods::TYPE_FEDAPAY) {
                    return view('customer.Payments.fedapay', [
                            'public_key' => $data->getData()->public_key,
                            'amount'     => $number->price,
                            'first_name' => $request->first_name,
                            'last_name'  => $request->last_name,
                            'email'      => $request->email,
                            'item_name'  => __('locale.phone_numbers.payment_for_number').' '.$number->number,
                            'postData'   => [
                                    'user_id'      => $number->user_id,
                                    'request_type' => 'number',
                                    'post_data'    => $number->uid,
                            ],
                    ]);
                }


                if ($request->payment_methods == PaymentMethods::TYPE_CASH) {
                    return view('customer.Payments.offline', [
                            'data'      => $data->getData()->data,
                            'type'      => 'number',
                            'post_data' => $number->uid,
                    ]);
                }

                if ($request->payment_methods == PaymentMethods::TYPE_VODACOMMPESA) {

                    return view('customer.Payments.vodacom_mpesa', [
                            'number'   => $number,
                            'post_url' => route('customer.numbers.vodacommpesa', $number->uid),
                    ]);
                }

                return redirect()->to($data->getData()->redirect_url);
            }

            return redirect()->route('customer.numbers.pay', $number->uid)->with([
                    'status'  => 'error',
                    'message' => $data->getData()->message,
            ]);
        }

        return redirect()->route('customer.numbers.pay', $number->uid)->with([
                'status'  => 'error',
                'message' => __('locale.exceptions.something_went_wrong'),
        ]);

    }

    /*
    |--------------------------------------------------------------------------
    | Version 3.6
    |--------------------------------------------------------------------------
    |
    | Purchase number using Twilio API
    |
    */

    /**
     * @throws AuthorizationException
     * @throws ConfigurationException
     */
    public function buyUsingAPI(SendingServer $sending_server)
    {

        $this->authorize('buy_numbers_using_api');

        $breadcrumbs = [
                ['link' => url('dashboard'), 'name' => __('locale.menu.Dashboard')],
                ['link' => url('numbers'), 'name' => __('locale.menu.Numbers')],
                ['name' => __('locale.phone_numbers.buy_number')],
        ];

        try {
            $twilio    = new Client($sending_server->account_sid, $sending_server->auth_token);
            $countries = $twilio->pricing->v1->phoneNumbers->countries->read(216);

            return view('customer.Numbers.buy-using-api', compact('countries', 'sending_server', 'breadcrumbs'));
        } catch (RestException $exception) {
            return back()->with([
                    'status'  => 'error',
                    'message' => $exception->getMessage(),
            ]);
        }
    }


    /**
     * @param  SendingServer  $sending_server
     * @param  Request  $request
     *
     * @return Application|Factory|\Illuminate\Contracts\View\View|RedirectResponse
     * @throws AuthorizationException
     * @throws ConfigurationException
     */
    public function searchNumbersUsingAPI(SendingServer $sending_server, Request $request)
    {

        $this->authorize('buy_numbers_using_api');

        $breadcrumbs = [
                ['link' => url('dashboard'), 'name' => __('locale.menu.Dashboard')],
                ['link' => url('numbers'), 'name' => __('locale.menu.Numbers')],
                ['name' => __('locale.phone_numbers.buy_number')],
        ];

        $request->validate([
                'country' => 'required|min:1|max:3',
        ]);

        $data = [];
        if (isset($request->smsEnabled) && $request->smsEnabled) {
            $data['smsEnabled'] = true;
        }

        if (isset($request->voiceEnabled) && $request->voiceEnabled) {
            $data['voiceEnabled'] = true;
        }

        if (isset($request->mmsEnabled) && $request->mmsEnabled) {
            $data['mmsEnabled'] = true;
        }

        if (isset($request->area_code) && $request->area_code && ($request->country == 'US' || $request->country == 'CA')) {
            $data['areaCode'] = $request->area_code;
        }

        try {

            $twilio  = new Client($sending_server->account_sid, $sending_server->auth_token);
            $numbers = $twilio->availablePhoneNumbers($request->country)
                    ->local
                    ->read($data, 50);

            return view('customer.Numbers.purchase-numbers', compact('numbers', 'breadcrumbs', 'sending_server'));
        } catch (RestException $exception) {
            return back()->withInput($request->only('country'))->with([
                    'status'  => 'error',
                    'message' => $exception->getMessage(),
            ]);
        }
    }

    /**
     * @param $number
     * @param  SendingServer  $sending_server
     *
     * @return Application|Factory|\Illuminate\Contracts\View\View|RedirectResponse
     * @throws ConfigurationException
     * @throws TwilioException
     */
    public function purchaseNumber($number, SendingServer $sending_server)
    {
        try {

            $twilio         = new Client($sending_server->account_sid, $sending_server->auth_token);
            $purchaseNumber = $twilio->incomingPhoneNumbers->create(['phoneNumber' => $number]);
            if (isset($purchaseNumber->sid)) {
                $paymentMethod = PaymentMethods::where('type', PaymentMethods::TYPE_CASH)->first();

                if ($paymentMethod) {

                    $phone = str_replace(['(', ')', '+', '-', ' '], '', $number);

                    $capabilities = [];
                    if ($purchaseNumber->capabilities['sms']) {
                        $capabilities[] = 'sms';
                    }

                    if ($purchaseNumber->capabilities['voice']) {
                        $capabilities[] = 'voice';
                    }

                    if ($purchaseNumber->capabilities['mms']) {
                        $capabilities[] = 'mms';
                    }

                    $current = Carbon::now();

                    $number = PhoneNumbers::create([
                            'user_id'          => Auth::user()->id,
                            'currency_id'      => Auth::user()->customer->subscription->plan->currency->id,
                            'number'           => $phone,
                            'status'           => 'available',
                            'capabilities'     => json_encode($capabilities),
                            'price'            => 5,
                            'billing_cycle'    => 'monthly',
                            'frequency_amount' => 1,
                            'frequency_unit'   => 'month',
                            'validity_date'    => $current->addMonth(),
                            'transaction_id'   => $purchaseNumber->sid,
                    ]);

                    $pageConfigs = [
                            'bodyClass' => 'ecommerce-application',
                    ];

                    $breadcrumbs = [
                            ['link' => url('dashboard'), 'name' => __('locale.menu.Dashboard')],
                            ['link' => url('dashboard'), 'name' => __('locale.menu.Sending')],
                            ['link' => url('numbers'), 'name' => __('locale.menu.Numbers')],
                            ['name' => __('locale.labels.checkout')],
                    ];


                    $payment_methods = PaymentMethods::where('status', true)->cursor();

                    return view('customer.Numbers.checkout', compact('breadcrumbs', 'pageConfigs', 'number', 'payment_methods'));

                }

                return back()->with([
                        'status'  => 'error',
                        'message' => __('locale.exceptions.something_went_wrong'),
                ]);
            }

            return back()->with([
                    'status'  => 'error',
                    'message' => __('locale.exceptions.something_went_wrong'),
            ]);
        } catch (RestException $exception) {
            return back()->with([
                    'status'  => 'error',
                    'message' => $exception->getMessage(),
            ]);
        }
    }
}
