<?php

namespace App\Services\Statistic;

use App\Events\BatchStockUpdateEvent;
use App\Http\Enum\CacheKeyEnum;
use App\Http\Enum\StaticTypeEnum;
use App\Models\BusinessOrderItem;
use App\Models\CombinationGood;
use App\Models\DailyStockRecord;
use App\Models\GoodsSku;
use App\Models\LossRecords;
use App\Utils\DateTimeUtils;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;

class SaleDataService
{
    /**
     * sku维度的统计数据
     */
    public static function saleStatistics(Request $request)
    {
        if (StaticTypeEnum::TODAY == $request->type) {
            //实时统计 sku维度
            return static::skuSaleStatisticsByToday($request);
        } else {
            //历史数据查询
            return static::skuSaleStatisticsByHistory($request);
        }
    }

    /**
     * sku维度 今日实时统计
     */
    public static function skuSaleStatisticsByToday(Request $request)
    {
        [$startTime, $endTime] = static::getTimeRange($request);
        $build = BusinessOrderItem::query();
        if (!empty($request->sku_id)) {
            $externalSkuId = GoodsSku::query()->where("id", "=", $request->sku_id)
                ->pluck("external_sku_id")->first();
            if (!empty($externalSkuId)) {
                $build->where("external_sku_id", "=", $externalSkuId);
            } else {
                return [];
            }
        }
        $orderItems = $build
            ->leftJoin("business_orders as b", "business_order_id", "=", "b.id")
            ->select("business_order_items.external_sku_id"
                , DB::raw("sum(CASE WHEN b.shipping_status>0 THEN goods_number-already_cancel_number ELSE 0 END) as shipping_num")
                , DB::raw("sum(CASE WHEN b.shipping_status=0 THEN goods_number-already_cancel_number ELSE 0 END) as unshipping_num")
                , DB::raw("sum(goods_number-already_cancel_number) as goods_total")
                , DB::raw("ROUND(sum(goods_amount) / 100,2) as goods_total_amount"))
            ->whereBetween("business_order_items.created_at", [$startTime, $endTime])
            ->where("external_sku_id", "!=", "")
            ->where("business_order_items.cancel_status", "=", 0)
            ->groupBy('external_sku_id')
            ->orderBy("goods_total", "DESC")
            ->paginate($request->get('per_page'));

        if (!empty($orderItems->items())) {
            $externalSkuIds = collect($orderItems->items())->pluck("external_sku_id")->toArray();
            $goodsSkus = GoodsSku::query()->whereIn("external_sku_id", $externalSkuIds)
                ->select("id", "title", "name", "stock", "sale_stock", "status", "external_sku_id")->get()
                ->pluck(null, "id")->toArray();
            return static::addSaleDataToGoodsSku($goodsSkus, $orderItems);
        }
        return $orderItems;
    }

    public static function skuSaleStatisticsByHistory(Request $request)
    {
        [$startTime, $endTime] = static::getTimeRange($request);
        $build = DailyStockRecord::query();
        if (!empty($request->sku_id)) {
            $build->where("sku_id", "=", $request->sku_id);
        }
        $dailyRecord = $build->select("sku_id", DB::raw("sum(order_goods_num) as goods_total")
            , DB::raw("sum(order_total_amount) as goods_total_amount"))
            ->whereBetween("created_at", [$startTime, $endTime])
            ->groupBy("sku_id")
            ->having(DB::raw("sum(order_goods_num)"), ">", 0)
            ->orderBy("goods_total", "DESC")
            ->paginate($request->get('per_page'));
        $skuIds = collect($dailyRecord->items())->pluck('sku_id')->toArray();
        $goodsSkusMapKeyBySkuId = GoodsSku::query()->select("id", "title", "name", "stock", "sale_stock"
            , "status", "external_sku_id")->whereIn("id", $skuIds)
            ->get()->pluck(null, "id")->toArray();
        $dailyRecord->getCollection()->map(function ($v) use ($goodsSkusMapKeyBySkuId) {
            if (!empty($goodsSkusMapKeyBySkuId[$v['sku_id']])) {
                foreach ($goodsSkusMapKeyBySkuId[$v['sku_id']] as $key => $val) {
                    $v->$key = $val;
                }
            } else {
                //出现异常的skuid
                $v->title = "未知商品";
                $v->name = "未知商品";
                $v->stock = 0;
                $v->sale_stock = 0;
                $v->status = "下架";
                $v->id = 0;
            }
        });
        return $dailyRecord;
    }

