<?php

namespace App\Http\Controllers\Goods;

use App\Events\StockUpdateEvent;
use App\Exports\GoodsSkusExport;
use App\Http\Controllers\Controller;
use App\Http\Requests\GoodsRequest;
use App\Http\Requests\GoodsSkuRequest;
use App\Models\Goods;
use App\Models\Log;
use App\Models\Log as LogModel;
use App\Utils\DateTimeUtils;
use Illuminate\Http\Request;
use App\Models\GoodsSku;
use App\Http\Resources\GoodsSkuResource;
use App\Imports\GoodsSkusImport;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Maatwebsite\Excel\Facades\Excel;
use App\Models\DailyStockRecord;

class GoodsSkusController extends Controller
{
    public function __construct(Request $request)
    {
        $this->log = new LogModel([
            'module' => 'goods',
            'action' => $request->getMethod(),
            'target_type' => 'goods_sku',
        ]);
    }

    public function index(Request $request)
    {
        $goods = Goods::query()->filter()->get()->toArray();
        $goodsIds = array_column($goods, 'id');
        // 状态变更时间查询,日志
        $ids = [];
        if ($request->get('keyword_type', '') && $request->get('keyword_value', '')) {
            $ids = Log::query()->where('target_type', 'sku')
                ->where('target_field', $request->keyword_type)
                ->whereBetween('created_at', explode(' - ', $request->keyword_value))
                ->pluck('sku_id')
                ->toArray();
        }
        $day = DateTimeUtils::getToday();
        $goodsSkus = GoodsSku::query()
            ->whereIn('goods_id', $goodsIds)
            ->when($ids, function ($query, $ids) {
                return $query->whereIn('id', $ids);
            })
            ->filter()
            ->with(['goods' => function ($query) {
                $query->with(['type:id,name', 'brand:id,name']);
            }])
            ->with(['daily' => function ($query) use ($day) {
                $query->where('day', $day);
            }])
            ->orderBy('updated_at', 'desc')
            ->paginate($request->get('per_page'));

        return GoodsSkuResource::collection($goodsSkus);
    }

    public function show($id)
    {
        return new GoodsSkuResource(GoodsSku::query()
            ->with(['goods' => function ($query) {
                $query->with(['type:id,name', 'brand:id,name']);
            }])
            ->find($id));
    }

    public function update($id, Request $request)
    {
        $goodsRules = (new GoodsRequest())->arrayRules('goods.');
        $skuRules = (new GoodsSkuRequest())->arrayRules('sku.');
        $validator = Validator::make($request->all(), array_merge($goodsRules, $skuRules));
        if ($validator->fails()) {
            $this->setValidatorFailResponse($validator->getMessageBag()->getMessages());

            return response($this->res, $this->res['httpCode']);
        }
        DB::beginTransaction();
        try {
            // 商品规格更新
            $sku = GoodsSku::query()->find($id);
            $this->setBeforeUpdate($sku->toArray());
            $sku->update($request->sku);
            $this->setAfterUpdate($sku->toArray());
            $this->addLog($id, 'update');
            // 商品更新
            $goods = Goods::query()->find($sku->goods_id);
            $this->log = new LogModel([
                'module' => 'goods',
                'action' => $request->getMethod(),
                'target_type' => 'goods',
            ]);
            $this->setBeforeUpdate($goods->toArray());
            $goods->update($request->goods);
            $this->setAfterUpdate($goods->toArray());
            $this->addLog($sku->goods_id, 'update');
            DB::commit();
        } catch (\Exception $exception) {
            DB::rollBack();
            $this->res = [
                'httpCode' => 400,
                'errorCode' => 400416,
                'errorMessage' => $exception->getMessage(),
            ];
        }

        return response($this->res, $this->res['httpCode']);
    }

    public function batchUpdate(Request $request)
    {
        $appendRules = [
            'updateType' => ['required', 'string', Rule::in(['newest', 'inventory', 'stock'])],
            'skus' => ['required', 'array'],
            'skus.*.id' => [
                'required',
                Rule::exists('goods_skus', 'id'),
            ],
        ];
        $skuRules = (new GoodsSkuRequest())->arrayRules('skus.*.');
        $validator = Validator::make($request->all(), array_merge($appendRules, $skuRules));
        if ($validator->fails()) {
            $this->setValidatorFailResponse($validator->getMessageBag()->getMessages());

            return response($this->res, $this->res['httpCode']);
        }
        $function = $request->updateType;

        return $this->$function($request);
    }

