<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);

namespace app\api\model\bargain;

use app\api\model\Goods as GoodsModel;
use app\api\model\bargain\Active as ActiveModel;
use app\api\model\bargain\TaskHelp as TaskHelpModel;
use app\api\service\User as UserService;
use app\api\service\Goods as GoodsService;
use app\api\service\bargain\Amount as AmountService;
use app\common\model\bargain\Task as TaskModel;
use app\common\library\helper;
use cores\exception\BaseException;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;

/**
 * 砍价任务模型
 * Class Task
 * @package app\api\model\bargain
 */
class Task extends TaskModel
{
    /**
     * 隐藏的字段
     * @var array
     */
    protected $hidden = [
        'peoples',
        'section',
        'is_delete',
        'store_id',
        'create_time',
        'update_time',
    ];

    /**
     * 我的砍价列表
     * @return mixed|\think\Paginator
     * @throws \cores\exception\BaseException
     * @throws \think\db\exception\DbException
     */
    public function getMyList()
    {
        // 当前登录用户ID
        $userId = UserService::getCurrentLoginUserId();
        // 砍价活动列表
        $list = $this->where('user_id', '=', $userId)
            ->where('is_delete', '=', 0)
            ->order(['create_time' => 'desc'])
            ->paginate(15);
        // 设置商品数据
        return !$list->isEmpty() ? GoodsService::setGoodsData($list, true, ['goods_images']) : $list;
    }

    /**
     * 获取砍价任务详情
     * @param int $taskId
     * @return static|null
     * @throws BaseException
     */
    public function getDetail(int $taskId): ?Task
    {
        // 砍价任务详情
        $detail = static::detail($taskId, ['user.avatar']);
        empty($detail) && throwError('砍价任务不存在');
        return $detail;
    }

    /**
     * 获取砍价任务的商品列表(用于订单结算)
     * @param int $taskId 砍价任务ID
     * @return array
     * @throws BaseException
     */
    public static function getTaskGoods(int $taskId): array
    {
        // 获取砍价任务详情
        $taskInfo = static::detail($taskId);
        // 验证砍价任务状态(是否允许购买)
        self::checkTaskStatus($taskInfo);
        // 获取砍价活动详情
        $activeInfo = (new ActiveModel)->getDetail($taskInfo['active_id']);
        // 获取商品详情
        $goods = (new GoodsModel)->getBasic($taskInfo['goods_id'], false);
        // 获取商品sku信息
        $goods['skuInfo'] = GoodsService::getSkuInfo($taskInfo['goods_id'], $taskInfo['goods_sku_id']);
        // 商品封面 (优先sku封面)
        $goods['goods_image'] = $goods['skuInfo']['goods_image'] ?: $goods['goods_image'];
        // 商品列表 (隐藏用不到的属性)
        $goodsList = [$goods->hidden(GoodsModel::getHidden(['content', 'images', 'goods_images']))];
        foreach ($goodsList as &$item) {
            // 商品单价
            $item['goods_price'] = $taskInfo['actual_price'];
            // 商品购买数量
            $item['total_num'] = 1;
            $item['goods_sku_id'] = $item['skuInfo']['goods_sku_id'];
            // 商品购买总金额
            $item['total_price'] = $taskInfo['actual_price'];
            // 砍价商品限购信息
            $goods['is_restrict'] = $activeInfo['is_restrict'];
            $goods['restrict_total'] = $activeInfo['restrict_total'];
        }
        return $goodsList;
    }

    /**
     * 验证砍价任务状态(是否允许购买)
     * @param self $taskInfo
     * @return bool
     * @throws BaseException
     */
    public static function checkTaskStatus(self $taskInfo): bool
    {
        if (empty($taskInfo) || $taskInfo['is_delete'] || !$taskInfo['status']) {
            throwError('砍价任务不存在或已结束');
        }
        if ($taskInfo['is_buy']) {
            throwError('该砍价商品已购买');
        }
        return true;
    }

    /**
     * 订单创建后将砍价任务结束
     * @param int $taskId 砍价任务ID
     * @return int|bool
     */
    public static function setTaskEnd(int $taskId)
    {
        // ps: 这里的save方法实际返回值是int, 所以不能强制类型bool
        return (new static)->where('task_id', '=', $taskId)->save(['status' => 0]);
    }

    /**
     * 获取用户是否正在参与改砍价活动,如果已参与则返回task_id
     * @param int $activeId
     * @param int $userId
     * @return bool|int
     */
    public static function getHandByUser(int $activeId, int $userId)
    {
        $taskId = (new static)->where('active_id', '=', $activeId)
            ->where('user_id', '=', $userId)
            ->where('end_time', '>', time())
            ->where('status', '=', 1)
            ->where('is_delete', '=', 0)
            ->value('task_id');
        return $taskId ?: false;
    }

    /**
     * 新增砍价任务
     * @param int $activeId
     * @param string $goodsSkuId
     * @return false|mixed
     * @throws BaseException
     */
    public function partake(int $activeId, string $goodsSkuId)
    {
        // 当前用户ID
        $userId = UserService::getCurrentLoginUserId();
        // 获取活动详情
        $active = $this->getActiveDetail($activeId);
        // 验证能否创建砍价任务
        if (!$this->onVerify($active, $userId)) {
            return false;
        }
        // 获取商品详情
        $goods = GoodsModel::detail($active['goods_id']);
        // 商品sku信息
        $goods['skuInfo'] = GoodsService::getSkuInfo($goods['goods_id'], $goodsSkuId);
        // 事务处理
        return $this->transaction(function () use ($userId, $active, $goodsSkuId, $goods) {
            // 创建砍价任务
            $this->add($userId, $active, $goodsSkuId, $goods);
            // 发起人自砍一刀
            $active['is_self_cut'] && $this->onCutEvent($userId, true);
            return true;
        });
    }

