<?php

namespace app\traits\spu;

use app\exception\RepositoryException;
use app\model\mall\SpuLimitTime;
use app\model\Sku;
use app\model\Spu;
use app\model\Spu as SpuModel;
use app\repository\SpuRepository;
use Exception;
use think\facade\Db;
use think\facade\Log;
use think\Model;

trait LimitTimeTrait
{
    /**
     * 添加限时折扣
     *
     * @param  array  $data  基础信息
     * @param  array  $skus  规格信息
     * @return Model
     * @throws RepositoryException
     */
    public function addLimitTime(array $data, array $skus): Model
    {
        Db::startTrans();
        try {
            $now = date('Y-m-d H:i:s');

            $spuFields = ['id', 'spu_type_id', 'activity_id', 'is_activity', 'activity_type', 'multi_spec'];
            if (!$spuInfo = SpuModel::findById($data['spu_id'], $spuFields)) {
                throw new RepositoryException('商品不存在');
            }

            if ($spuInfo['activity_type'] != SpuModel::TYPE_NORMAL) {
                throw new RepositoryException('该商品已参与其他活动');
            }

            if ($spuInfo['multi_spec'] == SpuModel::COMMON_OFF) {
                $skus = [$skus];
            }

            $multiSpec = (bool) $spuInfo['multi_spec'];

            $data['created_at'] = $now;
            $data['updated_at'] = $now;
            $limitTime          = SpuLimitTime::create($data);

            // 创建活动SKU
            foreach ($skus as &$sku) {
                unset($sku['id']);
                unset($sku['coding']);
                $sku['spu_id'] = $data['spu_id'];
            }
            $skuData = $this->handleSku($limitTime['id'], $skus, [], SpuLimitTime::TYPE, $multiSpec);

            (new Sku())->saveAll($skuData['data']);

            $limitTime->save([
                'spec' => json_encode($skuData['spec'], JSON_UNESCAPED_UNICODE)
            ]);

            // 修改基础商品
            $spuInfo->save([
                'activity_id'   => $limitTime['id'],
                'activity_type' => SpuLimitTime::TYPE,
                'is_activity'   => SpuLimitTime::COMMON_ON,
            ]);

            Db::commit();
            return $limitTime;
        } catch (RepositoryException $e) {
            // 回滚事务
            Db::rollback();
            throw $e;
        } catch (Exception $e) {
            SpuRepository::log($e->getMessage(), $e);
            // 回滚事务
            Db::rollback();
            throw new RepositoryException('限时折扣创建失败');
        }
    }

    /**
     * 编辑限时折扣
     *
     * @param  int  $id  限时折扣ID
     * @param  array  $data  基础信息
     * @param  array  $skus  规格信息
     * @return Model
     * @throws RepositoryException
     */
    public function editLimitTime(int $id, array $data, array $skus): Model
    {
        Db::startTrans();
        try {
            if (!$limitTime = SpuLimitTime::findById($id)) {
                throw new RepositoryException('记录不存在');
            }

            $spuFields = ['id', 'spu_type_id', 'activity_id', 'activity_type', 'is_activity', 'multi_spec'];
            if (!$spuInfo = SpuModel::findById($limitTime['spu_id'], $spuFields)) {
                throw new RepositoryException('商品不存在');
            }

            if ($spuInfo['activity_type'] != SpuModel::TYPE_NORMAL && $spuInfo['activity_id'] != $id) {
                throw new RepositoryException('该商品已参与其他活动');
            }

            $multiSpec = (bool) $spuInfo['multi_spec'];

            if (!$multiSpec) {
                $skus = [$skus];
            }

            $data['updated_at'] = date('Y-m-d H:i:s');

            $originalSkuList = Sku::where('spu_activity_id', $id)
                ->where('spu_id', 0)
                ->where('type', SpuLimitTime::TYPE)
                ->where('enable', Sku::COMMON_ON)
                ->order('sort', 'asc')
                ->order('id', 'asc')
                ->select()
                ->toArray();

            $skuData = $this->handleSku($id, $skus, array_column($originalSkuList, 'id'), SpuLimitTime::TYPE, $multiSpec);

            (new Sku())->saveAll($skuData['data']);
            (new Sku())->where('id', 'in', $skuData['delete'])->delete();

            $data['spec']        = json_encode($skuData['spec'], JSON_UNESCAPED_UNICODE);
            $data['is_restore']  = SpuLimitTime::COMMON_OFF;
            $data['restored_at'] = null;
            $limitTime->save($data);

            // 修改基础商品
            $spuInfo->save([
                'activity_id'   => $id,
                'activity_type' => SpuLimitTime::TYPE,
                'is_activity'   => SpuModel::COMMON_ON,
            ]);

            Db::commit();
            return $limitTime;
        } catch (RepositoryException $e) {
            // 回滚事务
            Db::rollback();
            throw $e;
        } catch (Exception $e) {
            SpuRepository::log($e->getMessage(), $e);
            // 回滚事务
            Db::rollback();
            throw new RepositoryException('限时折扣编辑失败');
        }
    }