    private function newest($request)
    {
        DB::beginTransaction();
        try {
            $logs = [];
            foreach ($request->skus as $sku) {
                $costLog = $arrivedLog = [
                    'module' => 'goods',
                    'action' => $request->getMethod(),
                    'target_type' => 'goods_sku',
                    'target_id' => $sku['id'],
                    'user_id' => $request->user()->id
                ];
                // 成本
                $goodsSku = GoodsSku::query()->where('id', $sku['id'])->first(['id', 'cost', 'stock', 'num']);
                $costLog['target_field'] = 'cost';
                $costLog['before_update'] = $goodsSku->cost;
                $goodsSku->cost = $sku['cost'];
                $goodsSku->reference_price = $sku['cost'] * 1.5;
                $goodsSku->stock += $sku['arrived_today_num'];
                $goodsSku->num += $sku['arrived_today_num'];
                $goodsSku->save();
                $costLog['after_update'] = $goodsSku->cost;
                $logs[] = $costLog;
                // 今日到货
                $record = DailyStockRecord::query()->where('sku_id', $sku['id'])->where('day', DateTimeUtils::getToday())->first(['id', 'arrived_today_num']);
                $arrivedLog['target_field'] = 'arrived_today_num';
                $arrivedLog['before_update'] = $record->arrived_today_num;
                $record->arrived_today_num += $sku['arrived_today_num'];
                $record->save();
                $arrivedLog['after_update'] = $record->arrived_today_num;
                $logs[] = $arrivedLog;
            }
            $log = new LogModel();
            $log->batchInsert($logs);
            DB::commit();
            event(new StockUpdateEvent(array_column($request->skus, 'id')));
        } catch (\Exception $exception) {
            DB::rollBack();
            $this->res = [
                'httpCode' => 400,
                'errorCode' => 400500,
                'errorMessage' => $exception->getMessage(),
            ];
        }

        return response($this->res, $this->res['httpCode']);
    }

    private function inventory($request)
    {
        DB::beginTransaction();
        try {
            $logs = [];
            foreach ($request->skus as $sku) {
                $inventoryLog = [
                    'module' => 'goods',
                    'action' => $request->getMethod(),
                    'target_type' => 'goods_sku',
                    'target_id' => $sku['id'],
                    'user_id' => $request->user()->id
                ];
                $record = DailyStockRecord::query()->where('sku_id', $sku['id'])->where('day', DateTimeUtils::getToday())->first(['id', 'inventory']);
                $inventoryLog['target_field'] = 'inventory';
                $inventoryLog['before_update'] = $record->inventory;
                $record->inventory = $sku['inventory'];
                $record->save();
                $inventoryLog['after_update'] = $record->inventory;
                $logs[] = $inventoryLog;
            }
            $log = new LogModel();
            $log->batchInsert($logs);
            DB::commit();
        } catch (\Exception $exception) {
            DB::rollBack();
            $this->res = [
                'httpCode' => 400,
                'errorCode' => 400500,
                'errorMessage' => $exception->getMessage(),
            ];
        }

        return response($this->res, $this->res['httpCode']);
    }

