<?php

namespace App\Modules\Company\Subscription\Payments;

use App\Models\Company;
use App\Models\CompanySubscriptions;
use App\Models\CompanySubscriptionPayments;
use App\Models\CompanySubscriptionRates;
use App\Jobs\Company\ProcessSendEmailPurchaseSubscription;
use App\Modules\Company\Subscription\Discount\Coupon;
use App\Modules\Company\Subscription\Subscription;
use App\Traits\CreateUserActionTraits;
use App\Traits\UserTraits;
use App\Traits\ValidatorTraits;
use App\Traits\RetrieveCustomerDataTraits;

use Exception;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

use Carbon\Carbon;

use Stripe;

class RenewPayment 
{
    use ValidatorTraits; 
    use UserTraits; 
    use CreateUserActionTraits; 
    use RetrieveCustomerDataTraits;

    public $coupon;
    public $currentUser;
    public $companyDetails;
    public $companySubscription;

    public function __construct()
    {
        Stripe\Stripe::setApiKey(config('stripe.secret_key'));

        $this->coupon              = new Coupon;
        $this->currentUser         = $this->getCurrentUser();
        $this->companyDetails      = Company::where('id', $this->currentUser->company_id)->first();
        $this->companySubscription = new Subscription;
    }

    public function renewSubscription($payload)
    {
        $currentCompanySubscription = CompanySubscriptions::where('company_id', $this->currentUser->company_id)->first();

        if(!$currentCompanySubscription)
        { 
            return response()->json(['error' => 'Company is not yet susbcribed.']);
        }
        //selected subscription
        $subscriptionRateDetails = CompanySubscriptionRates::where('id', $payload['subscription_rate_id'])->first(); 
        
        if(!$subscriptionRateDetails)
        {
            return response()->json(['error' => 'Subscription rate does not exist.']);
        }

        $subscriptionBillingCyle = $payload['mode'];
        
        if(!($subscriptionBillingCyle == 'monthly' || $subscriptionBillingCyle == 'yearly'))
        {
            return response()->json(['error' => 'Billing cycle is invalid.', 'mode' => $subscriptionBillingCyle]);
        }

        $currentEmployeeCount = $this->companySubscription->getCompanyEmployeesCount($this->currentUser->company_id);

        // if selected subscription is not unlimited, check the subscription capacity
        if($subscriptionRateDetails->is_unlimited==false)
        {
            if($currentEmployeeCount > $subscriptionRateDetails->max_employee)
            {
                return response()->json(['error' => "Your current employee's number exceeded the capacity of the selected subscription."]);
            }
        }

        // Is good to go for renewal validation
        $dateOfTransaction           = Carbon::now()->format('Y-m-d h:i:s');
        $currentSubscriptionDuration = $currentCompanySubscription->duration;
        $daysDifference              = Carbon::parse($dateOfTransaction)->diffInDays($currentSubscriptionDuration);
        $isPast                      = $dateOfTransaction > $currentSubscriptionDuration;
        $isReadyForRenewal           = ($dateOfTransaction < $currentSubscriptionDuration && $daysDifference >= 7);

        if (!($isPast || $isReadyForRenewal)) 
        {
            return response()->json(
                [
                    'error'             => 'Subscription is not yet ready for renewal. Please refresh the page.',
                    'daysDifference'    => $daysDifference,
                    'daysDifferenceTwo' => Carbon::parse($currentSubscriptionDuration)->diffInDays($dateOfTransaction),
                ]
            );
        }     
        
        // Coupon Discount Validation
        $couponData = false;

        if (isset($payload['couponCode'])) 
        {
            $couponData = $this->coupon->fetchCouponDetails($payload['couponCode']);
            
            if($couponData->for_all == false)
            {
                if($couponData->company_subscription_rates_id != $subscriptionRateDetails->id)
                {
                    return response()->json(['error' => 'Coupon code is not applicable on the choosen subscription rate.']); 
                }
            }

            if ($couponData->is_disabled) 
            {
                return response()->json(['error' => 'Coupon code is currently disabled.']);
            }

            $companyUsage = $this->coupon->checkCompanyUsage($this->currentUser->company_id);
            
            //how many times the company can use the coupon
            if($companyUsage && $companyUsage->coupon_subscription_rates_id == $couponData->coupon_subscription_rates_id)
            {
                if($companyUsage->company_usage >= $companyUsage->range)
                {
                    return response()->json(['error' => 'You have already exceeded the maximum monthly range of the coupon.']);
                }
            }
        }

        $newSubcriptionDurationDate  = Carbon::now()->addDays(360)->format('Y-m-d h:i:s');
        $monthlySubscriptionDuration = 0;

        if($currentSubscriptionDuration > $dateOfTransaction)
        {
            $newSubcriptionDurationDate = Carbon::parse($currentSubscriptionDuration)->addDays(360)->format('Y-m-d h:i:s');
        }

        $subscriptionRatePrice = 0;

        if($subscriptionBillingCyle == 'yearly')
        {
            $subscriptionRatePrice = (float) $subscriptionRateDetails->yearly_rate;
        }
        else
        {
            $subscriptionRatePrice = (float) $subscriptionRateDetails->monthly_rate;
            $monthlySubscriptionDuration = Carbon::now()->addDays(30)->format('Y-m-d h:i:s');
        }

       //Compute coupon discount
        $couponDiscount = 0;
        if($couponData)
        {
            $couponDiscount = $this->getCouponCodeDiscount($couponData, $subscriptionBillingCyle, $subscriptionRatePrice);
        }

        $companyDiscount = 0;

        // Company Discount
        if($this->companyDetails->discount)
        {
            $companyDiscount = $this->getDiscountedValue($this->companyDetails->discount, $subscriptionRatePrice);
        }

        $previousBalance  = (float) $currentCompanySubscription->amount_due;
        $totalDiscount    = $couponDiscount + $companyDiscount;
        $totalAmountToPay = ($subscriptionRatePrice + $previousBalance) - $totalDiscount;
        $newAmountDue     = 0;

        if($totalAmountToPay <= 0)
        {
            $newAmountDue     = $totalAmountToPay;
            $totalAmountToPay = 0;
        }

        $paymentPayload = (object)
        [
            'stripeToken'                 => $payload['token'],
            'stripeCustomerID'            => $currentCompanySubscription->customer_id,
            'previousBalance'             => $previousBalance,
            'newAmountDue'                => (float) $newAmountDue,
            'couponCode'                  => isset($payload['couponCode']) ? $payload['couponCode'] : null,
            'couponDiscount'              => $couponDiscount,
            'companyDiscount'             => $companyDiscount,
            'dateOfTransaction'           => $dateOfTransaction,
            'subscriptionRatePrice'       => $subscriptionRatePrice,
            'subscriptionBillingCyle'     => $subscriptionBillingCyle,
            'subcriptionRateID'           => $subscriptionRateDetails->id,
            'subcriptionRateName'         => $subscriptionRateDetails->name,
            'subcriptionDurationDate'     => $newSubcriptionDurationDate,
            'monthlySubscriptionDuration' => $monthlySubscriptionDuration,
            'totalDiscount'               => $totalDiscount,
            'amountToPay'                 => $totalAmountToPay,
            'currentUserID'               => $this->currentUser->id,
            'currentUserEmailAddress'     => $this->currentUser->email_address,
            'companyID'                   => $this->currentUser->company_id,
            'couponData'                  => $couponData,
        ];

        $user_data = 
        [
            'title'       => 'ConX Purchase Subscription Confirmation',
            'email'       => $this->currentUser->email_address,
            'currentUser' => $this->currentUser->first_name . ' ' . $this->currentUser->last_name,
            'amountPaid'  => round($totalAmountToPay, 2),
            'duration'    => date("F j, Y", strtotime($newSubcriptionDurationDate))
        ];

        ProcessSendEmailPurchaseSubscription::dispatch($this->currentUser->email_address, $user_data)->onConnection('jobs_emails')->onQueue('sendemail');

        return $this->stripePayment($paymentPayload);
    }

