/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许，不可做商业用途！
 *
 * 版权所有，侵权必究！
 */

package com.yami.shop.admin.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yami.shop.bean.model.Category;
import com.yami.shop.common.annotation.SysLog;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.security.admin.util.SecurityUtils;
import com.yami.shop.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import com.yami.shop.common.response.ServerResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 分类管理相关的控制器类，主要负责处理商品分类的各种操作，包括获取分类信息、保存、更新、删除分类以及获取分类列表等功能，
 * 同时在部分操作方法上添加了权限控制和操作日志记录功能，以确保系统的安全性和可追溯性。
 *
 * @author lgh
 *
 */
@RestController
@RequestMapping("/prod/category")
public class CategoryController {

    // 注入CategoryService，用于与商品分类相关的业务逻辑处理，例如查询、保存、更新、删除分类等操作。
    @Autowired
    private CategoryService categoryService;

    /**
     * 获取用于菜单页面展示的分类列表信息的方法，通过调用CategoryService的相关方法，
     * 获取当前店铺（根据当前登录用户所属店铺ID确定）下的分类列表信息，并返回给前端展示。
     * 同时，通过@PreAuthorize注解进行权限控制，只有具备"prod:category:page"权限的用户才能访问此方法。
     *
     * @return 返回包含分类信息的ServerResponseEntity对象，若获取成功则响应体中包含分类列表数据，否则返回相应错误信息。
     */
    @GetMapping("/table")
    @PreAuthorize("@pms.hasPermission('prod:category:page')")
    public ServerResponseEntity<List<Category>> table() {
        // 调用CategoryService的tableCategory方法，传入当前登录用户所属的店铺ID，获取用于菜单页面展示的分类列表信息。
        List<Category> categoryMenuList = categoryService.tableCategory(SecurityUtils.getSysUser().getShopId());
        // 将获取到的分类列表信息封装在成功的响应实体中返回给前端。
        return ServerResponseEntity.success(categoryMenuList);
    }

    /**
     * 根据分类ID获取分类详细信息的方法，通过传入的分类ID，调用CategoryService的getById方法从数据库中查询对应的分类对象，
     * 并将其封装在成功的响应实体中返回给前端。此方法没有添加权限控制注解，需根据实际业务需求确认是否需要添加相应权限控制。
     *
     * @param categoryId 要获取信息的分类的唯一标识符。
     * @return 返回包含分类详细信息的ServerResponseEntity对象，若获取成功则响应体中包含对应的分类对象，否则返回相应错误信息。
     */
    @GetMapping("/info/{categoryId}")
    public ServerResponseEntity<Category> info(@PathVariable("categoryId") Long categoryId) {
        Category category = categoryService.getById(categoryId);
        return ServerResponseEntity.success(category);
    }

    /**
     * 保存商品分类信息的方法，接收一个Category对象作为请求体，代表要保存的分类信息。
     * 在保存前设置分类所属的店铺ID（通过当前登录用户所属店铺ID确定）以及记录时间等信息，
     * 然后检查分类名称是否已存在（在当前店铺下），若不存在则调用CategoryService的saveCategory方法将分类信息保存到数据库中，
     * 最后返回成功的响应结果给前端。通过@PreAuthorize注解进行权限控制，只有具备"prod:category:save"权限的用户才能访问此方法，
     * 同时使用@SysLog注解记录此操作的日志信息，方便后续查看操作记录。
     *
     * @param category 包含分类信息的请求体对象，如分类名称、上级分类ID等属性。
     * @return 返回表示操作成功的ServerResponseEntity对象，由于这里只是执行保存操作，无需返回具体数据，所以返回的是Void类型的成功响应。
     */
    @SysLog("保存分类")
    @PostMapping
    @PreAuthorize("@pms.hasPermission('prod:category:save')")
    public ServerResponseEntity<Void> save(@RequestBody Category category) {
        category.setShopId(SecurityUtils.getSysUser().getShopId());
        category.setRecTime(new Date());
        // 通过LambdaQueryWrapper构建查询条件，查询在当前店铺下分类名称与要保存的分类名称相同的分类记录，用于判断名称是否已存在。
        Category categoryName = categoryService.getOne(new LambdaQueryWrapper<Category>()
               .eq(Category::getCategoryName, category.getCategoryName())
               .eq(Category::getShopId, category.getShopId()));
        if (Objects.nonNull(categoryName)) {
            throw new YamiShopBindException("类目名称已存在！");
        }
        categoryService.saveCategory(category);
        return ServerResponseEntity.success();
    }