    private function stock($request)
    {
        $skus = $request->skus;
        $update = reset($skus);
        DB::beginTransaction();
        try {
            $sku = GoodsSku::query()->where('id', $update['id'])->first(['id', 'two_days_ago_num', 'yesterday_num', 'num', 'stock', 'reserve']);
            $record = DailyStockRecord::query()
                ->where('sku_id', $sku->id)
                ->where('day', DateTimeUtils::getToday())
                ->first();
            $this->setBeforeUpdate([
                'two_days_ago_num' => $sku->two_days_ago_num,
                'yesterday_num' => $sku->yesterday_num,
                'arrived_today_num' => $record->arrived_today_num,
                'num' => $sku->num,
                'stock' => $sku->stock,
            ]);
            $sku->two_days_ago_num = $update['two_days_ago_num'];
            $sku->yesterday_num = $update['yesterday_num'];
            $sku->num = $update['two_days_ago_num'] + $update['yesterday_num'] + $update['arrived_today_num'];
            $sku->stock += ($update['two_days_ago_num'] + $update['yesterday_num'] + $update['arrived_today_num'] - $record->arrived_today_num - $sku->two_days_ago_num - $sku->yesterday_num);
            $sku->save();
            $record->arrived_today_num = $update['arrived_today_num'];
            $record->save();
            $this->setAfterUpdate([
                'two_days_ago_num' => $sku->two_days_ago_num,
                'yesterday_num' => $sku->yesterday_num,
                'arrived_today_num' => $record->arrived_today_num,
                'num' => $sku->num,
                'stock' => $sku->stock,
            ]);
            $this->addLog($sku->id, 'stock');
            DB::commit();
            event(new StockUpdateEvent($sku));
        } catch (\Exception $exception) {
            DB::rollBack();
            $this->res = [
                'httpCode' => 400,
                'errorCode' => 400416,
                'errorMessage' => $exception->getMessage(),
            ];
        }

        return response($this->res, $this->res['httpCode']);
    }

    public function updateField($id, Request $request)
    {
        $rules = [
            'updateField' => [
                'required',
                Rule::in(['reference_price', 'reserve', 'loss_num', 'status'])
            ],
            'reference_price' => [
                'sometimes',
                'numeric',
                'gt:0'
            ],
            'reserve' => [
                'sometimes',
                'integer',
            ],
            'loss_num' => [
                'sometimes',
                'integer',
            ],
            'reason' => [
                'sometimes',
                'required',
                'string'
            ],
            'status' => [
                'sometimes',
                'required',
                'integer',
                Rule::in([0, 1, 2])],
        ];
        $validator = Validator::make($request->all(), $rules);
        if ($validator->fails()) {
            $this->setValidatorFailResponse($validator->getMessageBag()->getMessages());
            goto end;
        }
        $updateField = \request('updateField');
        $sku = GoodsSku::query()->find($id);
        if ('loss_num' === $updateField) {
            $record = DailyStockRecord::query()
                ->where('sku_id', $id)
                ->where('day', DateTimeUtils::getToday())
                ->first(['id', 'loss_num']);
            $this->log->message = $request->get('reason');
            $this->setBeforeUpdate($record->loss_num);
            $record->loss_num += $request->loss_num;
            $record->save();
            $this->setAfterUpdate($record->loss_num);
            $sku->stock -= $request->loss_num;
            $sku->save();
        } else {
            $this->setBeforeUpdate($sku->$updateField);
            if ('reserve' === $updateField) {
                $changeNum = $sku->reserve - $request->reserve;
                if (0 > $changeNum + $sku->stock) {
                    $this->setValidatorFailResponse('预留量超过库存数量');
                    goto end;
                }
                $sku->stock += $changeNum;
            }
            $sku->$updateField = $request->$updateField;
            $sku->save();
            $this->setAfterUpdate($sku->$updateField);
        }
        if (in_array($updateField, ['reserve', 'loss_num'])) {
            event(new StockUpdateEvent($sku));
        }
        $this->addLog($id, $updateField);
        end:

        return response($this->res, $this->res['httpCode']);
    }

    public function store(Request $request)
    {
        if (!$request->hasFile('goodsSkus')) {
            $this->res = [
                'httpCode' => 404,
                'errorCode' => 404404,
                'errorMessage' => 'not found goodsSkus file',
            ];
        }
        try {
            $import = new GoodsSkusImport();
            $path = $request->file('goodsSkus');
            Excel::import($import, $path);
            $this->addLog(0, 'import');
        } catch (ValidationException $exception) {
            $this->setValidatorFailResponse($exception->validator->getMessageBag()->getMessages());
        }

        return response($this->res, $this->res['httpCode']);
    }

    public function export(Request $request)
    {
        $type = $request->get('exportType');
        ob_end_clean();
        return Excel::download(new GoodsSkusExport($type), $type, '.xlsx');
    }
}
