<?php

namespace App\Helpers;

use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use App\Helpers\PlanHelper as PlanHelper;
use Stripe as StripeLib;
use Mail;
use App\Mail\PlanPaymentAdminSuccess;
use App\Mail\PlanPaymentAdminPending;
use App\Mail\PlanPaymentUserPending;
use App\Mail\PlanPaymentUserSuccess;
use App\Mail\PlanPaymentUserRejected;
use App\Mail\PlanPaymentUserVerified;
use Illuminate\Support\Facades\Log;

class PaymentHelper
{

    /**
     * Get all plan payments
     * @param int $userId
     * @param mixed $status
     * @return array
     */
    public static function getAll(
        int $userId,
        mixed $status = false,
    ): array {
        $userData = DB::table('users')
            ->where('id', $userId)
            ->first();

        if (!$userData) {
            return [
                false,
                __('lang.error')
            ];
        }

        $payments = DB::table('payment_logs')
            ->leftJoin(
                'users',
                'payment_logs.created_by_id',
                'users.id'
            )
            ->leftJoin(
                'payment_plans',
                'payment_logs.plan',
                'payment_plans.id'
            )
            ->leftJoin(
                'payment_gateways',
                'payment_logs.gateway',
                'payment_gateways.id'
            )
            ->select('payment_logs.*')
            ->addSelect('payment_plans.name as planName')
            ->addSelect(
                'payment_plans.price_monthly as priceMonthly',
                'payment_plans.price_yearly as priceYearly'
            )
            ->addSelect(
                'payment_plans.features as planFeatures',
            )
            ->addSelect('payment_gateways.name as gateway')
            ->addSelect('users.name as userName')
            ->addSelect(
                'users.id as userId',
                'users.photo as userPhoto',
                'users.email as userEmail',
            )
            ->when($status, function ($query) use ($status) {
                if ($status == 0) {
                    $query->where('payment_logs.status', 0);
                } elseif ($status == 1) {
                    $query->where('payment_logs.status', 1);
                }
            })
            ->where(function ($query) use ($userData) {
                if ($userData->type == 1) {
                    $query
                        ->where('payment_logs.created_by_id', $userData->id);
                }
            })
            ->orderBy('payment_logs.created_at', 'desc')
            ->get();

        // Create list array
        $paymentsArr = [];
        if ($payments) {
            foreach ( $payments as $payment ) {

                $userName = $payment->userName
                    ? $payment->userName
                    : __('lang.deleted_user');

                $userEmail = $payment->userEmail
                    ? $payment->userEmail
                    : __('lang.deleted_user');

                $price = $payment->duration == 1
                    ? $payment->priceMonthly
                    : $payment->priceYearly;

                $paymentsArr[] = [
                    'id' => $payment->id,
                    'date' => $payment->created_at,
                    'planName' => $payment->planName,
                    'planFeatures' => $payment->planFeatures,
                    'planDuration' => $payment->duration,
                    'priceMonthly' => $payment->priceMonthly,
                    'priceYearly' => $payment->priceYearly,
                    'planPrice' => $price,
                    'gateway' => $payment->gateway,
                    'userId' => $payment->userId ?? '-',
                    'userName' => $userName,
                    'userPhoto' => $payment->userPhoto,
                    'userEmail' => $userEmail,
                    'transaction' => $payment->transaction,
                    'status' => $payment->status,
                ];
            }
        }

        return [
            true,
            $paymentsArr
        ];
    }