    /**
     * 帮砍一刀
     * @return false|mixed
     * @throws BaseException
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function helpCut()
    {
        // 当前用户ID
        $userId = UserService::getCurrentLoginUserId();
        // 验证每日帮砍的次数限制
        TaskHelpModel::checkHelpCutLimit($userId);
        // 当前是否已砍
        if (TaskHelpModel::isCut((int)$this['task_id'])) {
            $this->error = '您已参与砍价,请不要重复操作';
            return false;
        }
        // 帮砍一刀事件
        return $this->transaction(function () use ($userId) {
            return $this->onCutEvent($userId, $this->isCreater($this));
        });
    }

    /**
     * 砍一刀的金额
     * @return string
     */
    public function getCutMoney(): string
    {
        return (string)$this['section'][$this['cut_people']];
    }

    /**
     * 帮砍一刀事件
     * @param int $userId
     * @param bool $isCreater
     * @return bool|false
     */
    private function onCutEvent(int $userId, bool $isCreater = false): bool
    {
        // 砍价金额
        $cutMoney = $this->getCutMoney();
        // 砍价助力记录
        $model = new TaskHelpModel;
        $model->add($this, $userId, $cutMoney, $isCreater);
        // 实际购买金额
        $actualPrice = helper::bcsub($this['actual_price'], $cutMoney);
        // 更新砍价任务信息
        return $this->save([
            'cut_people' => ['inc', 1],
            'cut_money' => ['inc', $cutMoney],
            'actual_price' => $actualPrice,
            'is_floor' => helper::bcequal($actualPrice, $this['floor_price']),
        ]);
    }

    /**
     * 创建砍价任务记录
     * @param int $userId
     * @param ActiveModel $active 砍价活动详情
     * @param string $goodsSkuId 指定的商品sku
     * @param GoodsModel $goods 商品详情
     * @return void
     * @throws \Exception
     */
    private function add(int $userId, ActiveModel $active, string $goodsSkuId, GoodsModel $goods): void
    {
        // 分配砍价金额区间
        $section = $this->calcBargainSection(
            $goods['skuInfo']['goods_price'],
            $active['floor_price'],
            $active['peoples']
        );
        // 新增记录
        $this->save([
            'active_id' => $active['active_id'],
            'user_id' => $userId,
            'goods_id' => $active['goods_id'],
            'goods_sku_id' => $goodsSkuId,
            'goods_price' => $goods['skuInfo']['goods_price'],
            'floor_price' => $active['floor_price'],
            'peoples' => $active['peoples'],
            'cut_people' => 0,
            'section' => $section,
            'cut_money' => 0.00,
            'actual_price' => $goods['skuInfo']['goods_price'],
            'end_time' => time() + ($active['expiryt_time'] * 3600),
            'is_buy' => 0,
            'status' => 1,
            'store_id' => static::$storeId,
        ]);
    }

    /**
     * 砍价任务标记为已购买
     * @return bool|false
     */
    public function setIsBuy(): bool
    {
        return $this->save(['is_buy' => 1]);
    }

    /**
     * 分配砍价金额区间
     * @param string $goodsPrice
     * @param string $floorPrice
     * @param int $peoples
     * @return array|mixed
     * @throws \Exception
     */
    private function calcBargainSection(string $goodsPrice, string $floorPrice, int $peoples)
    {
        $amount = (float)helper::bcsub($goodsPrice, $floorPrice);
        $AmountService = new AmountService($amount, $peoples);
        return $AmountService->handle()['items'];
    }

    /**
     * 当前是否为发起人
     * @param self $taskInfo 砍价任务
     * @return bool
     * @throws BaseException
     */
    public function isCreater(self $taskInfo): bool
    {
        if (!$userId = UserService::getCurrentLoginUserId(false)) {
            return false;
        }
        return $userId == $taskInfo['user_id'];
    }

    /**
     * 当前是否为发起人
     * @param int $taskId 砍价任务ID
     * @return bool
     * @throws BaseException
     */
    public function isCut(int $taskId): bool
    {
        return TaskHelpModel::isCut($taskId);
    }

    /**
     * 获取活动详情
     * @param int $activeId
     * @return Active|array|null
     * @throws BaseException
     */
    private function getActiveDetail(int $activeId)
    {
        // 获取活动详情
        $ActiveModel = new ActiveModel;
        return $ActiveModel->getDetail($activeId);
    }

    /**
     * 验证能否创建砍价任务
     * @param $active
     * @param int $userId
     * @return bool
     */
    private function onVerify($active, int $userId): bool
    {
        // 活动是否开始
        if (!$active['is_start']) {
            $this->error = '很抱歉,当前砍价活动未开始';
            return false;
        }
        // 活动是否结束
        if ($active['is_end']) {
            $this->error = '很抱歉,当前砍价活动已结束';
            return false;
        }
        // 判断当前用户是否已参加
        $taskId = static::getHandByUser($active['active_id'], $userId);
        if ($taskId !== false && $taskId > 0) {
            $this->error = '很抱歉,当前砍价活动您已参加,无需重复参与';
            return false;
        }
        return true;
    }
}