    public static function addSaleDataToGoodsSku($goodsSku, $orderItems)
    {
        $skuIds = collect($goodsSku)->pluck("id")->toArray();
        //查询8天内-昨天的数据
        $startTime = Carbon::now()->subDays(8)->startOfDay()->toDateTimeString();
        $endTime = Carbon::yesterday()->endOfDay()->toDateTimeString();
        $dailyRecord = DailyStockRecord::query()->whereIn("sku_id", $skuIds)
            ->whereBetween("day", [$startTime, $endTime])
            ->get()->toArray();
        $dailyRecordGroupBySkuId = collect($dailyRecord)->groupBy("sku_id")->toArray();
        Log::info("dailyRecordGroupBySkuId", $dailyRecordGroupBySkuId);
        $combineGoodsSkus = collect($goodsSku)->map(function ($v) use ($dailyRecordGroupBySkuId, $orderItems) {
            $v['yesterday_avg_num'] = round(collect($dailyRecordGroupBySkuId[$v['id']] ?? [])->sortByDesc("day")
                    ->take(1)->avg("order_goods_num") ?? 0, 2);
            $v['three_day_avg_num'] = round(collect($dailyRecordGroupBySkuId[$v['id']] ?? [])->sortByDesc("day")
                    ->take(3)->avg("order_goods_num") ?? 0, 2);
            $v['seven_day_avg_num'] = round(collect($dailyRecordGroupBySkuId[$v['id']] ?? [])->sortByDesc("day")
                    ->take(7)->avg("order_goods_num") ?? 0, 2);
            return $v;
        })->pluck(null, "external_sku_id")->toArray();
        Log::info("combineGoodsSkus", $combineGoodsSkus);
        $orderItems->getCollection()->map(function ($v) use ($combineGoodsSkus) {
            if (!empty($combineGoodsSkus[$v['external_sku_id']])) {
                foreach ($combineGoodsSkus[$v['external_sku_id']] as $key => $val) {
                    $v->$key = $val;
                }
            } else {
                //出现异常售卖的编码
                $v->title = "未知商品";
                $v->name = "未知商品";
                $v->stock = 0;
                $v->sale_stock = 0;
                $v->status = "下架";
                $v->yesterday_avg_num = 0;
                $v->three_day_avg_num = 0;
                $v->id = 0;
                $v->seven_day_avg_num = 0;
            }
        });
        return $orderItems;
    }

    public static function spuSaleStatistics(Request $request)
    {
        //spu 基本就是全统计了
        if (StaticTypeEnum::TODAY == $request->type) {
            return static::spuSaleStatisticsByToday($request);
        } else {
            //统计历史数据 这里走缓存
            return static::spuSaleStatisticsByHistoryCache($request);
        }
    }

    public static function getTimeRange(Request $request)
    {
        if (!empty($request->input("start_time")) && !empty($request->input("start_time"))) {
            $startTime = Carbon::parse($request->input("start_time"))->toDateTimeString();
            $endTime = Carbon::parse($request->input("end_time"))->toDateTimeString();
        } else {
            $startTime = Carbon::parse($request->input("start_day"))->toDateTimeString();
            $endTime = Carbon::parse($request->input("end_day"))->endOfDay()->toDateTimeString();
        }
        return [$startTime, $endTime];
    }