    /**
     * 还原限时折扣
     * @param  array  $ids  为空则还原所有过期的活动
     * @param  bool  $delete  是否删除
     * @return bool
     */
    public function restoreLimitTime(array $ids = [], bool $delete = false): bool
    {
        Db::startTrans();
        try {
            $where   = [];
            $where[] = ['is_restore', '=', SpuLimitTime::COMMON_OFF];
            $now     = date('Y-m-d H:i:s');
            if (!empty($ids)) {
                // 还原指定ID的活动
                $where[] = ['id', 'in', $ids];
            } else {
                // 还原所有过期活动
                $where[] = ['end_at', '<', $now];
            }
            $spuIds = SpuLimitTime::where($where)->column('spu_id');
            if (empty($spuIds)) {
                return true;
            }

            Spu::whereIn('id', $spuIds)
                ->where('is_activity', Spu::COMMON_ON)
                ->where('activity_type', SpuLimitTime::TYPE)
                ->save([
                    'is_activity'   => Spu::COMMON_OFF,
                    'activity_type' => Spu::TYPE_NORMAL,
                    'activity_id'   => 0,
                ]);

            if ($delete) {
                SpuLimitTime::whereIn('id', $ids)->whereNull('deleted_at')->save(
                    [
                        'is_restore'  => SpuLimitTime::COMMON_ON,
                        'restored_at' => $now,
                        'deleted_at'  => $now,
                    ]
                );
                Sku::whereIn('spu_activity_id', $ids)->save(['deleted_at' => $now]);
            } else {
                SpuLimitTime::where($where)->save(
                    ['is_restore' => SpuLimitTime::COMMON_ON, 'restored_at' => $now]
                );
            }

            Db::commit();
            return true;
        } catch (Exception $e) {
            Db::rollback();
            Log::error('还原限时折扣失败 msg:'.$e->getMessage());
            return false;
        }
    }

    /**
     * 重启活动
     * @param  int  $id  活动ID
     * @return bool
     * @throws RepositoryException
     */
    public function restart(int $id): bool
    {
        Db::startTrans();
        try {
            $where   = [];

            $item = SpuLimitTime::findById($id);
            if (!$item) {
                throw new RepositoryException('活动不存在');
            }

            // 检测所选商品 是否存在参加其他活动
            $spu = Spu::findById($item['spu_id'], ['id', 'activity_id', 'is_activity', 'activity_type']);
            if (!$spu || $spu['deleted_at']) {
                throw new RepositoryException('商品不存在');
            }

            if ($spu['activity_id'] > 0 && $spu['activity_id'] != $item['spu_id']) {
                throw new RepositoryException('该商品已参加其他活动');
            }

            $item->save(
                ['is_restore' => SpuLimitTime::COMMON_OFF, 'restored_at' => null]
            );

            $spu->save([
                'is_activity' => Spu::COMMON_ON,
                'activity_type' => Spu::TYPE_LIMIT_TIME,
                'activity_id' => $id,
            ]);

            Db::commit();
            return true;
        } catch (RepositoryException $e) {
            Db::rollback();
            throw $e;
        } catch (Exception $e) {
            Db::rollback();
            Log::error('还原限时折扣失败 msg:'.$e->getMessage());
            return false;
        }
    }
}