<?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\Modules\Company\Subscription\Discount\Coupon;
use App\Jobs\Company\ProcessSendEmailPurchaseSubscription;
use App\Traits\UserTraits;
use App\Traits\CreateUserActionTraits;
use App\Traits\RetrieveCustomerDataTraits;
use App\Traits\ValidatorTraits;

use Exception;

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

use Carbon\Carbon;
use Stripe;

class UpgradePayment 
{
    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 = $this->fetchCompanySubscription($this->currentUser->company_id);
    }

    public function upgradeSubscription($payload)
    {
        if (!$this->companySubscription)
        { 
            return response()->json(['error' => 'Company is not yet susbcribed.']);
        }

        $validateCurrentSubscription = CompanySubscriptionRates::where('id', $this->companySubscription->subscription_rate_id)->first();
        
        if($validateCurrentSubscription->is_unlimited)
        {
            return response()->json(['error' => 'Company has already subscribed to the most upgraded plan.']);
        }

        $selectedSubscriptionRateDetails = CompanySubscriptionRates::where('id', $payload['selectedSubscriptionRateID'])->first();
        
        if(!$selectedSubscriptionRateDetails)
        {
            return response()->json(['error' => 'Selected subscription rate does not exist.']);
        }

        $dayCount = 0;
        $subscriptionRatePrice = 0;
        
        if($this->companySubscription->mode_of_payment == 'monthly')
        {
            $subscriptionRatePrice = (float) $selectedSubscriptionRateDetails->monthly_rate;
            $subscriptionBillingCyle = 'monthly';
            $dayCount = 30;
        }
        else if($this->companySubscription->mode_of_payment == 'yearly')
        {
            $subscriptionRatePrice = (float) $selectedSubscriptionRateDetails->yearly_rate;
            $subscriptionBillingCyle = 'yearly';
            $dayCount = 360;

        }
        else
        {
            return response()->json(['error' => 'There is something wrong in your company subscription. Please contact the system administrator for more details.']);
        }

        // 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 != $selectedSubscriptionRateDetails->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.']);
                }
            }
        }

         //Compute company discount
        $companyDiscount = 0;   

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

        //Compute coupon discount
        $couponDiscount = 0;

        if($couponData)
        {
            $couponDiscount = $this->getCouponCodeDiscount($couponData, $subscriptionBillingCyle, $subscriptionRatePrice);
        }

        $dateOfTransaction               = Carbon::now()->format('Y-m-d h:i:s');
        $currentPaidSubscriptionDuration = $this->companySubscription->current_month_paid;
        $additionalPlanCharge            = 0;
        $remainingConsumableAmount       = 0;
        $recentBillBalance               = (float) $this->companySubscription->amount_due;  //balance from previous payment

        $currentRate       = (float) $this->companySubscription->rate;
        $upgradedRate      = $subscriptionRatePrice;
        $currentDailyRate  = $currentRate / $dayCount;
        $upgradedDailyRate = $upgradedRate / $dayCount;

        $remainingConsumableDays = Carbon::parse($dateOfTransaction)->diffInDays($currentPaidSubscriptionDuration);

        $additionalPlanCharge      = $upgradedDailyRate * $remainingConsumableDays;
        $remainingConsumableAmount = $currentDailyRate * $remainingConsumableDays;
        $amountDifference          = $additionalPlanCharge - $remainingConsumableAmount;

        $totalAmountToPay = $recentBillBalance + $amountDifference - ($couponDiscount + $companyDiscount);

        $amountDue       = 0;
        $newPreviousBill = 0;
        $totalDiscount   = $couponDiscount + $companyDiscount;

        if($totalAmountToPay <= 0)
        {
            $amountDue        = $totalAmountToPay;
            $totalAmountToPay = 0;
            $newPreviousBill  = $amountDue;
        }

        $paymentPayload = (object)[
            'additionalPlanCharge'            => $additionalPlanCharge,
            'remainingConsumableDays'         => $remainingConsumableDays,
            'companyDiscount'                 => $companyDiscount,
            'couponDiscount'                  => $couponDiscount,
            'currentDailyRate'                => $currentDailyRate,
            'subscriptionRatePrice'           => $subscriptionRatePrice,
            'dateOfTransaction'               => $dateOfTransaction,
            'recentBillBalance'               => $recentBillBalance,
            'currentPaidSubscriptionDuration' => $currentPaidSubscriptionDuration,
            'remainingConsumableAmount'       => $remainingConsumableAmount,
            'upgradedDailyRate'               => $upgradedDailyRate,
            'amountDifference'                => $amountDifference,
            'amountToPay'                     => number_format($totalAmountToPay, 2),
            'stripeCustomerID'                => $this->companySubscription->customer_id,
            'companyID'                       => $this->currentUser->company_id,
            'currentUserEmailAddress'         => $this->currentUser->email_address,
            'currentUserID'                   => $this->currentUser->id,
            'subcriptionRateID'               => $selectedSubscriptionRateDetails->id,
            'amountDue'                       => $amountDue,
            'totalDiscount'                   => $totalDiscount,
            'subscriptionBillingCyle'         => $subscriptionBillingCyle,
            'subcriptionDurationDate'         => $this->companySubscription->duration,
            'subcriptionRateName'             => $selectedSubscriptionRateDetails->name,
            'stripeToken'                     => $payload['token'],
            'couponCode'                      => isset($payload['couponCode']) ? $payload['couponCode'] : null,
            'couponData'                      => $couponData,
            'newPreviousBill'                 => $newPreviousBill,
        ];

        $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($this->companySubscription->duration))
        ];

        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,
                    'rate'                 => $paymentPayload->subscriptionRatePrice,
                    'discount'             => $paymentPayload->totalDiscount,
                    'total_paid'           => $paymentPayload->amountToPay,
                    'amount_paid'          => $paymentPayload->amountToPay,
                    'current_bill'         => $paymentPayload->amountDifference,
                    'previous_bill'        => $paymentPayload->newPreviousBill,
                    'amount_due'           => $paymentPayload->amountDue,               //equal to balance
                    'penalty_charge'       => 0,
                    'is_blocked'           => '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->currentPaidSubscriptionDuration,
                    'current_bill'            => $paymentPayload->amountDifference,
                    'previous_bill'           => $paymentPayload->recentBillBalance,
                    'penalty_charge'          => 0,
                    'balance'                 => $paymentPayload->amountDue,                         //equal to amount due
                    'type'                    => 'upgrade',
                    'total_paid'              => $paymentPayload->amountToPay,
                );

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

            $this->createUserAction('Upgraded the subscription into '. $paymentPayload->subcriptionRateName);

            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'        => 'Subscription Successfully Upgraded!',
                ];
            } 
            else 
            {
                DB::rollBack();
            }
        }
        catch (Exception $e) 
        {
            http_response_code(500);
            echo json_encode(['error' => $e->getMessage()]);

            return response()->json(['errors' => ["Sorry, you have used a non-existing US mobile number. {$e->getMessage()}"]])->setStatusCode(201);
        }
    }

    public function fetchCompanySubscription($id)
    {
        return CompanySubscriptions::where('company_id', $id)->first();
    }

    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);
    }

    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;
        }
    }
}