    /**
     * Get single plan payment
     * @param int $userId
     * @param int $paymentId
     * @return array
     */
    public static function getSingle(
        int $userId,
        int $paymentId,
    ): array {
        $userData = DB::table('users')
            ->where('id', $userId)
            ->first();

        if (!$userData) {
            return [
                false,
                __('lang.error')
            ];
        }

        $payment = DB::table('payment_logs')
            ->leftJoin(
                'users',
                'payment_logs.created_by_id',
                'users.id'
            )
            ->leftJoin(
                'payment_plans',
                'payment_logs.plan',
                'payment_plans.id'
            )
            ->leftJoin(
                'payment_gateways',
                'payment_logs.gateway',
                'payment_gateways.id'
            )
            ->select('payment_logs.*')
            ->addSelect('payment_plans.name as planName')
            ->addSelect(
                'payment_plans.price_monthly as priceMonthly',
                'payment_plans.price_yearly as priceYearly'
            )
            ->addSelect(
                'payment_plans.features as planFeatures',
            )
            ->addSelect('payment_gateways.name as gateway')
            ->addSelect('users.name as userName')
            ->addSelect(
                'users.photo as userPhoto',
                'users.email as userEmail',
            )
            ->where('payment_logs.id', $paymentId)
            ->first();

        // Create payment data
        $paymentData = [];
        if ($payment) {

            $price = ($payment->duration == 1)
                ? $payment->priceMonthly
                : $payment->priceYearly;

            $paymentData = [
                'id' => $payment->id,
                'created_by_ip' => $payment->created_by_ip,
                'transaction' => $payment->transaction,
                'date' => $payment->created_at,
                'info' => $payment->info,
                'planName' => $payment->planName,
                'planFeatures' => $payment->planFeatures,
                'planDuration' => $payment->duration,
                'priceMonthly' => $payment->priceMonthly,
                'priceYearly' => $payment->priceYearly,
                'planPrice' => $price,
                'gateway' => $payment->gateway,
                'userName' => $payment->userName,
                'userPhoto' => $payment->userPhoto,
                'userEmail' => $payment->userEmail ?? '-',
                'status' => $payment->status,
            ];
        }

        return [
            true,
            $paymentData
        ];
    }

    /**
     * Verify a bank payment
     * @param int $userId
     * @param int $paymentId
     * @return array
     */
    public static function verifyBank(
        int $userId,
        int $paymentId,
    ): array {
        $userData = DB::table('users')
            ->where('id', $userId)
            ->first();

        if (!$userData) {
            return [
                false,
                __('lang.error')
            ];
        }

        $paymentData = DB::table('payment_logs')
            ->where('id', $paymentId)
            ->first();

        if (!$paymentData) {
            return [
                false,
                __('lang.data_not_found')
            ];
        }

        if ($paymentData->status != 0) {
            return [
                false,
                __('lang.payment_already_approved')
            ];
        }

        $buyer = DB::table('users')
            ->where('id', $paymentData->created_by_id)
            ->first();

        if (!$buyer) {
            return [
                false,
                __('lang.cannot_verify_payment')
            ];
        }

        $planData = DB::table('payment_plans')
            ->where('id', $paymentData->plan)
            ->first();

        if (!$planData) {
            return [
                false,
                __('lang.error')
            ];
        }

        $request = request();
        $now = Carbon::now()
            ->timezone('Etc/Greenwich')
            ->toDateTimeString();

        $verify = DB::table('payment_logs')
            ->where('id', $paymentId)
            ->update([
                'updated_at' => $now,
                'updated_by_id' => $userData->id,
                'updated_by_ip' => $request->ip(),
                'status' => 1
            ]);

        if ($verify) {

            try {
                $myPlan = PlanHelper::myPlan($buyer->id);

                $period = $paymentData->duration == 1
                    ? __('lang.monthly')
                    : __('lang.yearly');
                $currency = paymentSetting('currency_name');

                $mailDataUser = [
                    'plan' => $planData->name,
                    'period' => $period,
                    'gateway' => __('lang.bank'),
                    'payment' => "{$paymentData->revenue}{$currency}",
                    'info' => $paymentData->info,
                    'start' => $myPlan['start'],
                    'end' => $myPlan['end'],
                ];

                Mail::to($buyer->email)
                    ->send(new PlanPaymentUserVerified($mailDataUser));

                return [true];

            } catch (\Exception $e) {
                Log::error($e->getMessage());
                return [
                    false,
                    __('lang.error')
                ];
            }
        }

        return [
            false,
            __('lang.error')
        ];
    }