    public static function spuSaleStatisticsByToday(Request $request)
    {
        [$startTime, $endTime] = static::getTimeRange($request);
        //实时统计 sku维度
        $orderItems = BusinessOrderItem::query()
            ->leftJoin("business_orders as b", "business_order_id", "=", "b.id")
            ->select("business_order_items.external_sku_id"
                , DB::raw("sum(CASE WHEN b.shipping_status>0 THEN goods_number-already_cancel_number ELSE 0 END) as shipping_num")
                , DB::raw("sum(CASE WHEN b.shipping_status=0 THEN goods_number-already_cancel_number ELSE 0 END) as unshipping_num")
                , DB::raw("sum(goods_number-already_cancel_number) as goods_total")
                , DB::raw("ROUND(sum(goods_amount) / 100,2) as goods_total_amount"))
            ->where('b.confirm_at', '>=', Carbon::parse($startTime)->getPreciseTimestamp(3))
            ->where('b.confirm_at', '<=', Carbon::parse($endTime)->getPreciseTimestamp(3))
            ->where("external_sku_id", "!=", "")
            ->where("business_order_items.cancel_status", "=", 0)
            ->groupBy('external_sku_id')->get()->toArray();

        $externalSkuIds = collect($orderItems)->pluck("external_sku_id")->toArray();
        $goodsSkus = GoodsSku::query()
            ->with([
                'combinationGoods:id,goods_sku_id,item_id,item_num',
                'combinationGoods.goodsSkuItem:id,name,goods_id,title,stock,sale_stock,external_sku_id,updated_at,yesterday_num,reference_price,status',
            ])
            ->whereIn("external_sku_id", $externalSkuIds)->get()->pluck(null, "external_sku_id")
            ->toArray();
        $skus = [];
        //组合商品
        foreach ($orderItems as $orderItem) {
            $sku = $goodsSkus[$orderItem['external_sku_id']] ?? [];
            if (!empty($sku['is_combination'])) {
                foreach ($sku['combination_goods'] as $combinationGood) {
                    $skuItem = $goodsSkus[$combinationGood['goods_sku_item']['external_sku_id']] ?? [];
                    $skuItem['shipping_num'] = $skuItem['shipping_num'] ?? 0 + $orderItem['shipping_num'] * $combinationGood['item_num'];
                    $skuItem['unshipping_num'] = $skuItem['unshipping_num'] ?? 0 + $orderItem['unshipping_num'] * $combinationGood['item_num'];
                    $skuItem['goods_total'] = $skuItem['goods_total'] ?? 0 + $orderItem['goods_total'] * $combinationGood['item_num'];
                    $skuItem['goods_total_amount'] = $skuItem['goods_total_amount'] ?? 0 + $orderItem['goods_total_amount'] * $combinationGood['item_num'];
                    $skus[$combinationGood['goods_sku_item']['external_sku_id']] = $skuItem;
                }
            } else {
                $skuItem = $skus[$orderItem['external_sku_id']] ?? [];
                $skuItem['shipping_num'] = $skuItem['shipping_num'] ?? 0 + $orderItem['shipping_num'];
                $skuItem['unshipping_num'] = $skuItem['unshipping_num'] ?? 0 + $orderItem['unshipping_num'];
                $skuItem['goods_total'] = $skuItem['goods_total'] ?? 0 + $orderItem['goods_total'];
                $skuItem['goods_total_amount'] = $skuItem['goods_total_amount'] ?? 0 + $orderItem['goods_total_amount'];
                $skus[$orderItem['external_sku_id']] = $skuItem;
            }
        }
        $goodsSkuWithTypes = GoodsSku::query()
            ->Join("goods", "goods_id", "=", "goods.id")
            ->Join("goods_types", "goods.type_id", "=", "goods_types.id")
            ->whereIn("external_sku_id", array_keys($skus))
            ->select("goods_skus.id", "goods_types.id as type_id", "goods_types.name", "external_sku_id", "stock", "sale_stock")
            ->get()->toArray();

        return collect($goodsSkuWithTypes)->map(function ($v) use ($skus) {
            if (!empty($skus[$v['external_sku_id']])) {
                return array_merge($v, $skus[$v['external_sku_id']]);
            }
        })->filter()->values()->groupBy('type_id')->map(function ($v, $key) {
            return [
                "type_id" => $key,
                "type_name" => $v[0]['name'] ?? '',
                "stock" => $v->sum("stock"),
                "sale_stock" => $v->sum("sale_stock"),
                "shipping_num" => $v->sum("shipping_num"),
                "unshipping_num" => $v->sum("unshipping_num"),
                "goods_total" => $v->sum("goods_total"),
                "goods_total_amount" => $v->sum("goods_total_amount"),
            ];
        })->sortByDesc('goods_total')->values()->toArray();
    }

