<?php

namespace Modules\TpsTransfer\Http\Controllers;

use App\Brands;
use App\BusinessLocation;
use App\Category;
use App\Product;
use App\PurchaseLine;
use App\SellingPriceGroup;
use App\TaxRate;
use App\Transaction;
use App\Unit;
use App\Utils\ModuleUtil;
use App\Utils\ProductUtil;
use App\Utils\TransactionUtil;
use App\Utils\Util;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
use Modules\TpsTransfer\Entities\NstPrepareStock;
use Modules\TpsTransfer\Entities\NstShipping;
use Modules\TpsTransfer\Entities\NstTransaction;
use Modules\TpsTransfer\Entities\Services\CategoryService;
use Modules\TpsTransfer\Entities\Services\LocationsService;
use Modules\TpsTransfer\Entities\Services\TransactionService;
use Yajra\DataTables\Facades\DataTables;

class TpsReceiveStockController extends Controller
{
    protected $moduleUtil;
    protected $locationsService;
    protected $categoryService;
    protected $transactionService;
    protected $productUtil;
    protected $util;
    protected $transactionUtil;

    public function __construct(TransactionService $transactionService, TransactionUtil $transactionUtil, ProductUtil $productUtil, ModuleUtil $moduleUtil, LocationsService $locationsService, CategoryService $categoryService, Util $util)
    {
        $this->transactionService = $transactionService;
        $this->categoryService = $categoryService;
        $this->locationsService = $locationsService;
        $this->productUtil = $productUtil;
        $this->moduleUtil = $moduleUtil;
        $this->util = $util;
        $this->transactionUtil = $transactionUtil;
    }

    /**
     * Receive stock index.
     * @return Response
     */
    public function index()
    {
        if (!auth()->user()->can('tpsTransfer.access_received_stock_transfer')) {
            abort(403, 'Unauthorized action.');
        }

        $businessId = request()->session()->get('user.business_id');
        $is_woocommerce = $this->moduleUtil->isModuleInstalled('Woocommerce');

        $categories = Category::forDropdown($businessId, 'product');
        $brands = Brands::forDropdown($businessId);
        $units = Unit::forDropdown($businessId);
        $tax_dropdown = TaxRate::forBusinessDropdown($businessId, false);
        $taxes = $tax_dropdown['tax_rates'];

        $fromBusinessLocations = $this->locationsService->accessLocationsForUserforDropdown($businessId, 'from', false, true, 'receive');
        $toBusinessLocations = $this->locationsService->accessLocationsForUserforDropdown($businessId, 'to', false, true, 'receive');


        return view('tpsTransfer::receive')->with(compact(
            'categories',
            'brands',
            'units',
            'taxes',
            'fromBusinessLocations',
            'toBusinessLocations',
            'is_woocommerce',
        ));
    }