    /**
     * 更新商品分类信息的方法，接收一个Category对象作为请求体，代表要更新的分类信息。
     * 首先设置分类所属的店铺ID（通过当前登录用户所属店铺ID确定），然后进行一系列合法性校验，
     * 如检查分类的上级是否为自身、分类名称是否已存在（除自身外）等，若校验通过且满足其他相关条件（如从下线改成正常时上级分类状态的判断），
     * 则调用CategoryService的updateCategory方法将更新后的分类信息保存到数据库中，最后返回成功的响应结果给前端。
     * 通过@PreAuthorize注解进行权限控制，只有具备"prod:category:update"权限的用户才能访问此方法，
     * 同时使用@SysLog注解记录此操作的日志信息，方便后续查看操作记录。
     *
     * @param category 包含更新后的分类信息的请求体对象，如分类名称、上级分类ID、状态等属性。
     * @return 返回表示操作成功的ServerResponseEntity对象，若更新成功则响应体中包含成功信息，若校验不通过则返回相应的错误提示信息。
     */
    @SysLog("更新分类")
    @PutMapping
    @PreAuthorize("@pms.hasPermission('prod:category:update')")
    public ServerResponseEntity<String> update(@RequestBody Category category) {
        category.setShopId(SecurityUtils.getSysUser().getShopId());
        if (Objects.equals(category.getParentId(), category.getCategoryId())) {
            return ServerResponseEntity.showFailMsg("分类的上级不能是自己本身");
        }
        // 通过LambdaQueryWrapper构建查询条件，查询在当前店铺下分类名称与要更新的分类名称相同且ID不同（排除自身）的分类记录，用于判断名称是否已存在。
        Category categoryName = categoryService.getOne(new LambdaQueryWrapper<Category>()
               .eq(Category::getCategoryName, category.getCategoryName())
               .eq(Category::getShopId, category.getShopId())
               .ne(Category::getCategoryId, category.getCategoryId()));
        if (categoryName!= null) {
            throw new YamiShopBindException("类目名称已存在！");
        }
        Category categoryDb = categoryService.getById(category.getCategoryId());
        // 如果从下线（状态为0）改成正常（状态为1），则需要判断上级的状态，确保上级分类存在且为正常状态。
        if (Objects.equals(categoryDb.getStatus(), 0) && Objects.equals(category.getStatus(), 1) &&!Objects.equals(category.getParentId(), 0L)) {
            Category parentCategory = categoryService.getOne(new LambdaQueryWrapper<Category>()
                   .eq(Category::getCategoryId, category.getParentId()));
            if (Objects.isNull(parentCategory) || Objects.equals(parentCategory.getStatus(), 0)) {
                // 修改失败，上级分类不存在或者不为正常状态
                throw new YamiShopBindException("修改失败，上级分类不存在或者不为正常状态");
            }
        }
        categoryService.updateCategory(category);
        return ServerResponseEntity.success();
    }

    /**
     * 删除商品分类信息的方法，根据传入的分类ID，首先检查该分类是否还有子分类，若有子分类则不允许删除，返回相应的错误提示信息，
     * 若无子分类则调用CategoryService的deleteCategory方法将分类信息从数据库中删除，最后返回成功的响应结果给前端。
     * 通过@PreAuthorize注解进行权限控制，只有具备"prod:category:delete"权限的用户才能访问此方法，
     * 同时使用@SysLog注解记录此操作的日志信息，方便后续查看操作记录。
     *
     * @param categoryId 要删除的分类的唯一标识符。
     * @return 返回表示操作成功的ServerResponseEntity对象，若删除成功则响应体中包含成功信息，若存在子分类则返回相应的错误提示信息。
     */
    @SysLog("删除分类")
    @DeleteMapping("/{categoryId}")
    @PreAuthorize("@pms.hasPermission('prod:category:delete')")
    public ServerResponseEntity<String> delete(@PathVariable("categoryId") Long categoryId) {
        // 通过LambdaQueryWrapper构建查询条件，统计以当前分类ID为上级分类的子分类数量，若数量大于0则说明还有子分类，不允许删除。
        if (categoryService.count(new LambdaQueryWrapper<Category>().eq(Category::getParentId, categoryId)) > 0) {
            return ServerResponseEntity.showFailMsg("请删除子分类，再删除该分类");
        }
        categoryService.deleteCategory(categoryId);
        return ServerResponseEntity.success();
    }

    /**
     * 获取所有满足一定条件的分类列表信息的方法，通过构建LambdaQueryWrapper查询条件，
     * 查询等级小于等于2且属于当前店铺（根据当前登录用户所属店铺ID确定）的分类信息，并按照顺序排序，
     * 最后将查询到的分类列表信息封装在成功的响应实体中返回给前端。此方法没有添加权限控制注解，需根据实际业务需求确认是否需要添加相应权限控制。
     *
     * @return 返回包含分类列表信息的ServerResponseEntity对象，若查询成功则响应体中包含符合条件的分类列表数据，否则返回相应错误信息。
     */
    @GetMapping("/listCategory")
    public ServerResponseEntity<List<Category>> listCategory() {

        return ServerResponseEntity.success(categoryService.list(new LambdaQueryWrapper<Category>()
               .le(Category::getGrade, 2)
               .eq(Category::getShopId, SecurityUtils.getSysUser().getShopId())
               .orderByAsc(Category::getSeq)));
    }

    /**
     * 获取所有用于产品展示的分类列表信息的方法，通过调用CategoryService的treeSelect方法，
     * 获取指定店铺（根据当前登录用户所属店铺ID确定）下用于产品展示的分类列表信息（这里可能是构建树形结构的分类数据，具体取决于service层实现），
     * 最后将查询到的分类列表信息封装在成功的响应实体中返回给前端。此方法没有添加权限控制注解，需根据实际业务需求确认是否需要添加相应权限控制。
     *
     * @return 返回包含分类列表信息的ServerResponseEntity对象，若查询成功则响应体中包含符合条件的分类列表数据，否则返回相应错误信息。
     */
    @GetMapping("/listProdCategory")
    public ServerResponseEntity<List<Category>> listProdCategory() {
        List<Category> categories = categoryService.treeSelect(SecurityUtils.getSysUser().getShopId(), 2);
        return ServerResponseEntity.success(categories);
    }
}