    public function stripePayment($paymentPayload)
    {
        $invoiceNumber = 'IVN-'.Carbon::now()->format('Y-m-d-h-i-s').'-'.Str::random(10);

        try 
        {
            DB::beginTransaction();
            //check  if company has subscription
            $checkCustomer = $this->retrieveStripeCustomerData($paymentPayload->stripeCustomerID);

            if($checkCustomer)
            {
                // Update if company has payment details
                $customer = \Stripe\Customer::update(
                    $paymentPayload->stripeCustomerID,
                    [
                        'email'    => $paymentPayload->currentUserEmailAddress,
                        'source'   => $paymentPayload->stripeToken,
                        "metadata" => ["company_id" => $paymentPayload->companyID]
                    ]
                );

                $stripeCustomerID = $paymentPayload->stripeCustomerID;
            }
            else
            {
                $customer = \Stripe\Customer::create(
                    [
                        'email'    => $paymentPayload->currentUserEmailAddress,
                        'source'   => $paymentPayload->stripeToken,
                        "metadata" => ["company_id" => $paymentPayload->companyID]
                    ]
                );
                
                $stripeCustomerID = $customer->id;
            }

            //Stripe charge payment
            if($paymentPayload->amountToPay <= 0)
            {
                // Big discount, negative amount due
                $stripePaymentTransaction = (object)
                [
                    'id'          => 'no-charge-transaction',
                    'status'      => 'succeeded',
                    'receipt_url' => '',
                ];
            }
            else
            {
                $stripePaymentTransaction = \Stripe\Charge::create(
                    [
                        'customer'      => $stripeCustomerID,
                        'amount'        => $paymentPayload->amountToPay * 100,
                        'currency'      => 'usd',
                        'receipt_email' => $paymentPayload->currentUserEmailAddress,
                    ]
                );
            }

            // NOTE: Before m save calculate mona ang remaining balance
            if(($stripePaymentTransaction && $stripePaymentTransaction->status == "succeeded"))
            {
                
                    $companySubscriptionData = array(
                        'subscription_rate_id' => $paymentPayload->subcriptionRateID,
                        'company_id'           => $paymentPayload->companyID,
                        'customer_id'          => $stripeCustomerID,
                        'invoice_no'           => $invoiceNumber,
                        'date_subscribed'      => $paymentPayload->dateOfTransaction,
                        'current_month_paid'   => $paymentPayload->monthlySubscriptionDuration? $paymentPayload->monthlySubscriptionDuration: $paymentPayload->subcriptionDurationDate,   //monthly duratioon for monthly mode
                        'duration'             => $paymentPayload->subcriptionDurationDate,
                        'mode_of_payment'      => $paymentPayload->subscriptionBillingCyle,
                        'rate'                 => $paymentPayload->subscriptionRatePrice,
                        'discount'             => $paymentPayload->totalDiscount,
                        'total_paid'           => $paymentPayload->amountToPay,
                        'amount_paid'          => $paymentPayload->amountToPay,
                        'previous_bill'        => $paymentPayload->previousBalance,
                        'current_bill'         => $paymentPayload->subscriptionRatePrice,
                        'amount_due'           => $paymentPayload->newAmountDue,                                                                                                          //equal to balance
                        'penalty_charge'       => 0,
                        'is_blocked'           => 'false',
                        'is_expired'           => 'false',
                        'is_monthly_expired'   => 'false',
                    );

                $companySubscriptionTransaction = $this->updateCompanySubscription($paymentPayload->companyID, $companySubscriptionData);

                $paymentDetails = array(
                    'charge_id'               => $stripePaymentTransaction->id,
                    'company_subscription_id' => $companySubscriptionTransaction->id,
                    'subscription_rate_id'    => $paymentPayload->subcriptionRateID,
                    'company_id'              => $paymentPayload->companyID,
                    'customer_id'             => $stripeCustomerID,
                    'user_id'                 => $paymentPayload->currentUserID,
                    'invoice_no'              => $invoiceNumber,
                    'mode_of_payment'         => $paymentPayload->subscriptionBillingCyle,
                    'rate'                    => $paymentPayload->subscriptionRatePrice,
                    'duration'                => $paymentPayload->subcriptionDurationDate,
                    'discount'                => $paymentPayload->companyDiscount,
                    'coupon_discount'         => $paymentPayload->couponDiscount,
                    'total_discounted_amount' => $paymentPayload->totalDiscount,
                    'amount_paid'             => $paymentPayload->amountToPay,
                    'month_paid'              => $paymentPayload->subcriptionDurationDate,
                    'current_bill'            => $paymentPayload->subscriptionRatePrice,
                    'previous_bill'           => $paymentPayload->previousBalance,
                    'penalty_charge'          => 0,
                    'balance'                 => $paymentPayload->newAmountDue,              //equal to balance
                    'type'                    => 'renew',
                    'total_paid'              => $paymentPayload->amountToPay,
                );

                $subscriptionPaymentTransaction = $this->createSubscriptionPayment($paymentDetails);
            }

            $saveUserAction = $this->createUserAction('Subscribed the '. $paymentPayload->subcriptionRateName. ' subscription plan');

            if ($companySubscriptionTransaction && $subscriptionPaymentTransaction)
            {
                DB::commit();

                /* Add usage on coupon code */
                if($paymentPayload->couponDiscount && $paymentPayload->couponData)
                {
                    $companyUsageData = array(
                        'couponID' => $paymentPayload->couponData->id,
                        'companyID' => $paymentPayload->companyID,
                    );

                    $this->coupon->addUsageOnCouponDiscount($companyUsageData);
                }

                return [
                    'result'      => $stripePaymentTransaction->status,
                    'invoice_no'  => $invoiceNumber,
                    'receipt_url' => $stripePaymentTransaction->receipt_url,
                    'rate_name'   => $paymentPayload->subcriptionRateName,
                    'note'        => 'Successfully Renewed Subscription!',
                ];
            } 
            else 
            {
                DB::rollBack();
            }
            
        }
        catch (Exception $e)
        {
            http_response_code(500);

            echo json_encode(
                [
                    'error' => $e->getMessage()
                ]
            );
            
            return response()->json(
                [
                    'errors' => 
                    [
                        $e->getMessage()
                    ]
                ]
            )->setStatusCode(500);
        }
    }

    public function getCouponCodeDiscount($couponData, $mode, $subscriptionRatePrice)
    {
        if(!$couponData)
        {
            return 0;
        }
        else
        {
            if($mode == 'monthly')
            {
                // Coupon Discount
                if($couponData->type == 'fixed')
                {
                    return (float) $couponData->discount;
                }
                else
                {
                    return $this->getDiscountedValue($couponData->discount, $subscriptionRatePrice);
                }
            }
            else if($mode == 'yearly')
            {
                if($couponData->type == 'fixed')
                {
                    return (float) $couponData->discount * 12;
                }
                else
                {
                    return  $this->getDiscountedValue($couponData->discount, $subscriptionRatePrice);
                }
            }
            return 0;
        }
    }

    public function getDiscountedValue($percentage, $amountToBePercentage)
    {
        return (float)(($percentage / 100) * $amountToBePercentage);
    }

    public function updateCompanySubscription($companyID, $data)
    {
        return tap(CompanySubscriptions::where('company_id', $companyID))->update($data)->first();
    }

    public function createSubscriptionPayment($data)
    {
        return CompanySubscriptionPayments::create($data);
    }

}