    /**
     * Delete (reject) a bank payment
     * @param int $userId
     * @param int $paymentId
     * @param int $type
     * @return array
     */
    public static function rejectBank(
        int $userId,
        int $paymentId,
    ): array {
        $userData = DB::table('users')
            ->where('id', $userId)
            ->first();

        if (!$userData) {
            return [
                false,
                __('lang.error')
            ];
        }

        $paymentData = DB::table('payment_logs')
            ->where('id', $paymentId)
            ->first();

        if (!$paymentData) {
            return [
                false,
                __('lang.data_not_found')
            ];
        }

        if ($paymentData->status != 0) {
            return [
                false,
                __('lang.payment_already_approved')
            ];
        }

        $buyer = DB::table('users')
            ->where('id', $paymentData->created_by_id)
            ->first();

        if (!$buyer) {
            DB::table('payment_logs')
                ->where('id', $paymentId)
                ->delete();
            return [true];
        }

        $planData = DB::table('payment_plans')
            ->where('id', $paymentData->plan)
            ->first();

        if (!$planData) {
            return [
                false,
                __('lang.error')
            ];
        }

        $update = DB::table('payment_logs')
            ->where('id', $paymentId)
            ->update([
                'status' => 2
            ]);

        if ($update) {

            try {
                $period = $paymentData->duration == 1
                    ? __('lang.monthly')
                    : __('lang.yearly');
                $currency = paymentSetting('currency_name');

                $mailDataUser = [
                    'plan' => $planData->name,
                    'period' => $period,
                    'gateway' => 'Bank Payment',
                    'payment' => "{$paymentData->revenue}{$currency}",
                    'info' => $paymentData->info,
                ];

                Mail::to($buyer->email)
                    ->send(new PlanPaymentUserRejected($mailDataUser));

                return [true];

            } catch (\Exception $e) {
                Log::error($e->getMessage());
                return [
                    false,
                    __('lang.error')
                ];
            }
            
        }

        return [
            false,
            __('lang.error')
        ];
    }

