<?php

namespace App\Services\tcc;

use Api\PhpUtils\Common\GoodsSkuId;
use Api\PhpUtils\Log\FileLog;
use App\Exception\custom\GoodsException;
use App\Models\goods\mysql\GoodsSku;
use App\Models\goods\mysql\PindanGoodsSku;
use App\Models\goods\mysql\Tcc;
use App\Models\marketing\mysql\Marketing;
use App\Models\marketing\mysql\MarketingPindan;
use App\Services\goods\MarketingPindanGoodsService;

class Tcc2Service
{
    const TCC_RESULT_FAIL = false;
    const TCC_RESULT_SUCCESS = true;

    /**
     * 下单 商品tcc try
     * @param $keys
     * @param $tid
     * @return bool
     * @throws GoodsException
     */
    public static function placeAnOrderTry($keys, $tid)
    {
        $goodsInfoList = json_decode($keys, true);
        if (empty($goodsInfoList)) {
            throw new GoodsException(["cus" => 42]);
        }

        $isPindan = false;
        foreach ($goodsInfoList as $item) {
            if (empty($item["goods_sku_id"]) || empty($item["num"])) {
                throw new GoodsException(["cus" => 42]);
            }
            if (!empty($item["marketing_id"]) && !empty($item["marketing_type"]) && $item["marketing_type"] == Marketing::MARKETING_TYPE_PINDAN) {
                $isPindan = true;
            }
        }

        $tccInfo = self::getTccRecord($tid);
        //有结果返回原来的结果，幂等
        if (!empty($tccInfo["status"]) && $tccInfo["status"] == Tcc::STATUS_T_SUCCESS) {
            return self::TCC_RESULT_SUCCESS;
        }

        //如果空 cancel，则直接成功
        if (!empty($tccInfo["status"]) && in_array($tccInfo["status"], [Tcc::STATUS_C2_SUCCESS])) {
            return self::TCC_RESULT_SUCCESS;
        }

        if ($isPindan) {
            return self::pindanGoodsTry($tid, $keys, $goodsInfoList);
        } else {
            return self::generalGoodsTry($tid, $keys, $goodsInfoList);
        }
    }

    /**
     * 普通商品库存 try
     * @param $tid
     * @param $keys
     * @param $goodsInfoList
     * @return bool
     * @throws GoodsException
     */
    private static function generalGoodsTry($tid, $keys, $goodsInfoList)
    {
        if (!empty($goodsInfoList)) {
            GoodsSku::beginTransaction();
            foreach ($goodsInfoList as $item) {
                $goodsSkuId = $item["goods_sku_id"];
                $num = $item["num"];
                $sku = GoodsSku::getRecord(["goods_sku_id" => $goodsSkuId]
                    , ["goods_sku_id", "inventory_rest", "inventory_lock", "online_status"]);
                if ($sku["online_status"] == GoodsSku::ONLINE_STATUS_ONLINE) {
                    //条件加上乐观锁
                    $rowCount = GoodsSku::save([
                        "inventory_lock[+]" => $num,
                        "inventory_rest[-]" => $num,
                    ], ["goods_sku_id" => $goodsSkuId, "inventory_rest[>=]" => $num]);
                    if ($rowCount <= 0) {
                        GoodsSku::rollback();
                        throw new GoodsException(["cus" => 24]);
                    }
                } else {
                    GoodsSku::rollback();
                    throw new GoodsException(["cus" => 43]);
                }
            }
            Tcc::save([
                "tid" => $tid,
                "goods_data" => $keys,
                "status" => Tcc::STATUS_T_SUCCESS,
            ]);

            if (!GoodsSku::commit()) {
                GoodsSku::rollback();
                throw new GoodsException(["cus" => 44]);
            }
        }

        return self::TCC_RESULT_SUCCESS;
    }

