<?php
namespace App\Services;

use App\Models\Product;
use App\Models\ShippingCountry;
use App\Services\Yard\Commission;
use App\Services\Yard\Margin;
use App\Services\Yard\MarginItems;
use Gloudemans\Shoppingcart\Cart;
use Gloudemans\Shoppingcart\CartItem;
use Gloudemans\Shoppingcart\Contracts\Buyable;
use Illuminate\Session\SessionManager;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\Collection;


class Yard extends Cart
{
    private $shipping_price = 0;
    private $shipping_price_net = 0;
    private $shipping_tax_rate = 0;
    private $shipping_tax = 0;
    private $shipping_country_id = 0; //default de
    private $shipping_is_for;
    //private $num_comp;
    private $ysession;
    private $yinstance;
    private $shopping_data = [];
    private $user;
    private $payment_credit;

    private $yard_commission;
    private $yard_margin;

    private $global_tax_rate = 19;

    public function __construct(SessionManager $session, Dispatcher $events)
    {
        $this->ysession = $session;
        $this->yinstance = sprintf('%s.%s', 'cart', 'shipping_extras');
        if($this->getYardExtra('shipping_price')){
            $this->shipping_price = (float) ($this->getYardExtra('shipping_price'));
        }

        if($this->getYardExtra('shipping_price_net')){
            $this->shipping_price_net = (float) ($this->getYardExtra('shipping_price_net'));
        }

        if($this->getYardExtra('shipping_tax_rate')){
            $this->shipping_tax_rate = (float) ($this->getYardExtra('shipping_tax_rate'));
        }

        if($this->getYardExtra('shipping_tax')){
            $this->shipping_tax = (float) ($this->getYardExtra('shipping_tax'));
        }

        if($this->getYardExtra('shipping_country_id')){
            $this->shipping_country_id =  $this->getYardExtra('shipping_country_id');
        }

        if($this->getYardExtra('shipping_is_for')){
            $this->shipping_is_for =  $this->getYardExtra('shipping_is_for');
        }

        if($this->getYardExtra('shopping_user')){
            $this->user =  $this->getYardExtra('shopping_user');
        }

        if($this->getYardExtra('user')){
            $this->user =  $this->getYardExtra('user');
        }
        if($this->getYardExtra('payment_credit')){
            $this->payment_credit =  $this->getYardExtra('payment_credit');
        }

        if($this->getYardExtra('yard_commission')){
            $this->yard_commission =  $this->getYardExtra('yard_commission');
        }

        if($this->getYardExtra('yard_margin')){
            $this->yard_margin =  $this->getYardExtra('yard_margin');
        }

        /*if($this->getYardExtra('num_comp')){
            $this->num_comp = $this->getYardExtra('num_comp');
        }*/


        parent::__construct($session, $events);

        if(gettype($this->shipping_country_id) !== 'object' && $this->shipping_country_id == 0){
            $shippingCountry = ShippingCountry::first();
            if($shippingCountry){
                $this->shipping_country_id = $shippingCountry->id;
            }
        }

        if($this->shipping_price == 0){
            self::instance('shopping')->setShippingCountryWithPrice($this->shipping_country_id, $this->shipping_is_for);
        }
    }

    public static function getTaxRate()
    {
        return config('cart.tax');
    }
    
    public function putYardExtra($key, $value){

        $content = $this->getYContent();
        $content->put($key, $value);
        $this->ysession->put($this->yinstance, $content);
    }

    public function getYardExtra($key){
        $content = $this->getYContent();
        if ($content->has($key)){
            return $content->get($key);
        }
        return false;
    }

    public function getShippingCountryName(){

        $shippingCountry = ShippingCountry::find($this->shipping_country_id);
        if($shippingCountry && $shippingCountry->country){
            return $shippingCountry->country->getLocated();
        }
        return "";
    }
    public function getShippingCountryCountryId()
    {
        $shippingCountry = ShippingCountry::find($this->shipping_country_id);
        if($shippingCountry && $shippingCountry->country){
            return $shippingCountry->country->id;
        }
        return 1; //default DE
    }

    public function getShippingCountryId()
    {
        return $this->shipping_country_id;
    }


    public function getYContent()
    {
        if (is_null($this->ysession->get($this->yinstance))) {
            return new Collection([]);
        }
        return $this->ysession->get($this->yinstance);
    }


