package com.vctgo.system.controller;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.vctgo.common.core.constant.HttpStatus;
import com.vctgo.common.core.domain.R;
import com.vctgo.common.core.exception.ServiceException;
import com.vctgo.common.core.utils.poi.ExcelUtil;
import com.vctgo.common.core.web.controller.BaseController;
import com.vctgo.common.core.web.domain.AjaxResult;
import com.vctgo.common.log.annotation.Log;
import com.vctgo.common.log.enums.BusinessType;
import com.vctgo.common.security.annotation.InnerAuth;
import com.vctgo.common.security.annotation.Logical;
import com.vctgo.common.security.annotation.RequiresPermissions;
import com.vctgo.common.security.utils.SecurityUtils;
import com.vctgo.system.api.domain.SysClassSetting;
import com.vctgo.system.api.domain.SysLocationSetting;
import com.vctgo.system.api.domain.dto.ObserveChildhoodDto;
import com.vctgo.system.api.domain.dto.ObserveChildhoodNewDto;
import com.vctgo.system.api.domain.dto.ToySysClassSettingDto;
import com.vctgo.system.domain.dto.SysClassSettingDto;
import com.vctgo.system.domain.vo.SysDownLocation;
import com.vctgo.system.service.ISysClassSettingService;
import com.vctgo.system.service.ISysLocationSettingService;
import com.vctgo.system.service.ISysReClassUserService;
import com.vctgo.system.service.ISysReLocationClassService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 班级设置Controller
 *
 * @author caesar
 * @date 2023-05-30
 */
@Api(tags = "班级设置")
@RestController
@RequestMapping("/sysClassSetting")
public class SysClassSettingController extends BaseController {

    @Autowired
    private ISysClassSettingService sysClassSettingService;

    @Autowired
    private ISysReClassUserService sysReClassUserService;

    @Autowired
    private ISysReLocationClassService sysReLocationClassService;


    @Autowired
    private ISysLocationSettingService sysLocationSettingService;

    /**
     * 查询班级设置列表
     */
    @ApiOperation(value = "查询班级设置列表")
    @RequiresPermissions(value = {"system:class_setting:page","system:class_manage:page"}, logical = Logical.OR)
    @GetMapping("/page")
    public AjaxResult list(SysClassSetting sysClassSetting) {
        IPage<SysClassSetting> list = sysClassSettingService.selectSysClassSettingPage(sysClassSetting);
        return AjaxResult.success(list);
    }

    /**
     * 查询班级设置列表
     */
    @ApiOperation(value = "查询班级设置列表")
    @GetMapping("/book/page")
    public AjaxResult bookPage(SysClassSetting sysClassSetting) {
        List<SysClassSetting> list = sysClassSettingService.selectSysClassSettingBookList(sysClassSetting);
        return AjaxResult.success(list);
    }

    /**
     * 查询班级设置列表
     */
    @ApiOperation(value = "查询班级设置列表")
    @GetMapping("/selectAreaClassList")
    public AjaxResult selectAreaClassList(SysClassSetting sysClassSetting) {
        List<SysClassSettingDto> list = sysClassSettingService.selectAreaClassList(sysClassSetting);
        return AjaxResult.success(list);
    }

    /**
     * 查询班级设置列表
     */
    @ApiOperation(value = "查询当前租户的班级列表")
    @GetMapping("/external/list")
    public AjaxResult getClassList(@SpringQueryMap SysClassSetting sysClassSetting) {
        List<SysClassSetting> list = sysClassSettingService.selectSysClassSettingList(sysClassSetting);
        return AjaxResult.success(list);
    }

    /**
     * 获取班级设置详细信息
     */
    @InnerAuth
    @ApiOperation(value = "获取班级设置详细信息")
    @GetMapping(value = "/external/{id}")
    public AjaxResult getClassInfo(@PathVariable("id") Long id) {
        return success(sysClassSettingService.selectSysClassSettingById(id));
    }

    /**
     * 全量查询年份列表
     */
    @ApiOperation(value = "全量查询年份列表")
    @RequiresPermissions(value = {"system:class_setting:list","system:class_manage:list"}, logical = Logical.OR)
    @GetMapping("/enrollmentYearList")
    public AjaxResult getEnrollmentYearList() {
        return AjaxResult.success(sysClassSettingService.getEnrollmentYearList());
    }