    /**
     * 拼单商品库存 try
     * @param $tid
     * @param $keys
     * @param $goodsInfoList
     * @return bool
     * @throws GoodsException
     */
    private static function pindanGoodsTry($tid, $keys, $goodsInfoList)
    {
        if (!empty($goodsInfoList)) {
            PindanGoodsSku::beginTransaction();
            foreach ($goodsInfoList as $item) {
                $goodsSkuId = $item["goods_sku_id"];
                $num = $item["num"];
                $sku = PindanGoodsSku::getRecord(["goods_sku_id" => $goodsSkuId]
                    , ["goods_sku_id", "inventory_rest", "inventory_lock", "online_status"]);
                if ($sku["online_status"] == PindanGoodsSku::ONLINE_STATUS_ONLINE) {
                    //条件加上乐观锁
                    $rowCount = PindanGoodsSku::save([
                        "inventory_lock[+]" => $num,
                        "inventory_rest[-]" => $num,
                    ], ["goods_sku_id" => $goodsSkuId, "inventory_rest[>=]" => $num]);
                    if ($rowCount <= 0) {
                        PindanGoodsSku::rollback();
                        throw new GoodsException(["cus" => 24]);
                    }
                } else {
                    PindanGoodsSku::rollback();
                    throw new GoodsException(["cus" => 43]);
                }
            }
            Tcc::save([
                "tid" => $tid,
                "goods_data" => $keys,
                "status" => Tcc::STATUS_T_SUCCESS,
            ]);

            if (!PindanGoodsSku::commit()) {
                PindanGoodsSku::rollback();
                throw new GoodsException(["cus" => 44]);
            }
        }

        return self::TCC_RESULT_SUCCESS;
    }


    /**
     * 下单 商品tcc confirm
     * @param $keys
     * @param $tid
     * @return bool
     * @throws GoodsException
     */
    public static function placeAnOrderConfirm($keys, $tid)
    {
        $goodsInfoList = json_decode($keys, true);
        if (empty($goodsInfoList)) {
            throw new GoodsException(["cus" => 42]);
        }

        $isPindan = false;
        foreach ($goodsInfoList as $item) {
            if (empty($item["goods_sku_id"]) || empty($item["num"])) {
                throw new GoodsException(["cus" => 42]);
            }
            if (!empty($item["marketing_id"]) && !empty($item["marketing_type"]) && $item["marketing_type"] == Marketing::MARKETING_TYPE_PINDAN) {
                $isPindan = true;
            }
        }

        $tccInfo = self::getTccRecord($tid);
        //有结果返回原来的结果，幂等
        if (!empty($tccInfo["status"]) && $tccInfo["status"] == Tcc::STATUS_C1_SUCCESS) {
            return self::TCC_RESULT_SUCCESS;
        }
        //在confirm时候必须已经有try，如果没有就直接返回fail
        if (!empty($tccInfo["status"]) && !in_array($tccInfo["status"], [Tcc::STATUS_T_SUCCESS])) {
            throw new GoodsException(["cus" => 45]);
        }

        //如果已经 cancel 成功, 则失败
        if (!empty($tccInfo["status"]) && in_array($tccInfo["status"], [Tcc::STATUS_C2_SUCCESS])) {
            throw new GoodsException(["cus" => 46]);
        }

        if ($isPindan) {
            return self::pindanGoodsConfirm($tid, $goodsInfoList);
        } else {
            return self::generalGoodsConfirm($tid, $goodsInfoList);
        }
    }

    /**
     * 普通商品库存 confirm
     * @param $tid
     * @param $goodsInfoList
     * @return bool
     * @throws GoodsException
     */
    private static function generalGoodsConfirm($tid, $goodsInfoList)
    {
        if (!empty($goodsInfoList)) {
            GoodsSku::beginTransaction();
            foreach ($goodsInfoList as $item) {
                $goodsSkuId = $item["goods_sku_id"];
                $num = $item["num"];
                $sku = GoodsSku::getRecord(["goods_sku_id" => $goodsSkuId]
                    , ["goods_sku_id", "inventory_rest", "total_amount_order", "inventory_lock", "online_status"]);

                if (!empty($sku["inventory_lock"]) && $sku["online_status"] == GoodsSku::ONLINE_STATUS_ONLINE) {

                    //条件加上乐观锁
                    $rowCount = GoodsSku::save([
                        "inventory_lock[-]" => $num,
                        "total_amount_order[+]" => $num,
                    ], ["goods_sku_id" => $goodsSkuId, "inventory_lock[>=]" => $num]);
                    if ($rowCount <= 0) {
                        GoodsSku::rollback();
                        throw new GoodsException(["cus" => 31]);
                    }
                } else {
                    GoodsSku::rollback();
                    throw new GoodsException(["cus" => 43]);
                }
            }
            Tcc::save(["status" => Tcc::STATUS_C1_SUCCESS,], ["tid" => $tid]);

            if (!GoodsSku::commit()) {
                GoodsSku::rollback();
                throw new GoodsException(["cus" => 44]);
            }
        }

        return self::TCC_RESULT_SUCCESS;
    }