    public function reCalculate(){
        $this->calculateShippingPrice();
    }
    /* * ***** */

    public function calculateMargins(){
        //get user monthy amount
        $monthy_amount = UserMarign::getMontlyAmount($this->user);

        $content = $this->getContent();

        /*single + partner Commissions */
        //get price net commission, add amount, partner (total) from products is single_commission
        $yard_commission = new Commission();
        $yard_commission = $content->reduce(function ($yard_commission, CartItem $cartItem) {
            $price_net = $cartItem->price / ((100 + $cartItem->taxRate) / 100);
            if($cartItem->options->single_commission){
                $value_commission =  $price_net / 100 * $cartItem->options->value_commission;
                $partner_commission =  $price_net / 100 * $cartItem->options->partner_commission;

                $price_net_commission = ($cartItem->qty * $price_net) - ($cartItem->qty * $value_commission);
                $yard_commission->single_price_net += ($cartItem->qty * $price_net);
                $yard_commission->single_value_commission += ($cartItem->qty * $value_commission);
                if($this->user->user_level->partner_provision){
                    $yard_commission->single_partner_commission += ($cartItem->qty * $partner_commission);
                }
                $yard_commission->single_price_net_commission += $price_net_commission;
                //
                if($cartItem->options->amount_commission){
                    $yard_commission->single_amount_commission += $price_net_commission;
                }
            }else{
                $yard_commission->price_net = $yard_commission->price_net + ($cartItem->qty * $price_net);
            }
            return $yard_commission;
        }, $yard_commission);

        $this->yard_commission = $yard_commission;
        $this->putYardExtra('yard_commission', $this->yard_commission);

        //dump($monthy_amount);
        //add to $monthy_amount für max level margins
        $start_monthy_amount = $monthy_amount + $yard_commission->single_amount_commission;
        $end_monthy_amount = $start_monthy_amount + $yard_commission->price_net;

        //dump($start_monthy_amount);
        //dump($end_monthy_amount);

        $margin = new Margin();
        $rest_amount = 0;
        $range = 0;
        $price_net = $yard_commission->price_net;
        if($yard_commission->price_net > 0 && $this->user && $this->user->user_level && $this->user->user_level->user_level_margins){
            foreach ($this->user->user_level->user_level_margins_re as $user_level_margin) {
                //total split?
                if ($end_monthy_amount >= $user_level_margin->price_from) {
                    if(!isset($balance)){
                        if($start_monthy_amount >= $user_level_margin->price_from){
                            $balance = $yard_commission->price_net;
                            $rest_amount = 0;
                        }else{
                            $balance = $end_monthy_amount - $user_level_margin->price_from;
                            $rest_amount = $end_monthy_amount - ($start_monthy_amount + $balance);
                        }
                    }else{
                        $range = $last_limit-$user_level_margin->price_from;
                        if($rest_amount >= $range){
                            $balance = $range;
                        }else{
                            $balance = $rest_amount;
                        }
                        $rest_amount = $rest_amount-$balance;
                    }
                    $last_limit = $user_level_margin->price_from;
                    if($balance != 0){
                        $margin->add($user_level_margin->price_from, [
                            'price_net' => $price_net,
                            'range' => $range,
                            'rest_amount' => $rest_amount,
                            'balance' => $balance,
                            'trading_margin'=> $user_level_margin->trading_margin,
                            'commission'=> $user_level_margin->commission,
                        ]);
                    }
                }
            }
        }


        $margin->setCommission($this->yard_commission);
        $margin->calculate();

        $this->yard_margin = $margin;
        $this->putYardExtra('yard_margin', $this->yard_margin);

        //dump($margin);
    }

    public function getYardMargin(){
        return $this->yard_margin;
    }
    public function getYardCommission(){
        return $this->yard_commission;
    }
    /* * ***** */
    public function setShippingCountryWithPrice($shipping_country_id, $shipping_is_for = 'ot')
    {
        $this->shipping_country_id = $shipping_country_id;
        $this->putYardExtra('shipping_country_id', $shipping_country_id);

        $this->shipping_is_for = $shipping_is_for;
        $this->putYardExtra('shipping_is_for', $shipping_is_for);

        $this->calculateShippingPrice();

    }

    public function setShoppingUser($user, $payment_credit = false){
        $this->user = $user;
        $this->payment_credit = $payment_credit;
        $this->putYardExtra('user', $user);
        $this->putYardExtra('payment_credit', $payment_credit);

    }