    /**
     * Make payment via Stripe (Credit Card)
     * @param int $userId
     * @param int $type
     * @param array $formData
     * @return array
     */
    public static function payStripe(
        int $userId,
        array $formData
    ) {
        $userData = DB::table('users')
            ->where('id', $userId)
            ->first();

        if (!$userData) {
            return [
                false,
                __('lang.error')
            ];
        }

        $planData = DB::table('payment_plans')
            ->where('status', 1)
            ->where('id', $formData['plan'])
            ->first();

        if (!$planData) {
            return [
                false,
                __('lang.data_not_found')
            ];
        }

        // Stripe Gateway Data
        $stripeData = DB::table('payment_gateways')
            ->where('id', 2)
            ->first();

        if (!$stripeData) {
            return [
                false,
                __('lang.payment_method_unavailable')
            ];
        }

        $myPlan = myPlan();

        if ($myPlan['free'] && $planData->free == 1) {
            return [
                false,
                __('lang.already_free_plan')
            ];
        }

        $currency = paymentSetting('currency_name');

        $price = $formData['period'] == 1
            ? $planData->price_monthly
            : $planData->price_yearly;

        $description = $formData['period'] == 1
            ? "{$planData->name} - " . __('lang.monthly')
            : "{$planData->name} - " . __('lang.yearly');

        $purchaseData = [
            'amount' => $price * 100,
            'currency' => $currency,
            'source' => $formData['token'],
            'description' => $description,
        ];

        try {
            StripeLib\Stripe::setApiKey($stripeData->secret);
            $payment = StripeLib\Charge::create($purchaseData);

            if ($payment->status == 'succeeded') {

                $date = Carbon::now()
                    ->setTimezone('Etc/Greenwich')
                    ->toDateTimeString();
                $request = request();
                DB::table('payment_logs')->insert([
                    'created_at' => $date,
                    'created_by_id' => $userData->id,
                    'created_by_ip' => $request->ip(),
                    'updated_at' => $date,
                    'updated_by_id' => $userData->id,
                    'updated_by_ip' => $request->ip(),
                    'plan' => $planData->id,
                    'gateway' => $stripeData->id,
                    'duration' => $formData['period'],
                    'transaction' => $payment->balance_transaction,
                    'info' => null,
                    'revenue' => $price,
                    'status' => 1,
                ]);

                try {
                    $myPlan = PlanHelper::myPlan($userData->id);

                    $period = $formData['period'] == 1
                        ? "Monthly"
                        : "Yearly";
                    $currency = paymentSetting('currency_name');

                    $mailDataAdmin = [
                        'name' => $userData->name,
                        'email' => $userData->email,
                        'ip' => $request->ip(),
                        'plan' => $planData->name,
                        'period' => $period,
                        'gateway' => 'Stripe',
                        'revenue' => "{$price}{$currency}",
                    ];

                    $mailDataUser = [
                        'plan' => $planData->name,
                        'period' => $period,
                        'gateway' => 'Stripe',
                        'payment' => "{$price}{$currency}",
                        'start' => $myPlan['start'],
                        'end' => $myPlan['end'],
                    ];

                    Mail::to(emailSetting('email'))
                        ->send(new PlanPaymentAdminSuccess($mailDataAdmin));
                    Mail::to($userData->email)
                        ->send(new PlanPaymentUserSuccess($mailDataUser));

                    return [true];

                } catch (\Exception $e) {
                    Log::error($e->getMessage());
                    return [
                        false,
                        __('lang.error')
                    ];
                }

            } else {
                return [
                    false,
                    __('lang.error')
                ];
            }
        } catch (\Stripe\Exception\CardException $e) {
            return [
                false,
                $e->getError()->message
            ];
        } catch (\Stripe\Exception\InvalidRequestException $e) {
            return [
                false,
                $e->getError()->message
            ];
        } catch (Exception $e) {
            return [
                false,
                $e->getError()->message
            ];
        }
    }

    /**
     * Make payment via Bank
     * @param int $userId
     * @param int $type
     * @param array $formData
     * @return array
     */
    public static function payBank(
        int $userId,
        array $formData
    ): array {
        $userData = DB::table('users')
            ->where('id', $userId)
            ->first();

        if (!$userData) {
            return [
                false,
                __('lang.error')
            ];
        }

        $planData = DB::table('payment_plans')
            ->where('status', 1)
            ->where('id', $formData['plan'])
            ->first();

        if (!$planData) {
            return [
                false,
                __('lang.data_not_found')
            ];
        }

        // Bank Gateway Data
        $bankData = DB::table('payment_gateways')
            ->where('id', 1)
            ->first();

        if (!$bankData) {
            return [
                false,
                __('lang.payment_method_unavailable')
            ];
        }

        $myPlan = myPlan();

        if ($myPlan['free'] && $planData->free == 1) {
            return [
                false,
                __('lang.already_free_plan')
            ];
        }

        $price = $formData['period'] == 1
            ? $planData->price_monthly
            : $planData->price_yearly;

        $date = Carbon::now()
            ->setTimezone('Etc/Greenwich')
            ->toDateTimeString();
        $request = request();
        $key = generateCode(
            'payment_logs',
            'transaction',
            12
        );

        $save = DB::table('payment_logs')->insert([
            'created_at' => $date,
            'created_by_id' => $userData->id,
            'created_by_ip' => $request->ip(),
            'updated_at' => $date,
            'updated_by_id' => $userData->id,
            'updated_by_ip' => $request->ip(),
            'plan' => $planData->id,
            'gateway' => $bankData->id,
            'duration' => $formData['period'],
            'transaction' => 'BNK' . $key,
            'info' => $formData['bank-info'],
            'revenue' => $price,
            'status' => 0,
        ]);

        if ($save) {

            try {
                $period = $formData['period'] == 1
                    ? __('lang.monthly')
                    : __('lang.yearly');
                $currency = paymentSetting('currency_name');

                $mailDataAdmin = [
                    'name' => $userData->name,
                    'email' => $userData->email,
                    'ip' => $request->ip(),
                    'plan' => $planData->name,
                    'period' => $period,
                    'gateway' => __('lang.bank'),
                    'revenue' => "{$price}{$currency}",
                    'info' => $formData['bank-info'],
                ];

                $mailDataUser = [
                    'plan' => $planData->name,
                    'period' => $period,
                    'gateway' => __('lang.bank'),
                    'payment' => "{$price}{$currency}",
                    'info' => $formData['bank-info'],
                ];

                Mail::to(emailSetting('email'))
                    ->send(new PlanPaymentAdminPending($mailDataAdmin));
                Mail::to($userData->email)
                    ->send(new PlanPaymentUserPending($mailDataUser));

                return [true];

            } catch (\Exception $e) {
                Log::error($e->getMessage());
                return [
                    false,
                    __('lang.error')
                ];
            }
        }

        return [
            false,
            __('lang.error')
        ];
    }