    /**
     * Receive stock table.
     * @return Response
     */
    public function table()
    {
        if (!auth()->user()->can('tpsTransfer.access_received_stock_transfer')) {
            abort(403, 'Unauthorized action.');
        }

        $businessId = request()->session()->get('user.business_id');
        //Filter by location
        $from_location_id = request()->get('from_location_id', null);
        $to_location_id = request()->get('to_location_id', null);
        $shipping_id = request()->get('shipping_id', null);

        $query = NstPrepareStock::query()
            ->where('nst_prepare_products.status', 'receive')
            ->where('nst_prepare_products.business_id', $businessId)
            ->leftJoin('users','users.id','=','nst_prepare_products.created_by')
            ->leftJoin('business_locations as from_business_locations','from_business_locations.id','=','nst_prepare_products.from_location_id')
            ->leftJoin('products','products.id','=','nst_prepare_products.product_id')
            ->leftJoin('categories as c1', 'products.category_id', '=', 'c1.id')
            ->leftJoin('categories as c2', 'products.sub_category_id', '=', 'c2.id')
            ->leftJoin('brands', 'products.brand_id', '=', 'brands.id')
            ->join('units', 'products.unit_id', '=', 'units.id')
            ->leftJoin('tax_rates', 'products.tax', '=', 'tax_rates.id')
            ->leftJoin('variations','variations.id','=','nst_prepare_products.variation_id')
            ->join('product_variations as pv', 'pv.id', '=', 'variations.product_variation_id')
            ->leftJoin('purchase_lines','purchase_lines.id','=','nst_prepare_products.purchase_lines_id')
            ->leftJoin('business_locations as to_business_locations','to_business_locations.id','=','nst_prepare_products.to_location_id')
            ->whereNull('nst_prepare_products.deleted_at')

            ->leftJoin('variation_location_details as to_vld',function($join) {
                $join->on('variations.id','=','to_vld.variation_id')
                    ->whereRaw('to_vld.location_id = to_business_locations.id');
            })
            ->leftJoin('variation_location_details as from_vld',function($join) {
                $join->on('variations.id','=','from_vld.variation_id')
                    ->whereRaw('from_vld.location_id = from_business_locations.id');
            });

        if (!empty($shipping_id) && $shipping_id != 'none') {
            $query->where('nst_prepare_products.nst_shipping_id', '=', $shipping_id);
        }elseif($shipping_id == 'none'){
            $query->whereNull('nst_prepare_products.nst_shipping_id');
        }

        if (!empty($from_location_id) && $from_location_id != 'none') {
            $query->where('nst_prepare_products.from_location_id', '=', $from_location_id);
        }

        if (!empty($to_location_id) && $to_location_id != 'none') {
            $query->where('nst_prepare_products.to_location_id', '=', $to_location_id);
        }else{
	        $query->whereRaw( '1 = 2' );
        }

        $query = $query->select(
            'nst_prepare_products.id',
            'nst_prepare_products.transaction_date',
            'nst_prepare_products.ref_no',
            'nst_prepare_products.is_shipping_calculated',
            'users.id as user_id',
            'users.surname as user_surname',
            'from_location_id',
            'from_business_locations.name as from_location_name',
            'products.id as product_id',
            'products.name as product_name',
            'products.sku',
            'products.type',
            'c1.name as category',
            'c2.name as sub_category',
            'units.actual_name as unit',
            'brands.name as brand',
            'tax_rates.name as tax',
            'variations.id as variation_id',
            'variations.name as variation_name',
            'pv.name as product_variation_name',
            'purchase_lines.id as purchase_line_id',
            'purchase_lines.lot_number',
            'purchase_lines.purchase_price',
            'purchase_lines.purchase_price_inc_tax',
            'purchase_lines.quantity as purchase_lines_quantity',
            'nst_prepare_products.transfer_quantity',
            'to_location_id',
            'to_business_locations.name as to_location_name',
            'nst_prepare_products.is_important',
            'nst_prepare_products.note',
            'nst_prepare_products.value_per_unit',
            'nst_prepare_products.created_at',
            DB::raw('to_vld.qty_available as to_qty_available'),
            DB::raw('from_vld.qty_available as from_qty_available')
        );
        if (!request()->get('order')) {
            $query
                ->orderByRaw('ISNULL(nst_prepare_products.note), nst_prepare_products.note ASC')
                ->orderBy('nst_prepare_products.is_important', 'DESC')
                ->orderBy('to_qty_available', 'ASC')
                ->orderBy('products.id', 'ASC');
        }

        $type = request()->get('type', null);
        if (!empty($type)) {
            $query->where('products.type', $type);
        }

        $category_id = request()->get('category_id', null);
        if (!empty($category_id)) {
            $query->where('products.category_id', $category_id);
        }

        $categoryId = request()->get('category_id', null);
        $subCategoryId = request()->get('sub_category_id', null);

        if ($subCategoryId && $subCategoryId != 'all') {
            if ($subCategoryId) {
                $query->where('products.sub_category_id', $subCategoryId);
            }
            if ($categoryId) {
                $query->where('products.category_id', $categoryId);
            }
        }

        $brand_id = request()->get('brand_id', null);
        if (!empty($brand_id)) {
            $query->where('products.brand_id', $brand_id);
        }

        return Datatables::of($query)
	        ->editColumn('created_at', '{{@format_datetime($created_at)}}')
	        ->addColumn('data-href', function ($row){
                return action('\\App\Http\Controllers\ProductController@view', [$row->product_id]);
            })
            ->addColumn('row_checkbox', function ($row){
                return "<input type='checkbox' class='product-row-input' value='1' data-id='{$row->id}' data-name='accepted'> ";
            })
            ->addColumn('value_per_unit', function ($row){
                return round($row->value_per_unit, 2);
            })
            ->editColumn('from_location_name', function ($row){
                $from_qty_available = $row->from_qty_available;
                // if($row->purchase_line_id){
                //     $from_qty_available = $row->purchase_lines_quantity;
                // }

                return $row->from_location_name .' ('.intval($from_qty_available).')';
            })
            ->editColumn('to_location_name', function ($row){
                return $row->to_location_name .' ('.intval($row->to_qty_available).')';
            })
            ->addColumn('transfer_quantity_input', function ($row){
                $from_qty_available = $row->from_qty_available;
                if($row->purchase_line_id){
                    $from_qty_available = $row->purchase_lines_quantity;
                }

                $html = "<input id='tQty_{$row->id}' data-id='{$row->id}' data-name='transfer_quantity' class='form-control product-row-input transfer_quantity row-select' style='width:100%' type='number' min='1' max='".intval($from_qty_available)."' value='".intval($row->transfer_quantity)."'>";

                return $html;
            })
            ->addColumn('actions', function ($row){
                $class = is_null($row->note) ? '' : 'btn-success';
                $html = "
                <div class='btn-group'>
                    <div class='dropdown actions-list'>
                        <button class='btn btn-primary {$class} dropdown-toggle' type='button' data-toggle='dropdown' id='actions_btn_{$row->id}'>".__('messages.actions')."</button>
                        <div class='dropdown-menu dropdown-menu-right'>
                        <li><a target='_blank' href='".action('\Modules\TpsTransfer\Http\Controllers\TpsLabelsController@show', ['product_id' => $row->product_id, 'qty' => intval($row->transfer_quantity), 'purchase_line_id' => intval($row->purchase_line_id)]) ."' data-toggle='tooltip' title='" . __('lang_v1.label_help') . "'><i class='fa fa-barcode'></i> " . __('barcode.labels') . "</a></li>
                        <li><a data-toggle='modal' data-target='#add_notes' id='note_{$row->id}' data-id='{$row->id}' class='btn dropdown-item add_notes' type='button'>".__('tpsTransfer::lang.note')."</a></li>";
                if (auth()->user()->can('tpsTransfer.can_delete_nst_row')) {
                    $html .= "<li><a data-toggle='modal' data-target='#delete_confirm' id='delete_{$row->id}' data-id='{$row->id}' class='btn dropdown-item delete_row_button' type='button'>".__('messages.delete')."</a></li>";
                }
                $html .= "
                        </div>
                    </div>
                </div>";
                return $html;
            })
            ->editColumn('category', '{{$category}} @if(!empty($sub_category))<br/> -- {{$sub_category}}@endif')
            ->editColumn('product', function ($row) {
                $name = $row->product;
                if ($row->variation_name && $row->variation_name != 'DUMMY') {
                    $name .= ' (' . $row->variation_name . ')';
                }
                return $name;
            })
            ->editColumn('image', function ($row) {
                return '<div style="display: flex;"><img src="' . $row->image_url . '" alt="Product image" class="product-thumbnail-small"></div>';
            })
            ->editColumn('purchase_price', function ($row) use ($businessId) {
                $shipping = ($row->is_shipping_calculated == 1) ? " <i class='fa fa-shipping-fast'>" : null;

                if($row->purchase_price){
                    return $row->purchase_price.$shipping;
                }

                //add lot select list
                $avgPurchasePrice = 0;
                $count = 0;

                $lotNumbers = $this->transactionService->getLotDataFromVariation($row->variation_id, $businessId, $row->from_location_id, true);

                if($lotNumbers){
                    foreach ($lotNumbers as $lotNumber) {
                        $avgPurchasePrice += $lotNumber->purchase_price * $lotNumber->qty_available;
                        $count += $lotNumber->qty_available;
                    }
                    if($count){
                        $avgPurchasePrice = $avgPurchasePrice / $count;
                    }
                }
                return round($avgPurchasePrice, 2).$shipping;
            })
            ->editColumn('type', '@lang("lang_v1." . $type)')
            ->editColumn('transfer_quantity', function ($row) {
                return intval($row->transfer_quantity);
            })
            ->filterColumn('products.sku', function ($query, $keyword) {
                $query->whereHas('product.variations', function($q) use($keyword){
                    $q->where('sub_sku', 'like', "%{$keyword}%");
                })
                    ->orWhere('products.sku', 'like', "%{$keyword}%");
            })
            ->setRowAttr([
                'data-href' => function ($row) {
                    return  action('\Modules\TpsTransfer\Http\Controllers\TpsController@showInfoModal', [$row->id]) ;
                },
                'class' => function ($row){
                    if($row->to_qty_available == 0){
                        return 'bg-red';
                    }
                    return '';
                }
            ])
            ->rawColumns(['actions', 'row_checkbox', 'product', 'selling_price', 'purchase_price', 'category', 'transfer_quantity_input'])
            ->make(true);
    }