    /**
     * 导出班级设置列表
     */
    @ApiOperation(value = "导出班级设置列表")
    @RequiresPermissions(value = {"system:class_setting:export","system:class_manage:export"}, logical = Logical.OR)
    @Log(title = "班级设置", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, SysClassSetting sysClassSetting) {
        List<SysClassSetting> list = sysClassSettingService.selectSysClassSettingList(sysClassSetting);
        ExcelUtil<SysClassSetting> util = new ExcelUtil<>(SysClassSetting.class);
        util.exportExcel(response, list, "班级设置数据");
    }

    /**
     * 查询年级列表
     */
    @ApiOperation(value = "查询年级列表")
    @RequiresPermissions("system:class_setting:grade:list")
    @GetMapping("/gradeIn/list")
    public AjaxResult gradeInList() {
        return AjaxResult.success(sysClassSettingService.gradeInList());
    }

    /**
     * 通过年级获取对应的班级列表
     */
    @ApiOperation(value = "通过年级获取对应的班级列表")
    @RequiresPermissions("system:class_setting:select:grade")
    @GetMapping("/selectClassListByGradeIn/{gradeIn}")
    public AjaxResult selectClassListByGradeIn(@PathVariable String gradeIn) {
        return AjaxResult.success(sysClassSettingService.selectClassListByGradeIn(gradeIn));
    }

    /**
     * 查询所有的班级列表
     */
    @ApiOperation(value = "查询所有的班级列表")
    @RequiresPermissions("system:class_setting:classList")
    @GetMapping("/classList")
    public AjaxResult classList() {
        return AjaxResult.success(sysClassSettingService.selectSysClassSettingList(SysClassSetting.builder().isDeleted("0").build()));
    }

    /**
     * 根据班级绑定人员筛选班级列表
     */
    @GetMapping("/classList/bindUserId")
    public AjaxResult classListByBindInfo() {
        List<SysClassSetting> classSettingList = sysClassSettingService.selectSysClassListByUserId(SecurityUtils.getUserId());
        return AjaxResult.success(classSettingList);
    }

    /**
     * 查询所有的班级列表
     */
    @ApiOperation(value = "查询所有的班级列表")
    @GetMapping("/class/list")
    public AjaxResult discardDuplicatesClassList() {
        List<SysClassSetting> list = sysClassSettingService.selectSysClassSettingList(SysClassSetting.builder().isDeleted("0").build());
        //去除重复的班级
        list = list.stream().filter(distinctByKey(obj->obj.getGradeIn()+"|"+obj.getClassNumber())).collect(Collectors.toList());
        //由于班级编号为字符串类型，需要按班级排序
        list = list.stream().sorted(Comparator.comparingInt(obj -> Integer.parseInt(obj.getClassNumber()))).collect(Collectors.toList());
        return AjaxResult.success(list);
    }

    private static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    /**
     * 获取班级设置详细信息
     */
    @ApiOperation(value = "获取班级树形结构")
    @RequiresPermissions(value = {"system:class_setting:tree","system:class_manage:tree"}, logical = Logical.OR)
    @GetMapping(value = "/tree/class/list")
    public AjaxResult getTreeClassList() {
        return AjaxResult.success(sysClassSettingService.selectSysTreeClassInfo());
    }

    /**
     * 获取当前幼儿园的年级班级学生树
     */
    @ApiOperation(value = "获取获取当前幼儿园的年级班级学生树形结构")
    @GetMapping(value = "/tree/classChild/list")
    public AjaxResult getTreeClassChildList(@SpringQueryMap ObserveChildhoodNewDto observeChildhoodNewDto) {
        return AjaxResult.success(sysClassSettingService.getTreeClassChildList(observeChildhoodNewDto));
    }


    /**
     * 获取班级设置详细信息
     */
    @ApiOperation(value = "获取班级设置详细信息")
    @RequiresPermissions(value = {"system:class_setting:query","system:class_manage:query"}, logical = Logical.OR)
    @GetMapping(value = "/{id}")
    public AjaxResult getInfo(@PathVariable("id") Long id) {
        return success(sysClassSettingService.selectSysClassSettingById(id));
    }

    /**
     * 获取班级设置详细信息
     */
    @GetMapping(value = "/weekPlan/{id}")
    public AjaxResult getInfoById(@PathVariable("id") Long id) {
        return success(sysClassSettingService.selectSysClassSettingById(id));
    }