    /**
     * Check given user can buy the plan
     * @param int $userId
     * @param int $planId
     * @return mixed
     */
    public static function check(
        int $userId,
        int $planId
    ): bool {
        // Get user data
        $user = DB::table('users')
            ->where('id', '=', $userId)
            ->first();

        // Check user is eligible to buy
        if (!$user || $user->type != 1) {
            return false;
        }

        // Get plan data
        $plan = DB::table('payment_plans')
            ->where('id', '=', $planId)
            ->first();

        // Check the eligibility of the plan
        if (!$plan || $plan->status != 1) {
            return false;
        }

        // Everything is ok
        return true;
    }

    /**
     * Create a client token for Stripe
     * @param array $paymentData
     * @return mixed
     */
    public static function stripeToken(
        array $paymentData,
    ): array {

        // Plan payment
        if (
            isset($paymentData['plan'])
            && isset($paymentData['period'])
        ) {

            // Get plan data
            $plan = DB::table('payment_plans')
                ->where('id', '=', $paymentData['plan'])
                ->first();

            // Check the eligibility of the plan
            if (!$plan || $plan->status != 1) {
                return [
                    false,
                    __('lang.cannot_purchase_plan')
                ];
            }

            $amount = ($paymentData['period'] == 1)
                ? $plan->price_monthly
                : $plan->price_yearly;

            $stripeData = DB::table('payment_gateways')
                ->where('id', 2)
                ->first();

            if (
                $stripeData->status == 0
                || !$stripeData->public
                || !$stripeData->secret
            ) {
                return [
                    false,
                    __('lang.payment_method_unavailable')
                ];
            }

            try {

                $stripe = new \Stripe\StripeClient("{$stripeData->secret}");

                if (session("stripe_{$paymentData['plan']}")) {

                    $paymentIntent = $stripe->paymentIntents->retrieve(
                        session("stripe_{$paymentData['plan']}")
                    );

                    return [
                        true,
                        $paymentIntent->client_secret
                    ];

                } else {

                    $url = "package={$paymentData['plan']}"
                        . "&period={$paymentData['period']}";
                    $encryptedUrl = encrypt($url);

                    $paymentIntent = $stripe->paymentIntents->create([
                        'amount' => $amount * 100,
                        'currency' => paymentSetting('currency_name'),
                        'automatic_payment_methods' => [
                            'enabled' => true,
                        ],
                        'metadata' => [
                            'userId' => userData('id'),
                            'userEmail' => userData('email'),
                            'planId' => $paymentData['plan'],
                            'planName' => $plan->name,
                            'period' => $paymentData['period'],
                            'source' => url("/pay/{$encryptedUrl}"),
                        ],
                        'description' => $plan->name
                    ]);

                    session([
                        "stripe_{$paymentData['plan']}" => $paymentIntent->id
                    ]);

                    return [
                        true,
                        $paymentIntent->client_secret
                    ];
                }

            } catch (Exception $e) {
                return [
                    false,
                    $e
                ];
            }

        }

        return [
            false,
            __('lang.error')
        ];
    }