    public function getPaymentCredit(){
        if($this->payment_credit && $this->user->payment_credit > 0){
            return $this->user->payment_credit;
        }
        return 0;
    }

    private function calculateShippingPrice(){

        $shippingCountry = ShippingCountry::find($this->shipping_country_id);
        if(!$shippingCountry){
            return;
        }
        $shipping = $shippingCountry->shipping;
        if($this->weight() == 0){
            $shipping_price = $shipping->shipping_prices->first();
            $shipping_price->price = 0;
            $shipping_price->price_comp = 0;
        }else{
            //first by price
            $shipping_price = $this->shippingPriceBySubTotal($shipping->shipping_prices, $this->subtotal(2, '.', ''));
            //sec by weight
            if(!$shipping_price){
                $shipping_price = $this->shippingPriceByWeight($shipping->shipping_prices, $this->weight());
            }
            //default
            if(!$shipping_price){

                $shipping_price = $shipping->shipping_prices->first();
            }
        }
        if($shipping_price){
            $price = $shipping_price->price;
            /*$this->num_comp = 0;
            if($this->shipping_is_for === 'me'){
                $price = $shipping_price->price_comp;
                $this->num_comp = $shipping_price->num_comp;

            }*/
            $this->shipping_price = $price;
            $this->shipping_tax_rate = $shipping_price->tax_rate;
            $this->shipping_price_net = round($price / ((100+$shipping_price->tax_rate) / 100), 2);
            $this->shipping_tax = round($price / (100+$shipping_price->tax_rate) * 100, 2);

            //$this->putYardExtra('num_comp', $this->num_comp);
            $this->putYardExtra('shipping_price', $this->shipping_price);
            $this->putYardExtra('shipping_tax_rate', $this->shipping_tax_rate);
            $this->putYardExtra('shipping_tax', $this->shipping_tax);
            $this->putYardExtra('shipping_price_net', $this->shipping_price_net);
        }
    }

    private function shippingPriceBySubTotal($prices, $total){
        foreach ($prices as $price){
            if($price->total_from > 0 && $price->total_to > 0){
                if($total >= $price->total_from && $total <= $price->total_to){
                    return $price;
                }
            }
        }
        return false;
    }
    private function shippingPriceByWeight($prices, $weight){
        foreach ($prices as $price){
            if($price->weight_from > 0 && $price->weight_to > 0){
                if($weight >= $price->weight_from && $weight <= $price->weight_to){
                    return $price;
                }
            }
        }
        return false;
    }

    /**
     * @param null $decimals
     * @param null $decimalPoint
     * @param null $thousandSeperator
     * @return string
     */
    public function shipping($decimals = null, $decimalPoint = null, $thousandSeperator = null)
    {
        return $this->numberFormat($this->shipping_price, $decimals, $decimalPoint, $thousandSeperator);
    }

    public function shippingNet($decimals = null, $decimalPoint = null, $thousandSeperator = null)
    {
        return $this->numberFormat($this->shipping_price_net, $decimals, $decimalPoint, $thousandSeperator);
    }
    //
    private function shippingTax($decimals = null, $decimalPoint = null, $thousandSeperator = null)
    {
        return $this->numberFormat($this->shipping_tax, $decimals, $decimalPoint, $thousandSeperator);

    }

   /* private function subShipping($decimals = null, $decimalPoint = null, $thousandSeperator = null){
        $subShipping = $this->shipping_price_net
        return $this->numberFormat($subShipping, $decimals, $decimalPoint, $thousandSeperator);
    }*/
    //netto
    public function subtotalWithShipping($decimals = null, $decimalPoint = null, $thousandSeperator = null)
    {
        $subtotal = (float) $this->shipping_price_net + $this->subtotal(2, '.', '');
        return $this->numberFormat($subtotal, $decimals, $decimalPoint, $thousandSeperator);
    }


    public function taxWithShipping($decimals = null, $decimalPoint = null, $thousandSeperator = null)
    {

       // $total = $this->totalWithShipping(2, '.', '');
       // $totalTax = (float) $this->tax(2, '.', '') + $this->shipping_tax;
        $subtotal_shipping = $this->subtotalWithShipping(2, '.', '');
        return $this->numberFormat(($subtotal_shipping / 100 * $this->global_tax_rate), $decimals, $decimalPoint, $thousandSeperator);
    }