    public function update(Request $request){
        foreach($request->receiveDoneTpsData as $id => $data){
            unset($data['accepted']);
            $data['status'] = 'receive';

            $nstReceiveStock = NstPrepareStock::findOrFail($id);
            $nstReceiveStockBefore = $nstReceiveStock->replicate();
            $nstReceiveStock->update($data);

            if(!$nstReceiveStock){
                return response()->json(['status' => 'error']);
            }
            $this->util->activityLog($nstReceiveStock, 'edited', $nstReceiveStockBefore);
        }
        return response()->json(['status' => 'success', 'redirect' => '/tps-product/receive-stock']);
    }

    public function receive(Request $request){
        $businessId = request()->session()->get('user.business_id');
        $createdBy = auth()->user()->id;
        $from_location_id = request()->get('from_location_id', null);
        $to_location_id = request()->get('to_location_id', null);

        $shipping = NstShipping::find(request()->get('shipping_id', null));
        $input_data['shipping_charges'] = 0;

        if($shipping){
            if($shipping->shipping_method == 'as_charge'){
                $input_data['shipping_charges'] = $shipping->shipping_charges;
            }
            $input_data['additional_notes'] = "shipping method : $shipping->shipping_method - shipping charges : $shipping->shipping_charges";
        }else{
            $new_shipping = NstShipping::create([
                'business_id' => $businessId,
                'shipping_details' => 'Auto created..',
                'shipping_method' => 'as_charge',
                'shipping_charges' => 0,
                'created_by' => $createdBy,
	        ]);

	        $new_shipping->ref_num = 'NST' . \Carbon::now()->year . '-' . $new_shipping->id;
	        $new_shipping->save();
        }

        DB::beginTransaction();

        // Start of - Insert data in core tables
        // Start of - Create Sell Transfer transaction
        $input_data['type'] = 'sell_transfer';
        $input_data['location_id'] = $from_location_id;
        $input_data['business_id'] = $businessId;
        $input_data['created_by'] = $request->session()->get('user.id');
        $input_data['payment_status'] = 'paid';
        $input_data['status'] = 'final';

        //Update reference count
        $ref_count = $this->productUtil->setAndGetReferenceCount('stock_transfer');
        //Generate reference number
        $input_data['ref_no'] = $this->productUtil->generateReferenceNumber('stock_transfer', $ref_count);

        $temp_total = 0;
        $sell_lines = [];
        $purchase_lines = [];
        $nstPrepareIds = [];

        foreach(array_chunk(request()->get('receiveDoneTpsData', null), 500, true) as $chunkedData){
            foreach($chunkedData as $id => $data){
                $nstPrepareIds[] = $id;
                $nstReceiveStock = NstPrepareStock::findOrFail($id);

                // Add shipping data if there is no shipping
                if(isset($new_shipping)){
                    $unitPriceAfterShipping = 0;
                    $unitPrices = $this->transactionService->getPurchasePriceFromVariationByQuantity($nstReceiveStock->transfer_quantity, $nstReceiveStock->variation_id, $businessId, $nstReceiveStock->from_location_id);

                    foreach ($unitPrices as $unitPrice){
                        $unitPriceAfterShipping += floatval($unitPrice['purchase_price']) * intval($unitPrice['quantity']);
                    }

                    $unitPriceAfterShipping = $unitPriceAfterShipping / intval($nstReceiveStock->transfer_quantity);

                    $data['nst_shipping_id'] = $new_shipping->id;
                    $data['unit_price_after_shipping'] = $nstReceiveStock->unit_price_after_shipping = $unitPriceAfterShipping;
                }

                // there is missing QTY between shipping and receiving
                if(\Arr::get($data, 'transfer_quantity') < $nstReceiveStock->transfer_quantity){
                    $data['transfer_quantity'] = $nstReceiveStock->transfer_quantity - \Arr::get($data, 'transfer_quantity');
                }
                // can`t receive more than sent QTY
                elseif(\Arr::get($data, 'transfer_quantity') > $nstReceiveStock->transfer_quantity){
                    DB::rollBack();
                    return response()->json(['status' => 'error', 'message' => 'The location must be same selected locations', ]);
                }else{
                    $data['status'] = 'complete';
                }

                if(!isset($input_data['transaction_date'])){
                    $input_data['transaction_date'] = $nstReceiveStock->transaction_date;
                }

                $sell_line_arr = [
                    'product_id' => $nstReceiveStock->product_id,
                    'variation_id' => $nstReceiveStock->variation_id,
                    'quantity' => $this->productUtil->num_uf(\Arr::get($data, 'transfer_quantity')),
                    'item_tax' => 0,
                    'tax_id' => null];

                if (!empty($nstReceiveStock->product->unit_id)) {
                    $sell_line_arr['product_unit_id'] = $nstReceiveStock->product->unit_id;
                }
                if (!empty($nstReceiveStock->product->sub_unit_id)) {
                    $sell_line_arr['sub_unit_id'] = $nstReceiveStock->product->sub_unit_id;
                }

                $purchase_line_arr = $sell_line_arr;

                $sell_line_arr['unit_price'] = $this->productUtil->num_uf($nstReceiveStock->unit_price_after_shipping);
                $sell_line_arr['unit_price_inc_tax'] = $sell_line_arr['unit_price'];

                $purchase_line_arr['purchase_price'] = $sell_line_arr['unit_price'];
                $purchase_line_arr['purchase_price_inc_tax'] = $sell_line_arr['unit_price'];

                if (!empty($nstReceiveStock->purchase_lines_id)) {
                    //Add lot_no_line_id to sell line
                    $sell_line_arr['lot_no_line_id'] = $nstReceiveStock->purchase_lines_id;

                    //Copy lot number and expiry date to purchase line
                    $lot_details = PurchaseLine::find($nstReceiveStock->purchase_lines_id);
                    $additionalLotNumber = $nstReceiveStock->is_shipping_calculated ? 'TR-' : null;
                    $purchase_line_arr['lot_number'] = $additionalLotNumber.$lot_details->lot_number;
                    $purchase_line_arr['mfg_date'] = $lot_details->mfg_date;
                    $purchase_line_arr['exp_date'] = $lot_details->exp_date;
                }


                if (isset($purchase_line_arr['sub_unit_id']) && $purchase_line_arr['sub_unit_id'] == $purchase_line_arr['product_unit_id']) {
                    unset($purchase_line_arr['sub_unit_id']);
                }
                unset($purchase_line_arr['product_unit_id']);

                $sell_lines[] = $sell_line_arr;
                $purchase_lines[] = $purchase_line_arr;

                unset($data['accepted']);
                $nstReceiveStockBefore = $nstReceiveStock->replicate();
                $nstReceiveStock->update($data);
                $temp_total += (intval(\Arr::get($data, 'transfer_quantity')) * floatval($nstReceiveStock->unit_price_after_shipping));
                $allReceived[] = $nstReceiveStock;

                if(!$nstReceiveStock){
                    DB::rollBack();
                    return response()->json(['status' => 'error']);
                }
                $this->util->activityLog($nstReceiveStock, 'edited', $nstReceiveStockBefore);

                //Decrease product stock from sell location
                //And increase product stock at purchase location
                $decrease_qty = $this->productUtil
                    ->num_uf(intval(\Arr::get($data, 'transfer_quantity')));

                $this->productUtil->decreaseProductQuantity(
                    $nstReceiveStock->product_id,
                    $nstReceiveStock->variation_id,
                    $from_location_id,
                    $decrease_qty
                );

                $this->productUtil->updateProductQuantity(
                    $to_location_id,
                    $nstReceiveStock->product_id,
                    $nstReceiveStock->variation_id,
                    $decrease_qty,
                    0,
                    null,
                    false
                );
            }
        }
        $input_data['final_total'] = $temp_total;

        //Create Sell Transfer transaction
        $sell_transfer = Transaction::create($input_data);

        //update all nstPrepareRows with selling transaction id
        NstPrepareStock::whereIn('id',$nstPrepareIds)->update(['transaction_id' => $sell_transfer->id]);

        //Create Purchase Transfer at transfer location
        $input_data['type'] = 'purchase_transfer';
        $input_data['location_id'] = $to_location_id;
        $input_data['transfer_parent_id'] = $sell_transfer->id;
        $input_data['status'] = 'received';

        $purchase_transfer = Transaction::create($input_data);

        //Sell Product from first location
        if (!empty($sell_lines)) {
            $this->transactionUtil->createOrUpdateSellLines($sell_transfer, $sell_lines, $input_data['location_id'], false, null, [], false);
        }

        //Purchase product in second location
        if (!empty($purchase_lines)) {
            $purchase_transfer->purchase_lines()->createMany($purchase_lines);
        }

        //Decrease product stock from sell location
        //And increase product stock at purchase location

        //Adjust stock over selling if found
        $this->productUtil->adjustStockOverSelling($purchase_transfer);

        //Map sell lines with purchase lines
        $business = ['id' => $businessId,
            'accounting_method' => $request->session()->get('business.accounting_method'),
            'location_id' => $sell_transfer->location_id
        ];
        $this->transactionUtil->mapPurchaseSell($business, $sell_transfer->sell_lines, 'purchase');

        $this->transactionUtil->activityLog($sell_transfer, 'added');

        // End of - Insert data in core tables
        DB::commit();
        return response()->json(['status' => 'success']);
    }

    public function destroy(Request $request){
        $nstPrepareStock = NstPrepareStock::findOrFail($request->get('id'));
        if($nstPrepareStock->destroy($request->get('id'))){
            $this->util->activityLog($nstPrepareStock, 'deleted');
            return response()->json(['status' => 'success']);
        }
        return response()->json(['status' => 'error']);
    }

    public function destroySelected(Request $request){
        $nstPrepareStocks = NstPrepareStock::whereIn('id', array_keys($request->receiveDoneTpsData))->get();

        foreach($nstPrepareStocks as $nstPrepareStock){
            if($nstPrepareStock->delete()){
                $this->util->activityLog($nstPrepareStock, 'deleted');
                continue;
            }
            return response()->json(['status' => 'error', 'message' => 'there is problem in delete some rows..']);
        }
        return response()->json(['status' => 'success']);
    }

    public function getSqlWithBindings($query){
        return vsprintf(str_replace('?', '%s', $query->toSql()), collect($query->getBindings())->map(function ($binding) {
            return is_numeric($binding) ? $binding : "'{$binding}'";
        })->toArray());
    }

}