    /**
     * Create a client token for Stripe
     * @param array $paymentData
     * @return mixed
     */
    public static function startPlan(
        array $paymentData,
    ): array {

        if (
            !isset($paymentData['plan'])
            || !isset($paymentData['period'])
            || !isset($paymentData['gateway'])
        ) {
            return [
                false,
                __('lang.error')
            ];
        }

        // Get plan data
        $plan = DB::table('payment_plans')
            ->where('id', '=', $paymentData['plan'])
            ->first();

        // Check the eligibility of the plan
        if (!$plan || $plan->status != 1) {
            return [
                false,
                __('lang.cannot_purchase_plan')
            ];
        }

        $amount = ($paymentData['period'] == 1)
            ? $plan->price_monthly
            : $plan->price_yearly;

        $date = Carbon::now()->toDateTimeString();
        $request = request();

        if ($paymentData['gateway'] == 1) {

            $bankData = DB::table('payment_gateways')
                ->where('id', $paymentData['gateway'])
                ->first();

            if ($bankData->status == 0) {
                return [
                    false,
                    __('lang.payment_method_unavailable')
                ];
            }

            $userId = userData('id');
            $key = "{$userId}##{$paymentData['plan']}##"
                . "{$paymentData['period']}##{$date}##"
                . "{$paymentData['gateway']}";
            $encryptedKey = encrypt($key);

            $paymentLog = [
                'created_at' => $date,
                'created_by_id' => $userId,
                'created_by_ip' => $request->ip(),
                'updated_at' => $date,
                'updated_by_id' => $userId,
                'updated_by_ip' => $request->ip(),
                'plan' => $paymentData['plan'],
                'gateway' => $paymentData['gateway'],
                'duration' => $paymentData['period'],
                'transaction' => $key,
                'info' => $paymentData['info'] ?? null,
                'revenue' => $amount,
                'status' => 0,
            ];

            $create = DB::table('payment_logs')->insert($paymentLog);

            if ($create) {

                NHelper::notify($userId, 1, 6);

                return [
                    true,
                    __('lang.payment_received')
                ];
            }

            return [
                false,
                __('lang.error')
            ];



        } elseif ($paymentData['gateway'] == 2) {

            session()->forget('stripe_payment_id');

            $stripeData = DB::table('payment_gateways')
                ->where('id', $paymentData['gateway'])
                ->first();

            if (
                $stripeData->status == 0
                || !$stripeData->public
                || !$stripeData->secret
            ) {
                return [
                    false,
                    __('lang.payment_method_unavailable')
                ];
            }

            $userId = userData('id');
            $key = "{$userId}##{$paymentData['plan']}##"
                . "{$paymentData['period']}##{$date}##"
                . "{$paymentData['gateway']}";
            $encryptedKey = encrypt($key);

            $paymentLog = [
                'created_at' => $date,
                'created_by_id' => $userId,
                'created_by_ip' => $request->ip(),
                'updated_at' => $date,
                'updated_by_id' => $userId,
                'updated_by_ip' => $request->ip(),
                'plan' => $paymentData['plan'],
                'gateway' => $stripeData->id,
                'duration' => $paymentData['period'],
                'transaction' => $encryptedKey,
                'info' => null,
                'revenue' => $amount,
                'status' => 1,
            ];

            $create = DB::table('payment_logs')->insert($paymentLog);

            if ($create) {

                NHelper::notify($userId, 1, 6);

                return [
                    true,
                    __('lang.plan_assigned')
                ];
            }

            return [
                false,
                __('lang.error')
            ];

        }

        return [
            false,
            __('lang.error')
        ];
    }

}
