<?php

namespace App\Services\GoodSku;

use App\Events\BatchStockUpdateEvent;
use App\Http\Enum\BusinessOrderShippingStatus;
use App\Http\Enum\TargetTypeEnum;
use App\Models\BusinessOrderItem;
use App\Models\CombinationGood;
use App\Models\DailyStockRecord;
use App\Models\GoodsSku;
use App\Models\Log as LogModel;
use App\Utils\DateTimeUtils;
use App\Utils\GeneratorUtils;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use phpDocumentor\Reflection\Types\Collection;

class GoodSkuService
{
    /**
     * $skusWithCombinationGoods 除了携带组合商品的字段 还拼接了盘点的具体库存值inventory
     * @param array $skusWithCombinationGoods
     * @return void
     */
    public function inventory(array $skusWithCombinationGoods)
    {
        $skusWithCombinationGoods = $this->handleSkusWithCombinationGoods($skusWithCombinationGoods);

        //传进来的sku可能包含组合商品 所以这里需要事先计算好数据
        $inventoryKeyBySkuIdMap = collect($skusWithCombinationGoods)->where('is_combination', "=", 0)
            ->pluck("real_stock", "id")->toArray();

        Log::info("库存原始操作map", $inventoryKeyBySkuIdMap);
        //计算组合商品
        foreach ($skusWithCombinationGoods as $sku) {
            if (!empty($sku['is_combination'])) {
                foreach ($sku['combination_goods'] as $combinationGoods) {
                    if (!isset($inventoryKeyBySkuIdMap[$combinationGoods["item_id"]])) {
                        //没有盘点到的sku需要在原先的sku库存
                        $inventoryKeyBySkuIdMap[$combinationGoods["item_id"]] = 0;
                    }

                    $inventoryKeyBySkuIdMap[$combinationGoods["item_id"]] += $combinationGoods['item_num'] * $sku['real_stock'];
                }
            }
        }

        $today = DateTimeUtils::getToday();
        $dateTime = date('Y-m-d H:i:s');
        $updateIds = [];
        Log::info("库存盘点前完整信息", $skusWithCombinationGoods);
        Log::info("需要操作的库存数据", $inventoryKeyBySkuIdMap);
        $batchNumber = GeneratorUtils::generateBatchNumber("import");

        DB::beginTransaction();
        try {
            foreach ($skusWithCombinationGoods as $sku) {
                // 更新每日数据
                DailyStockRecord::query()->updateOrCreate([
                    'sku_id' => $sku['id'],
                    'day' => $today,
                ], [
                    "inventory" => $sku['inventory'],
                    "inventory_time" => $dateTime,
                    "batch_number" => $batchNumber
                ]);
            }
            $costLogs = [];
            //更新库存并记录数据
            foreach ($inventoryKeyBySkuIdMap as $skuId => $realStock) {
                //库存修改 盘点是直接覆盖
                $skuData = GoodsSku::query()->where('id', $skuId)->first();
                if (!empty($skuData)) {
                    $costLogs[] = static::addStockLog($skuData
                        , TargetTypeEnum::INVENTORY, ['stock' => $realStock]);
                    $skuData->stock = $realStock;
                    $skuData->save();
                    $updateIds[] = $skuId;
                }

            }
            $log = new LogModel();
            $log->batchInsert($costLogs);
            DB::commit();
        } catch (\Exception $exception) {
            DB::rollBack();
            Log::error("库存盘点异常", ["error" => $exception->getMessage()]);
        }

        // 批量更新
        event(new BatchStockUpdateEvent($updateIds));
    }