    /**
     * 拼单商品库存 confirm
     * @param $tid
     * @param $goodsInfoList
     * @return bool
     * @throws GoodsException
     */
    private static function pindanGoodsConfirm($tid, $goodsInfoList)
    {
        if (!empty($goodsInfoList)) {
            PindanGoodsSku::beginTransaction();
            foreach ($goodsInfoList as $item) {
                $goodsSkuId = $item["goods_sku_id"];
                $num = $item["num"];
                $sku = PindanGoodsSku::getRecord(["goods_sku_id" => $goodsSkuId]
                    , ["goods_sku_id", "inventory_rest", "total_amount_order", "total_amount_sold", "inventory_lock", "online_status", "price"]);

                if (!empty($sku["inventory_lock"]) && $sku["online_status"] == PindanGoodsSku::ONLINE_STATUS_ONLINE) {
                    //条件加上乐观锁
                    $rowCount = PindanGoodsSku::save([
                        "inventory_lock[-]" => $num,
                        "total_amount_order[+]" => $num,
                    ], ["goods_sku_id" => $goodsSkuId, "inventory_lock[>=]" => $num]);
                    if ($rowCount <= 0) {
                        PindanGoodsSku::rollback();
                        throw new GoodsException(["cus" => 31]);
                    }
                } else {
                    PindanGoodsSku::rollback();
                    throw new GoodsException(["cus" => 43]);
                }
                //0元单
                if (isset($item["payment"]) && $item["payment"] == 0) {
                    self::zeroOrder($item["marketing_id"], $item["num"], $sku, $goodsSkuId);
                }
            }
            Tcc::save(["status" => Tcc::STATUS_C1_SUCCESS,], ["tid" => $tid]);

            if (!PindanGoodsSku::commit()) {
                PindanGoodsSku::rollback();
                throw new GoodsException(["cus" => 44]);
            }
        }

        return self::TCC_RESULT_SUCCESS;
    }

    /**
     * 0元单处理
     * @param $marketingId
     * @param $num
     * @param $goodsSku
     * @param $goodsSkuId
     */
    private static function zeroOrder($marketingId, $num, $goodsSku, $goodsSkuId)
    {
        $marketing = Marketing::getRecord(["marketing_id" => $marketingId]);
        if (!empty($marketing) && $marketing["marketing_type"] == Marketing::MARKETING_TYPE_PINDAN) {
            $marketingPindan = MarketingPindan::getRecord(["marketing_id" => $marketingId]);
            if (!empty($marketingPindan)) {
                MarketingPindan::save(["participate_number[+]" => $num], ["marketing_pindan_id" => $marketingPindan["marketing_pindan_id"]]);
            }

            PindanGoodsSku::save(["total_amount_sold[+]" => $num], ["goods_sku_id" => $goodsSkuId]);
        }
    }

    /**
     * 下单 商品tcc cancel
     * @param $keys
     * @param $tid
     * @return bool
     * @throws GoodsException
     */
    public static function placeAnOrderCancel($keys, $tid)
    {
        $goodsInfoList = json_decode($keys, true);
        if (empty($goodsInfoList)) {
            throw new GoodsException(["cus" => 42]);
        }

        $isPindan = false;
        foreach ($goodsInfoList as $item) {
            if (empty($item["goods_sku_id"]) || empty($item["num"])) {
                throw new GoodsException(["cus" => 42]);
            }
            if (!empty($item["marketing_id"]) && !empty($item["marketing_type"]) && $item["marketing_type"] == Marketing::MARKETING_TYPE_PINDAN) {
                $isPindan = true;
            }
        }

        $tccInfo = self::getTccRecord($tid);
        //允许空cancel(没有try情况)
        //在cancel时候必须已经有try，如果没有就直接返回success
        if (empty($tccInfo)) {
            Tcc::save([
                "tid" => $tid,
                "goods_data" => $keys,
                "status" => Tcc::STATUS_C2_SUCCESS,
            ]);
            return self::TCC_RESULT_SUCCESS;
        }

        //有结果返回原来的结果，幂等
        if (!empty($tccInfo["status"]) && $tccInfo["status"] == Tcc::STATUS_C2_SUCCESS) {
            return self::TCC_RESULT_SUCCESS;
        }

        if ($isPindan) {
            return self::pindanGoodsCancel($tid, $tccInfo, $goodsInfoList);
        } else {
            return self::generalGoodsCancel($tid, $tccInfo, $goodsInfoList);
        }
    }