    /**
     * 新增班级设置
     */
    @ApiOperation(value = "新增班级设置")
    @RequiresPermissions(value = {"system:class_setting:add","system:class_manage:add"}, logical = Logical.OR)
    @Log(title = "班级设置", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody List<SysClassSetting> sysClassSettingList) {
        //校验页面传进的多条数据是否存在于数据库（每条数据与数据库数据做比较）
        for (SysClassSetting checkSysClassSetting : sysClassSettingList) {
            String classAlias = checkSysClassSetting.getClassAlias();
            if(!(Boolean)checkClassNumber(checkSysClassSetting).get("data")){
                throw new ServiceException("相同入学年份、同一年级下，班级编号不可重复");
            }
            if(StringUtils.isNotEmpty(checkSysClassSetting.getClassAlias()) && !(Boolean)checkClassAlias(checkSysClassSetting).get("data")){
                throw new ServiceException("相同入学年份、同一年级下，班级别名不可重复");
            }
            checkSysClassSetting.setClassAlias(classAlias);
        }
        //校验前端页面传进的多条数据之间是否重复（前端的每条数据之间两两比较）
        Map<String, Long> groupByClassNumber = sysClassSettingList.stream().collect(Collectors.groupingBy(obj-> obj.getEnrollmentYear()+"|"+obj.getGradeIn()+"|"+obj.getClassNumber(), Collectors.counting()));
        Set set1 = groupByClassNumber.keySet();
        for (Object o : set1) {
//            System.out.println(o+"："+groupByClassNumber.get(o));
            if(groupByClassNumber.get(o) > 1){
                throw new ServiceException("相同入学年份、同一年级下，班级编号不可重复");
            }
        }
        Map<String, Long> groupByClassAlias = sysClassSettingList.stream().filter(obj-> StringUtils.isNotEmpty(obj.getClassAlias()))
                .collect(Collectors.groupingBy(obj-> obj.getEnrollmentYear()+"|"+obj.getGradeIn()+"|"+obj.getClassNumber(), Collectors.counting()));
        Set set2 = groupByClassAlias.keySet();
        for (Object o : set2) {
//            System.out.println(o+""+groupByClassAlias.get(o));
            if(groupByClassAlias.get(o) > 1){
                throw new ServiceException("相同入学年份、同一年级下，班级别名不可重复");
            }
        }
        return toAjax(sysClassSettingService.insertSysClassSetting(sysClassSettingList));
    }

    /**
     * 修改班级设置
     */
    @ApiOperation(value = "修改班级设置")
    @RequiresPermissions(value = {"system:class_setting:edit","system:class_manage:edit"}, logical = Logical.OR)
    @Log(title = "班级设置", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody SysClassSetting sysClassSetting) {
        SysClassSetting checkClassSetting = new SysClassSetting();
        BeanUtils.copyProperties(sysClassSetting, checkClassSetting);
        if(!(Boolean)checkClassNumber(checkClassSetting).get("data")){
            return AjaxResult.error("相同入学年份、同一年级下，班级编号不可重复");
        }
        if(StringUtils.isNotEmpty(checkClassSetting.getClassAlias()) && !(Boolean)checkClassAlias(checkClassSetting).get("data")){
            return AjaxResult.error("相同入学年份、同一年级下，班级别名不可重复");
        }
        return toAjax(sysClassSettingService.updateSysClassSetting(sysClassSetting));
    }

    /**
     * 逻辑删除班级
     */