    /**
     * 通过历史报表统计spu数据-缓存
     * @param Request $request
     * @return mixed
     */
    public
    static function spuSaleStatisticsByHistoryCache(Request $request)
    {
        [$startTime, $endTime] = static::getTimeRange($request);
        $cacheKey = CacheKeyEnum::SPU_STATISTIC_BY_DATE . $request->input("start_day") . "_" . $request->input("end_day");
        $expireTime = Carbon::now()->addHour();
        return Cache::remember($cacheKey, $expireTime, function () use ($startTime, $endTime) {
            return static::spuSaleStatisticsByHistory($startTime, $endTime);
        });
    }

    /**
     * 通过历史报表统计spu数据
     * @param $startTime
     * @param $endTime
     * @return array
     */
    public static function spuSaleStatisticsByHistory($startTime, $endTime)
    {
        $dailyAllRecord = DailyStockRecord::query()
            ->select("sku_id", DB::raw("sum(order_goods_num) as goods_total")
                , DB::raw("sum(order_total_amount) as goods_total_amount"))
            ->whereBetween("day", [$startTime, $endTime])
            ->groupBy("sku_id")
            ->having(DB::raw("sum(order_goods_num)"), ">", 0)
            ->get()->toArray();
        $skuIds = collect($dailyAllRecord)->pluck('sku_id')->toArray();
        $goodsSkus = GoodsSku::query()
            ->with([
                'combinationGoods:id,goods_sku_id,item_id,item_num'
            ])
            ->whereIn("id", $skuIds)->get()->pluck(null, "id")
            ->toArray();
        $skus = [];
        //组合商品需要分散到sku维度进行统计
        foreach ($dailyAllRecord as $skuRecord) {
            $sku = $goodsSkus[$skuRecord['sku_id']] ?? [];
            if (!empty($sku['is_combination'])) {
                foreach ($sku['combination_goods'] as $combinationGood) {
                    $skuItem = $goodsSkus[$combinationGood['item_id']] ?? [];
                    $skuItem['goods_total'] = $skuItem['goods_total'] ?? 0 + $skuRecord['goods_total'] * $combinationGood['item_num'];
                    $skuItem['goods_total_amount'] = $skuItem['goods_total_amount'] ?? 0 + $skuRecord['goods_total_amount'] * $combinationGood['item_num'];
                    $skus[$combinationGood['item_id']] = $skuItem;
                }
            } else {
                $skuItem = $skus[$skuRecord['sku_id']] ?? [];
                $skuItem['goods_total'] = $skuItem['goods_total'] ?? 0 + $skuRecord['goods_total'];
                $skuItem['goods_total_amount'] = $skuItem['goods_total_amount'] ?? 0 + $skuRecord['goods_total_amount'];
                $skus[$skuRecord['sku_id']] = $skuItem;
            }
        }
        $goodsSkuWithTypes = GoodsSku::query()
            ->Join("goods", "goods_id", "=", "goods.id")
            ->Join("goods_types", "goods.type_id", "=", "goods_types.id")
            ->select("goods_skus.id", "goods_types.id as type_id", "goods_types.name", "external_sku_id", "stock", "sale_stock")
            ->whereIn("goods_skus.id", array_keys($skus))
            ->get()->toArray();

        return collect($goodsSkuWithTypes)->map(function ($v) use ($skus) {
            if (!empty($skus[$v['id']])) {
                return array_merge($v, $skus[$v['id']]);
            }
        })->filter()->values()->groupBy('type_id')->map(function ($v, $key) {
            return [
                "type_id" => $key,
                "type_name" => $v[0]['name'] ?? '',
                "stock" => $v->sum("stock"),
                "sale_stock" => $v->sum("sale_stock"),
                "goods_total" => $v->sum("goods_total"),
                "goods_total_amount" => $v->sum("goods_total_amount"),
            ];
        })->sortByDesc('goods_total')->values()->toArray();
    }