    /**
     * 库存盘点 需要扣减未发货未取消的订单数据
     * 目前只查询了7日内的数据
     * @param array $skusWithCombinationGoods
     * @return \Illuminate\Support\Collection
     */
    public function handleSkusWithCombinationGoods(array $skusWithCombinationGoods)
    {
        //查询sku当前未发货的数量 需要扣减
        $externalSkuIds = collect($skusWithCombinationGoods)->pluck("external_sku_id")->toArray();
        $skuIds = collect($skusWithCombinationGoods)->pluck("id")->toArray();
        //关联的组合数据
        $combinationGoods = CombinationGood::query()->with("goodsSku:id,external_sku_id")->whereIn('item_id', $skuIds)->get()->toArray();
        $externalSkuIds = collect($combinationGoods)->pluck("goods_sku.external_sku_id")->merge(collect($externalSkuIds))->toArray();
        Log::info("需要查询的externalSkuIds",[$externalSkuIds]);
        //默认只查15天内未发货的数据
        $startTime = Carbon::now()->subDays(15)->toDateTimeString();
        $unshippedDataCollect = BusinessOrderItem::query()
            ->with([
                'shop:id,name',
                'goodsSku:id,external_sku_id,is_combination',
                'goodsSku.combinationGoods:id,goods_sku_id,item_id,item_num'
            ])
            ->leftJoin("business_orders as b", "business_order_id", "=", "b.id")
            ->select("external_sku_id", DB::raw("SUM(goods_number) - SUM(already_cancel_number) as number"))
            ->whereIn("external_sku_id", $externalSkuIds)
            ->where("b.shipping_status", "=", BusinessOrderShippingStatus::UNSHIP)
            ->where("b.after_sales_status", "!=", 2)
            ->where("business_order_items.created_at", ">=", $startTime)->where("business_order_items.cancel_status", "=", 0)
            ->groupBy('external_sku_id')->get()->toArray();

        Log::info("盘点未发货数据",[$unshippedDataCollect]);
        $ids = [];
        //重组订单
        foreach ($unshippedDataCollect as $businessOrderItem) {
            if (is_null($businessOrderItem['goods_sku'])) {
                continue;
            }
            $id = $businessOrderItem['goods_sku']['id'];
            if ($businessOrderItem['goods_sku']['is_combination']) {
                foreach ($businessOrderItem['goods_sku']['combination_goods'] ?? [] as $combinationGoods) {
                    $ids[$combinationGoods['item_id']] = ($ids[$combinationGoods['item_id']] ?? 0)
                        + ((int)$businessOrderItem['number']) * $combinationGoods['item_num'];
                }
            }
            if (isset($ids[$id])) {
                $ids[$id] += (int)$businessOrderItem['number'];
            } else {
                $ids[$id] = (int)$businessOrderItem['number'];
            }
        }

        return collect($skusWithCombinationGoods)->map(function ($v) use ($ids) {
            $v['real_stock'] = $v['inventory'] ?? null;
            if (!empty($ids[$v['id']]) && isset($v['inventory'])) {
                $v['real_stock'] = $v['inventory'] - $ids[$v['id']];
            }
            return $v;
        })->toArray();
    }


    public static function computeSkuStock(array $goodsSkuItem, array $changeData, $targetType = TargetTypeEnum::LOSS)
    {
        $updateIds = [];
        $updateParams = [];
        //添加系统日志
        $costLogs = [];
        // 成本
        if (empty($goodsSkuItem['is_combination'])) {
            $updateParam = [
                'stock' => $goodsSkuItem['stock'] + $changeData['num'],
                'sale_stock' => max($goodsSkuItem['sale_stock'] + $changeData['num'], 0),
            ];

            if ($targetType == TargetTypeEnum::PURCHASE) {
                $updateParam['cost'] = $changeData['cost'];
            }
            if ($updateParam['sale_stock'] <= 0) {
                $updateParam['status'] = GoodsSku::$STATUS_DOWN;
            }
            GoodsSku::query()->where('external_sku_id', "=", $goodsSkuItem['external_sku_id'])
                ->update($updateParam);
            $updateIds[] = $goodsSkuItem['id'];
            $updateParams[] = $updateParam;
            $costLogs[] = static::addStockLog($goodsSkuItem, $targetType, $updateParam);

        } else {
            $combinationGood = CombinationGood::query()->with('goodsSkuItem:id,stock,sale_stock')
                ->where('goods_sku_id', $goodsSkuItem['id'])->get();

            foreach ($combinationGood as $item) {
                $updateParam = [
                    'stock' => $item['goodsSkuItem']['stock'] + $changeData['num'] * $item['item_num'],
                    'sale_stock' => max($item['goodsSkuItem']['sale_stock'] + $changeData['num'] * $item['item_num'], 0),
                ];
                if ($updateParam['sale_stock'] <= 0) {
                    $updateParam['status'] = GoodsSku::$STATUS_DOWN;
                }
                GoodsSku::query()->where('id', $item['goodsSkuItem']['id'])->update($updateParam);

                $updateIds[] = $item['goodsSkuItem']['id'];
                $updateParams[] = $updateParam;
                $costLogs[] = static::addStockLog($item['goodsSkuItem'], $targetType, $updateParam);
            }
        }
        $log = new LogModel();
        $log->batchInsert($costLogs);
        Log::info("本次请求更新参数", $updateParams);
        return $updateIds;
    }

    public static function addStockLog($goodsSkuItem, $targetType = TargetTypeEnum::LOSS, $updateParam)
    {
        $userId = Auth::id();
        $costLog = [
            'module' => 'goods',
            'action' => "POST",
            'target_type' => $targetType,
            'target_id' => $goodsSkuItem['id'] ?? 0,
            'user_id' => $userId ?? 999,
            "target_field" => "stock"
        ];
        $costLog['before_update'] = json_encode($goodsSkuItem);
        $costLog['after_update'] = json_encode($updateParam);

        return $costLog;
    }

}