    /**
     * 普通商品库存 cancel
     * @param $tid
     * @param $tccInfo
     * @param $goodsInfoList
     * @return bool
     * @throws GoodsException
     */
    public static function generalGoodsCancel($tid, $tccInfo, $goodsInfoList)
    {
        if (!empty($goodsInfoList)) {
            GoodsSku::beginTransaction();
            foreach ($goodsInfoList as $item) {
                $goodsSkuId = $item["goods_sku_id"];
                $num = $item["num"];
                $sku = GoodsSku::getRecord(["goods_sku_id" => $goodsSkuId]
                    , ["goods_sku_id", "inventory_rest", "inventory_lock", "total_amount_order", "online_status"]);

                //T时候执行C2，条件加上乐观锁
                if ($tccInfo["status"] == Tcc::STATUS_T_SUCCESS) {
                    $rowCount = GoodsSku::save([
                        "inventory_lock[-]" => $num,
                        "inventory_rest[+]" => $num,
                    ], ["goods_sku_id" => $goodsSkuId, "inventory_lock[>=]" => $num]);
                    if ($rowCount <= 0) {
                        GoodsSku::rollback();
                        throw new GoodsException(["cus" => 31]);
                    }
                }

                //T时候执行C2，条件加上乐观锁
                if ($tccInfo["status"] == Tcc::STATUS_C1_SUCCESS) {
                    $rowCount = GoodsSku::save([
                        "total_amount_order[-]" => $num,
                        "inventory_rest[+]" => $num,
                    ], ["goods_sku_id" => $goodsSkuId, "total_amount_order[>=]" => $num]);
                    if ($rowCount <= 0) {
                        GoodsSku::rollback();
                        throw new GoodsException(["cus" => 32]);
                    }
                }
            }

            Tcc::save(["status" => Tcc::STATUS_C2_SUCCESS,], ["tid" => $tid]);

            if (!GoodsSku::commit()) {
                GoodsSku::rollback();
                throw new GoodsException(["cus" => 44]);
            }
        }

        return self::TCC_RESULT_SUCCESS;
    }

    /**
     * 拼单商品库存 cancel
     * @param $tid
     * @param $tccInfo
     * @param $goodsInfoList
     * @return bool
     * @throws GoodsException
     */
    public static function pindanGoodsCancel($tid, $tccInfo, $goodsInfoList)
    {
        if (!empty($goodsInfoList)) {
            PindanGoodsSku::beginTransaction();
            foreach ($goodsInfoList as $item) {
                $goodsSkuId = $item["goods_sku_id"];
                $num = $item["num"];
                $sku = PindanGoodsSku::getRecord(["goods_sku_id" => $goodsSkuId]
                    , ["goods_sku_id", "inventory_rest", "inventory_lock", "total_amount_order", "online_status"]);

                //T时候执行C2，条件加上乐观锁
                if ($tccInfo["status"] == Tcc::STATUS_T_SUCCESS) {
                    $rowCount = PindanGoodsSku::save([
                        "inventory_lock[-]" => $num,
                        "inventory_rest[+]" => $num,
                    ], ["goods_sku_id" => $goodsSkuId, "inventory_lock[>=]" => $num]);
                    if ($rowCount <= 0) {
                        PindanGoodsSku::rollback();
                        throw new GoodsException(["cus" => 31]);
                    }
                }

                //T时候执行C2，条件加上乐观锁
                if ($tccInfo["status"] == Tcc::STATUS_C1_SUCCESS) {
                    $rowCount = PindanGoodsSku::save([
                        "total_amount_order[-]" => $num,
                        "inventory_rest[+]" => $num,
                    ], ["goods_sku_id" => $goodsSkuId, "total_amount_order[>=]" => $num]);
                    if ($rowCount <= 0) {
                        PindanGoodsSku::rollback();
                        throw new GoodsException(["cus" => 32]);
                    }
                }
            }

            Tcc::save(["status" => Tcc::STATUS_C2_SUCCESS,], ["tid" => $tid]);

            if (!PindanGoodsSku::commit()) {
                PindanGoodsSku::rollback();
                throw new GoodsException(["cus" => 44]);
            }
        }

        return self::TCC_RESULT_SUCCESS;
    }