    /**
     * gmv 统计
     * @param Request $request
     * @return array
     */
    public static function gmvStatistics(Request $request)
    {
        if (StaticTypeEnum::TODAY == $request->type) {
            [$startTime, $endTime] = static::getTimeRange($request);
            $build = BusinessOrderItem::query();
            if (!empty($request->sku_id)) {
                $externalSkuId = GoodsSku::query()->where("id", "=", $request->sku_id)
                    ->pluck("external_sku_id")->first();
                if (!empty($externalSkuId)) {
                    $build->where("external_sku_id", "=", $externalSkuId);
                } else {
                    return [];
                }
            }
            $orderItems = $build->select("business_order_items.external_sku_id"
                , "goods_number", "already_cancel_number", "goods_amount", "created_at")
                ->whereBetween("business_order_items.created_at", [$startTime, $endTime])
                ->where("business_order_items.cancel_status", "=", 0)
                ->get()->toArray();
            $interval = $request->input("interval", 30);
            return collect($orderItems)->groupBy(function ($v) use ($startTime, $interval) {
                $diff = Carbon::parse($v['created_at'])->diffInMinutes(Carbon::parse($startTime));
                return (int)floor($diff / $interval);
            })->map(function ($v, $key) use ($startTime, $interval) {
                return [
                    "sort_key" => $key,
                    "interval" => $interval,
                    "time_start" => Carbon::parse($startTime)->addMinutes($key * $interval)->toTimeString(),
                    "time_end" => Carbon::parse($startTime)->addMinutes(($key + 1) * $interval)->toTimeString(),
                    "goods_total" => $v->sum("goods_number") - $v->sum("already_cancel_number"),
                    "goods_total_amount" => round($v->sum("goods_amount") / 100, 2),
                ];
            })->sort()->values()->toArray();
        } else {
            //gmv 统计历史数据
            [$startTime, $endTime] = static::getTimeRange($request);
            $build = DailyStockRecord::query();
            if (!empty($request->sku_id)) {
                $build->where("sku_id", "=", $request->sku_id);
            }
            return $build->select("day", DB::raw("sum(order_total_amount) as goods_total_amount")
                , DB::raw("sum(order_goods_num) as goods_total"))
                ->whereBetween("day", [$startTime, $endTime])
                ->groupBy("day")->orderBy("day")
                ->get()->toArray();
        }

    }

    /**
     * 报损统计
     * @param Request $request
     * @return array
     */
    public static function lossRecordStatistics(Request $request)
    {
        $startTime = Carbon::parse($request->input("start_time"))->toDateTimeString();
        $endTime = Carbon::parse($request->input("end_time"))->toDateTimeString();
        $build = LossRecords::query();
        if (!empty($request->sku_id)) {
            $externalSkuId = GoodsSku::query()->where("id", "=", $request->sku_id)
                ->pluck("external_sku_id")->first();
            if (!empty($externalSkuId)) {
                $build->where("external_sku_id", "=", $externalSkuId);
            } else {
                return [];
            }
        }
        return $build->select("date", DB::raw("sum(num) as total_num")
            , DB::raw("sum(num*cost) as total_loss_amount"))
            ->whereBetween("date", [$startTime, $endTime])
            ->groupBy("date")->orderBy("date")
            ->get()->toArray();
    }


}