    /**
     * 删除班级设置
     */
    @ApiOperation(value = "删除班级设置")
    @RequiresPermissions("system:class_setting:remove")
    @Log(title = "班级设置", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public AjaxResult remove(@PathVariable Long[] ids) {
        return toAjax(sysClassSettingService.deleteSysClassSettingByIds(ids));
    }

    /**
     * 批量导入班级列表
     *
     * @param file 文件
     * @throws Exception
     */
    @ApiOperation(value = "批量导入班级列表")
    @RequiresPermissions("system:class_setting:import")
    @Log(title = "批量导入班级列表", businessType = BusinessType.IMPORT)
    @PostMapping("/import")
    public AjaxResult importFile(MultipartFile file) throws Exception {
        sysClassSettingService.importFile(file);
        return AjaxResult.success();
    }

    /**
     * 通过入学年份、年级、班级编号、别名查询是否重复
     * 规则：
     * (1) 相同入学年份、同一年级下，班级编号不可重复
     * (2) 相同入学年份、同一年级下，班级别名不可重复
     */
    @RequiresPermissions("system:class_setting:repeat_check")
    @PostMapping(value = "/repeatCheck")
    public AjaxResult repeatCheck(@RequestBody SysClassSetting sysClassSetting) {
        return success(sysClassSettingService.repeatCheck(sysClassSetting));
    }

    @ApiOperation(value = "查询班级编号是否重复")
    @PostMapping(value = "/checkClassNumber")
    public AjaxResult checkClassNumber(@RequestBody SysClassSetting sysClassSetting) {
        sysClassSetting.setClassAlias(null);
        return AjaxResult.success(CollectionUtils.isNotEmpty(sysClassSettingService.repeatCheck(sysClassSetting)) ? false : true);
    }

    @ApiOperation(value = "查询班级别名是否重复")
    @PostMapping(value = "/checkClassAlias")
    public AjaxResult checkClassAlias(@RequestBody SysClassSetting sysClassSetting) {
        //班级别名为空时不校验
        if(StringUtils.isEmpty(sysClassSetting.getClassAlias())){
            return AjaxResult.success(true);
        }
        sysClassSetting.setClassNumber(null);
        return AjaxResult.success(CollectionUtils.isNotEmpty(sysClassSettingService.repeatCheck(sysClassSetting)) ? false : true);
    }

    /**
     * 通过位置id获取已绑定的班级列表
     *
     * @param locationId 位置id
     * @return
     */
    @ApiOperation(value = "通过位置id获取已绑定的班级列表")
    @RequiresPermissions("system:sysReLocationClass:bind")
    @Log(title = "绑定班级列表", businessType = BusinessType.UPDATE)
    @GetMapping("/bindClassList/{locationId}")
    public AjaxResult bindClassList(@PathVariable String locationId) {
        return AjaxResult.success(sysClassSettingService.bindClassList(locationId));
    }

    /**
     * 通过位置id获取未绑定的班级列表
     *
     * @param locationId 位置id
     * @return
     */
    @ApiOperation(value = "通过位置id获取未绑定的班级列表")
    @RequiresPermissions("system:sysReLocationClass:unbind")
    @Log(title = "未绑定班级列表", businessType = BusinessType.UPDATE)
    @GetMapping("/unBindClassList/{locationId}")
    public AjaxResult unBindClassList(@PathVariable String locationId) {
        return AjaxResult.success(sysClassSettingService.unBindClassList(locationId));
    }

    /**
     * 导出班级批量上传模板
     */
    @ApiOperation(value = "导出班级批量上传模板")
    @PostMapping(value = "/exportBatchUploadTemplate")
    public ResponseEntity<InputStreamResource> exportBatchUploadTemplate(String fileName, HttpServletResponse response, HttpServletRequest request) {
        return sysClassSettingService.exportBatchUploadTemplate(fileName,response,request);
    }

    /**
     * 获取班级设置详细信息
     */
    @InnerAuth
    @ApiOperation(value = "获取班级设置详细信息")
    @GetMapping(value = "/external/getLocationIdList/{ids}")
    public AjaxResult getLocationIdList(@PathVariable("ids") Long[] ids) {
        return success(sysClassSettingService.getLocationIdList(ids));
    }

    /**
     * 查询班级列表
     */
    @ApiOperation(value = "查询班级列表")
    @GetMapping("/getDownLocationList/{id}/{type}")
    public AjaxResult getDownLocationList(@PathVariable("id") Long id, @PathVariable Integer type) {
        if(type==1){
            SysClassSetting setting = new SysClassSetting();
            setting.setIsDeleted("0");
            setting.setTenantId(id);
            List<SysDownLocation> downLocationList = sysClassSettingService.getDownLocationList(setting);
            downLocationList = downLocationList.stream().collect(
                    Collectors.collectingAndThen(
                            Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(SysDownLocation::getId))),
                            ArrayList::new
                    )
            );
            return AjaxResult.success(downLocationList);
        } else {
            SysLocationSetting sysLocationSetting = new SysLocationSetting();
            sysLocationSetting.setIsDeleted("0");
            sysLocationSetting.setTenantId(id);
            return AjaxResult.success(sysLocationSettingService.getDownLocationList(sysLocationSetting));
        }
    }

    /**
     * 获取班级设置详细信息
     */
    @InnerAuth
    @ApiOperation(value = "获取班级设置详细信息")
    @GetMapping(value = "/external/getClassListByLocationId/{locationId}")
    public AjaxResult getClassListByLocationId(@PathVariable("locationId") Long locationId) {
        return success(sysClassSettingService.getClassListByLocationId(locationId));
    }

    @ApiOperation(value = "获取班级设置详细信息")
    @GetMapping(value = "/external/getClassListByLocationIdList")
    public AjaxResult getClassListByLocationIdList(@RequestParam("locationIds") List<Long> locationIds) {
        return success(sysClassSettingService.getClassListByLocationIdList(locationIds));
    }

    @ApiOperation(value = "根据年级获取班级设置详细信息")
    @GetMapping(value = "/external/getLocationByGradeIn")
    public AjaxResult getLocationByGradeIn(@SpringQueryMap ToySysClassSettingDto sysClassSettingDto) {
        return success(sysClassSettingService.getLocationByGradeIn(sysClassSettingDto));
    }


    // 以下是玩具小程序接口

    /**
     * 通过老师信息获取当前老师绑定的班级信息
     *
     * @return
     */
    @GetMapping(value = "/miniprogram")
    public AjaxResult miniprogramSelectClassByUser() {
        return success(sysClassSettingService.selectSysClassUserByUserId(SecurityUtils.getUserId()));
    }

    /**
     * 通过班级Id获取位置和区域信息
     *
     * @param classId 班级Id
     * @return
     */
    @GetMapping(value = "/inner/classId")
    public AjaxResult selectLocationAndAreaByClassId(@RequestParam(value = "classId",required = false) String classId) {
        return sysClassSettingService.selectLocationAndAreaByClassId(classId);
    }

    /**
     * 通过年级或班级Id获取位置和区域信息
     *
     * @param gradeIn  年级
     * @param classIds 班级
     * @param selectType 查询类型  1  查询全部  2  按照条件查询
     * @return
     */
    @GetMapping(value = "/inner/gradeInAndClass")
    public AjaxResult selectLocationAndAreaByGradeInAndClass(@RequestParam(value = "gradeIn", required = false) String[] gradeIn,
                                                             @RequestParam(value = "classIds", required = false) String[] classIds,
                                                             @RequestParam(value = "selectType", required = false) String selectType) {
        return sysClassSettingService.selectLocationAndAreaByGradeInAndClass(gradeIn, classIds,selectType);
    }

    /**
     * 通过位置Id获取位置和区域信息
     *
     * @param locationIds 位置Id
     * @return
     */
    @GetMapping(value = "/inner/locationId")
    public AjaxResult selectLocationAndAreaByLocationId(@RequestParam(value = "locationIds", required = false) String[] locationIds) {
        return sysClassSettingService.selectLocationAndAreaByLocationId(locationIds);
    }

    /**
     * 通过位置Id获取位置和区域信息
     *
     * @return
     */
    @GetMapping(value = "/inner/locationIdAndClass")
    public AjaxResult selectLocationIdAndClass() {
        return sysClassSettingService.selectLocationIdAndClass();
    }

    /**
     * 获取年级列表
     *
     * @return
     */
    @GetMapping(value = "/gradeIn")
    public AjaxResult selectGradeIn() {
        return sysClassSettingService.selectGradeIn();
    }

    /**
     * 获取班级列表
     *
     * @return
     */
    @GetMapping(value = "/class")
    public AjaxResult selectClass(String[] gradeIn) {
        return sysClassSettingService.selectClass(gradeIn);
    }

    /**
     * 获取区域列表
     *
     * @return
     */
    @GetMapping(value = "/area")
    public AjaxResult selectArea(String[] classIds, String[] locationIds) {
        return sysClassSettingService.selectArea(classIds, locationIds);
    }

    /**
     * 获取位置列表
     *
     * @return
     */
    @GetMapping(value = "/location")
    public AjaxResult selectLocation() {
        return sysClassSettingService.selectLocation();
    }


    /**
     * 获取班级设置详细信息
     */
    @InnerAuth
    @ApiOperation(value = "获取班级设置详细信息")
    @GetMapping(value = "/external/getClassSetting/locationIds")
    public AjaxResult getClassSetting(@RequestParam(value = "locationIds", required = false) String[] locationIds) {
        return success(sysClassSettingService.getClassSetting(locationIds));
    }

    /**
     * 获取班级与位置的关联
     */
    @ApiOperation(value = "获取班级与位置的关联")
    @GetMapping(value = "/inner/classSetting/locationId/{tenantId}")
    public AjaxResult selectClassSettingAndLocation(@PathVariable String tenantId) {
        SecurityUtils.setTenantId(tenantId);
        return success(sysClassSettingService.selectClassSettingAndLocation());
    }


    /**
     * 获取班级设置详细信息
     */
    @ApiOperation(value = "获取班级设置详细信息")
    @GetMapping(value = "/getClassSettingInfo/{id}")
    public AjaxResult getClassSettingInfo(@PathVariable("id") Long id) {
        return success(sysClassSettingService.getClassSettingInfo(id));
    }
}