    public function totalWithShipping($decimals = null, $decimalPoint = null, $thousandSeperator = null)
    {
        //$total = (float) ($this->total(2, '.', '')) + $this->shipping_price;
        $subtotal_shipping = $this->subtotalWithShipping(2, '.', '');
        $tax = $subtotal_shipping / 100 * $this->global_tax_rate;
        $total = ($subtotal_shipping + $tax);
        if($PaymentCredit = $this->getPaymentCredit()){
            $total = $total - $PaymentCredit;
            if($total < 0){
                $total = 0;
            }
        }
        return $this->numberFormat($total, $decimals, $decimalPoint, $thousandSeperator);
    }


    public function totalWithShippingWithoutCredit($decimals = null, $decimalPoint = null, $thousandSeperator = null)
    {
        //$total = (float) ($this->total(2, '.', '')) + $this->shipping_price;
        $subtotal_shipping = $this->subtotalWithShipping(2, '.', '');
        $tax = $subtotal_shipping / 100 * $this->global_tax_rate;
        $total = ($subtotal_shipping + $tax);
        return $this->numberFormat($total, $decimals, $decimalPoint, $thousandSeperator);
    }

    public function totalfromCredit($decimals = null, $decimalPoint = null, $thousandSeperator = null)
    {
        $rest = 0;
        if ($PaymentCredit = $this->getPaymentCredit()) {
            $subtotal_shipping= $this->subtotalWithShipping(2, '.', '');
            $tax = $subtotal_shipping/ 100 * $this->global_tax_rate;
            $total = ($subtotal_shipping+ $tax);
            $rest = $PaymentCredit - $total;
            if($rest < 0){
                $rest = $PaymentCredit;
            }else{
                $rest = $PaymentCredit - $rest;
            }
        }
        return $this->numberFormat($rest, $decimals, $decimalPoint, $thousandSeperator);
    }

    /**
     * Get the total price of the items in the cart.
     *
     * @param int    $decimals
     * @param string $decimalPoint
     * @param string $thousandSeperator
     * @return string
     */
    public function weight($decimals = null, $decimalPoint = null, $thousandSeperator = null)
    {
        $content = $this->getContent();
        $total = $content->reduce(function ($total, CartItem $cartItem) {
            return $total + ($cartItem->options->weight ? ($cartItem->options->weight*$cartItem->qty) : 0);
        }, 0);
        return $total;
    }

    public function points()
    {
        $content = $this->getContent();
        $total = $content->reduce(function ($total, CartItem $cartItem) {
            return $total + ($cartItem->options->points ? ($cartItem->options->points * $cartItem->qty) : 0);
        }, 0);
        return $total;
    }

    public function compCount()
    {
        $content = $this->getContent();

        $count = parent::count();
        $comp_count = $content->reduce(function ($comp_count, CartItem $cartItem) {
            return $cartItem->options->comp ? $comp_count + 1 : $comp_count;
        }, 0);
        return $count-$comp_count;
    }

    /**
     * Get the total price of the items in the cart.
     *
     * @param int    $decimals
     * @param string $decimalPoint
     * @param string $thousandSeperator
     * @return string
     */
    public function total($decimals = null, $decimalPoint = null, $thousandSeperator = null, $withFees = false)
    {
        $content = $this->getContent();
        $total = $content->reduce(function ($total, CartItem $cartItem) {
            return $total + ($cartItem->qty * $cartItem->price);
        }, 0);

        if ($withFees === true) {
            $fees = $this->feeTotal(null, null, null, true);
            $total = $total + $fees;
        }
        return $this->numberFormat($total, $decimals, $decimalPoint, $thousandSeperator);
    }



    /**
     * Get the total tax of the items in the cart.
     *
     * @param int    $decimals
     * @param string $decimalPoint
     * @param string $thousandSeperator
     * @return float
     */
    public function tax($decimals = null, $decimalPoint = null, $thousandSeperator = null, $withFees = false)
    {
        $content = $this->getContent();

        $tax = $content->reduce(function ($tax, CartItem $cartItem) {
            $priceTax = $cartItem->price / (100 + $cartItem->taxRate) * $cartItem->taxRate;
            return $tax + ($cartItem->qty * $priceTax);
        }, 0);

        if ($withFees === true) {
            $fees = $this->feeTax();
            $tax = $tax + floatval($fees);
        }

        return $this->numberFormat($tax, $decimals, $decimalPoint, $thousandSeperator);
    }