    /**
     * 取消订单，直接回退库存
     * @param $keys
     * @return bool
     * @throws GoodsException
     */
    public static function cancelConfirm($keys)
    {
        $goodsInfoList = json_decode($keys, true);
        if (empty($goodsInfoList)) {
            return self::TCC_RESULT_FAIL;
        }

        $isPindan = false;
        foreach ($goodsInfoList as $item) {
            if (empty($item["goods_sku_id"]) || empty($item["num"])) {
                return self::TCC_RESULT_FAIL;
            }
            $goodsSukParams = GoodsSkuId::getGoodsSkuIdParams($item["goods_sku_id"]);
            if (!empty($goodsSukParams)) {
                if (isset($goodsSukParams["table_tag"]) && ($goodsSukParams["table_tag"] == GoodsSkuId::TABLE_TAG_PINDAN)) {
                    $isPindan = true;
                }
            }
        }
        FileLog::info('goods_cancel_confirm_tcc', 'json decode error raw:' . $keys);
        if ($isPindan) {
            return self::pindanCancelConfirm($goodsInfoList);
        } else {
            return self::generalCancelConfirm($goodsInfoList);
        }
    }

    /**
     * 普通订单退库存
     * @param $goodsInfoList
     * @return bool
     * @throws GoodsException
     */
    private static function generalCancelConfirm($goodsInfoList)
    {
        if (!empty($goodsInfoList)) {
            GoodsSku::beginTransaction();
            foreach ($goodsInfoList as $item) {
                $goodsSkuId = $item["goods_sku_id"];
                $num = $item["num"];

                $params = [
                    "inventory_rest[+]" => $num,
                    "total_amount_order[-]" => $num,
                ];
                $rowCount = GoodsSku::save($params, ["goods_sku_id" => $goodsSkuId]);
                if ($rowCount <= 0) {
                    GoodsSku::rollback();
                    throw new GoodsException(["cus" => 32]);
                }
            }

            if (!GoodsSku::commit()) {
                GoodsSku::rollback();
            }
        }
        return self::TCC_RESULT_SUCCESS;
    }

    /**
     * 拼单活动取消订单退库存
     * @param $goodsInfoList
     * @return bool
     * @throws GoodsException
     */
    private static function pindanCancelConfirm($goodsInfoList)
    {
        if (!empty($goodsInfoList)) {
            PindanGoodsSku::beginTransaction();
            foreach ($goodsInfoList as $item) {
                $num = $item["num"];
                $params = [
                    "inventory_rest[+]" => $num,
                    "total_amount_order[-]" =>  $num,
                ];
                $rowCount = PindanGoodsSku::save($params, ["goods_sku_id" => $item["goods_sku_id"]]);
                if ($rowCount <= 0) {
                    PindanGoodsSku::rollback();
                    throw new GoodsException(["cus" => 47]);
                }
            }
            if (!PindanGoodsSku::commit()) {
                PindanGoodsSku::rollback();
            }
        }
        return self::TCC_RESULT_SUCCESS;
    }

    /**
     * 获取 tcc 数据记录
     * @param $tid
     * @return array
     */
    private static function getTccRecord($tid)
    {
        $res = Tcc::selectForUpdate("*", ['tid' => $tid]);
        $data = [];
        if (!empty($res)) {
            $data = $res[0];
        }
        return $data;
    }
}