    /**
     * Get the subtotal (total - tax) of the items in the cart.
     *
     * @param int    $decimals
     * @param string $decimalPoint
     * @param string $thousandSeperator
     * @return float
     */
    public function subtotal($decimals = null, $decimalPoint = null, $thousandSeperator = null, $discount = true)
    {
        $content = $this->getContent();

        $subTotal = $content->reduce(function ($subTotal, CartItem $cartItem) {
            $price_net = $cartItem->price / ((100 + $cartItem->taxRate) / 100);
            return $subTotal + ($cartItem->qty * $price_net);
        }, 0);

        if($discount && $this->yard_margin){
            $subTotal -= $this->yard_margin->net_discount;
        }
        return $this->numberFormat($subTotal, $decimals, $decimalPoint, $thousandSeperator);
    }


    public function getCartItemByProduct($product_id, $set_price='with'){
        if($product = Product::find($product_id)) {
            $image = "";
            if ($product->images->count()) {
                $image = $product->images->first()->slug;
            }
            $price = $product->price;
            if($set_price === 'with'){
                $price = $product->getPriceWith(false, true);
            }
            $cartItem = $this->getCartItem($product->id, $product->getLang('name'), 1, $price,
                [
                    'image' => $image,
                    'slug' => $product->slug,
                    'weight' => $product->weight,
                    'single_commission' => $product->single_commission,
                    'amount_commission' => $product->amount_commission,
                    'value_commission' => $product->value_commission,
                    'partner_commission' => $product->partner_commission,
                ]
            );
            $content = $this->getContent();

            if ($content->has($cartItem->rowId)){
                return $content->get($cartItem->rowId);
            }
            return $cartItem;
        }
        return null;

    }


    public function getCartItem($id, $name = null, $qty = null, $price = null, array $options = []){
        if ($id instanceof Buyable) {
            $cartItem = CartItem::fromBuyable($id, $qty ?: []);
        } elseif (is_array($id)) {
            $cartItem = CartItem::fromArray($id);
        } else {
            $cartItem = CartItem::fromAttributes($id, $name, $price, $options);
        }
        return $cartItem;
    }

    public function destroy()
    {
        $this->ysession->remove($this->yinstance);
        parent::destroy();

    }

    public function rowPriceNet(CartItem $row, $decimals = null, $decimalPoint = null, $thousandSeperator = null){
        $price = round($row->price / ((100 + $row->taxRate) /100), 4);
        return $this->numberFormat($price, $decimals, $decimalPoint, $thousandSeperator);
    }

    public function rowSubtotalNet(CartItem $row, $decimals = null, $decimalPoint = null, $thousandSeperator = null){
        $price = round($row->price / ((100 + $row->taxRate) /100), 4);
        return $this->numberFormat(($price * $row->qty), $decimals, $decimalPoint, $thousandSeperator);
    }

    public function getNumComp(){
        return 0; //$this->num_comp;
    }

    public function getCompProductBy($comp, $product_id=false){
        foreach ($this->content() as $row) {
            if($row->options->comp == $comp) {
                return $row->options->product_id;
            }
        }
        return false;
    }

    public function getContentByOrder(){
        $ret = [];
        $comp = [];
        foreach ($this->content() as  $row) {
            if($row->options->comp){
                $comp[100+$row->options->comp] = $row;
            }else{
                $ret[] = $row;
            }
        }
        ksort($comp);
        $ret = array_merge($ret, $comp);
        return $ret;
    }

    /**
     * Get the Formated number
     *
     * @param $value
     * @param $decimals
     * @param $decimalPoint
     * @param $thousandSeperator
     * @return string
     */
    protected function numberFormat($value, $decimals, $decimalPoint, $thousandSeperator)
    {
        if(is_null($decimals)){
            $decimals = is_null(config('cart.format.decimals')) ? 2 : config('cart.format.decimals');
        }
        if(is_null($decimalPoint)){
            $decimalPoint = is_null(config('cart.format.decimal_point')) ? '.' : config('cart.format.decimal_point');
        }
        if(is_null($thousandSeperator)){
            $thousandSeperator = is_null(config('cart.format.thousand_seperator')) ? ',' : config('cart.format.thousand_seperator');
        }

        return number_format($value, $decimals, $decimalPoint, $thousandSeperator);
    }
}
