package org.dromara.app.controller;


import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.digest.BCrypt;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.app.domain.bo.*;
import org.dromara.app.domain.mqtt.DeviceCommand;
import org.dromara.app.domain.vo.*;
import org.dromara.app.service.*;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.app.service.impl.AppScheduleServiceImpl;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.encrypt.annotation.ApiEncrypt;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.helper.DataPermissionHelper;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.web.core.BaseController;
import org.dromara.system.domain.bo.SysUserBo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.domain.vo.UserInfoVo;
import org.dromara.system.service.ISysUserService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.*;


@Slf4j
@RequiredArgsConstructor
@RestController
//@SaCheckLogin
@RequestMapping("/app/v1")
public class AppController extends BaseController {

    private final IAppDeviceService appDeviceService;
    private final IAppScheduleService appScheduleService;
    private final IAppScheduleDetailService appScheduleDetailService;
    private final AppScheduleServiceImpl appScheduleServiceimpl;
    private final IAppSchedulingDeviceService appSchedulingDeviceService;
    private final ISysUserService userService;
    private final IAppWateringLogService appWateringLogService;
    private final IDeviceCommandService deviceCommandService;

    /**
     *查询设备
     *
     */
    @GetMapping("/deviceList")
    public TableDataInfo<AppDeviceVo> list(AppDeviceBo bo, PageQuery pageQuery) {
        bo.setUserId(LoginHelper.getUserId());
        pageQuery.setOrderByColumn("createTime");
        pageQuery.setIsAsc("desc");
        return appDeviceService.queryPageList(bo, pageQuery);
    }



    /**
     * 添加设备
     *
     */
    @ApiEncrypt
    @PostMapping("/addDevice")
    public R<Void> addDevice(@RequestBody String body) {
        AppDeviceBo appDevic = JsonUtils.parseObject(body, AppDeviceBo.class);
        appDevic.setUserId(LoginHelper.getUserId());
        Boolean b = appDeviceService.insertByBo(appDevic);
        if (b){
            return R.ok();
        }else {

            return R.fail();
        }
    }

    /**
     * 绑定已通过MQTT注册上线的设备
     */
    @ApiEncrypt
    @PostMapping("/bindDevice")
    public R<AppDeviceVo> bindDevice(@RequestBody String body) {
        AppDeviceBo appDevice = JsonUtils.parseObject(body, AppDeviceBo.class);
        appDevice.setUserId(LoginHelper.getUserId());
        return R.ok(appDeviceService.bindRegisteredDevice(appDevice));
    }

    /**
     * 获取设备信息
     详细信息
     *
     * @param deviceNo 主键
     */
    @GetMapping("/device/{deviceNo}")
    public R<AppDeviceVo> getDeviceInfo(@NotNull(message = "主键不能为空")
                                  @PathVariable String deviceNo) {
        return R.ok(appDeviceService.queryById(deviceNo));
    }


    /**
     * 修改设备信息
     *
     */
  //  @SaCheckRole("appadmin")
    @RepeatSubmit()
    @PutMapping("/updateDeviceInfo")
    public R<Void> updateDeviceInfo(@Validated @RequestBody AppDeviceBo bo) {
        try {
            return toAjax(appDeviceService.updateByBo(bo));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 删除设备
     */
    @Log(title = "设备信息", businessType = BusinessType.DELETE)
    @DeleteMapping("/deleteDevice/{deviceNos}")
    public R<Void> removeDevice(@NotEmpty(message = "主键不能为空")
                          @PathVariable String[] deviceNos) {
        return toAjax(appDeviceService.deleteWithValidByIds(List.of(deviceNos), true));
    }


    /**
     * 手动开启关闭设备
     *
     */
    //  @SaCheckRole("appadmin")
    @RepeatSubmit()
    @PutMapping("/switchDevice")
    public R<Void> switchDevice(@RequestBody String body) {
        Map<String,String> map = JsonUtils.parseObject(body, Map.class);
        try {
            String deviceNo = map.get("deviceNo");
            String status = map.get("workStatus");
            Date date = new Date();
            String durationMinValue = map.get("durationMin");
            Integer durationMin = StringUtils.isBlank(durationMinValue) ? null : Integer.valueOf(durationMinValue);
            //更新设备工作状态
            Boolean flag = appDeviceService.switchDevice(deviceNo, status, startTime, durationMin);
                // 记录信息到历史表
            return toAjax(flag);
        } catch (Exception e) {
            return R.fail(e.getMessage());
        }

    }

    /**
     * 排程关联设备查询
     * @param
     * @return
     */
    @GetMapping("/scheduleDeviceList/{scheduleId}")
    public R<Map<String,Object>> scheduleDeviceList(   @PathVariable Long scheduleId) {

        Map retMap = new HashMap();
        //所有设备列表
        AppDeviceBo appDeviceBo = new AppDeviceBo();
        appDeviceBo.setUserId(LoginHelper.getUserId());
        List<AppDeviceVo> appDeviceVos = appDeviceService.queryList(appDeviceBo);

        List<AppDeviceVo> appDeviceVoList = new ArrayList();
        AppSchedulingDeviceBo schedulingDeviceBo = new AppSchedulingDeviceBo();
        schedulingDeviceBo.setScheduleId(scheduleId);


        for (AppDeviceVo appDeviceVo:appDeviceVos){
            List<AppSchedulingDeviceVo> appSchedulingDeviceVos = appSchedulingDeviceService.findByDeviceNo(appDeviceVo.getDeviceNo());
            if(appSchedulingDeviceVos.size() == 0){
                appDeviceVoList.add(appDeviceVo);
            }
        }
        retMap.put("deviceList", appDeviceVoList);


        return R.ok(retMap);
    }
    /**
     *绑定排程跟设备
     */
    @RepeatSubmit()
    @PostMapping("/addScheduleDevice")
    public R<Void> addScheduleDevice( @RequestBody String body) {
        Map<String, Object> map = JsonUtils.parseObject(body, Map.class);
        if (map == null || map.get("scheduleId") == null) {
            throw new ServiceException("scheduleId不能为空");
        }
        Long scheduleId = Long.valueOf(map.get("scheduleId").toString());
        List<String> deviceNos = parseDeviceNos(map);
        if (deviceNos.isEmpty()) {
            throw new ServiceException("deviceNos不能为空");
        }

        for (String deviceNo:deviceNos){
            if (StringUtils.isBlank(deviceNo)) {
                continue;
            }
            AppSchedulingDeviceBo schedulingDeviceBo = new AppSchedulingDeviceBo();
            schedulingDeviceBo.setDeviceNo(deviceNo);
            schedulingDeviceBo.setScheduleId(scheduleId);
            appSchedulingDeviceService.insertByBo(schedulingDeviceBo);
        }
        return R.ok();
    }

    private List<String> parseDeviceNos(Map<String, Object> map) {
        Object value = Optional.ofNullable(map.get("deviceNos"))
            .orElseGet(() -> Optional.ofNullable(map.get("deviceNo")).orElse(map.get("deviceIds")));
        if (value == null) {
            return Collections.emptyList();
        }
        if (value instanceof Collection<?> collection) {
            return collection.stream()
                .filter(Objects::nonNull)
                .map(Object::toString)
                .filter(StringUtils::isNotBlank)
                .toList();
        }
        String text = value.toString();
        if (StringUtils.isBlank(text)) {
            return Collections.emptyList();
        }
        return Arrays.stream(text.split(","))
            .map(String::trim)
            .filter(StringUtils::isNotBlank)
            .toList();
    }

    /**
     * 删除排程与设备绑定
     * @param scheduleId,deviceNo
     * @return
     */

    @Log(title = "排程与设备关联", businessType = BusinessType.DELETE)
    @DeleteMapping("/deleteScheduleDevice")
    public R<Void> deleteScheduleDevice(@NotEmpty(message = "主键不能为空")
                          @RequestParam String scheduleId,  @RequestParam String deviceNo) {
        return toAjax(appSchedulingDeviceService.deleteWithValidByScheduleIdAndDeviceNo(Long.valueOf(scheduleId), deviceNo));
    }



    /**
     * 查询排程列表
     */
    @GetMapping("/schedulelist")
    public TableDataInfo<AppScheduleVo> schedulelist(AppScheduleBo bo, PageQuery pageQuery) {
        bo.setUserId(LoginHelper.getUserId());
        pageQuery.setOrderByColumn("createTime");
        pageQuery.setIsAsc("desc");
        return appScheduleService.queryPageList(bo, pageQuery);
    }

    /**
     * 新增排程
     *
     */
    @Log(title = "日程排列", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping("/addschedule")
    public R<AppScheduleVo> addSchedule(@Validated(AddGroup.class) @RequestBody AppScheduleBo bo) {
        try {
            bo.setCreateTime(new Date());
            AppScheduleVo appScheduleVo = appScheduleService.insertByBo(bo);
            if (ObjectUtil.isNotNull(appScheduleVo)){
                return R.ok(appScheduleVo);
            }
            return R.fail();
           // return toAjax(appScheduleService.insertByBo(bo));
        } catch (RuntimeException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 排程詳情
     * @param
     * @param
     * @return
     */
    @GetMapping("/schedule/{id}")
    public R<AppScheduleVo> getScheduleInfo(@NotNull(message = "主键不能为空")
                                    @PathVariable Long id) {
        AppScheduleVo appScheduleVo = appScheduleService.queryById(id);
        if (ObjectUtil.isNotNull(appScheduleVo)){
            List<AppScheduleDetailVo> list = appScheduleDetailService.queryByScheduleIdByStatus(appScheduleVo.getId());

            AppSchedulingDeviceBo appSchedulingDeviceBo = new AppSchedulingDeviceBo();
            appSchedulingDeviceBo.setScheduleId(appScheduleVo.getId());
            List<AppSchedulingDeviceVo> appSchedulingDeviceVos = appSchedulingDeviceService.queryList(appSchedulingDeviceBo);
            List deviceList = new ArrayList();
            for (AppSchedulingDeviceVo appSchedulingDeviceVo:appSchedulingDeviceVos){
                AppDeviceVo appDeviceVo = appDeviceService.queryById(appSchedulingDeviceVo.getDeviceNo());
                if (ObjectUtil.isNotNull(appDeviceVo)){
                    deviceList.add(appDeviceVo);
                }

            }
            List detailList = new ArrayList();

            for (AppScheduleDetailVo detail:list){
                List timeSlots = new ArrayList();
                JSONArray mapArray = JSONUtil.parseArray(detail.getTimeData());
                for (Object timeSlot:mapArray){
                    timeSlots.add(timeSlot);
                }

                Map map = new HashMap();
                map.put("id",detail.getId() );
                map.put("weekday",detail.getWeekday() );
                map.put("timeData",timeSlots );
                map.put("triggerType",detail.getTriggerType() );
                map.put("status",detail.getStatus() );
                detailList.add(map);
            }



            appScheduleVo.setDetails(detailList);
            appScheduleVo.setDeviceNos(deviceList);
            return R.ok(appScheduleVo);
        }


        return R.fail(appScheduleVo);
    }


    /**
     * 修改排程状态
     * @param
     * @return
     */
    @PutMapping("/editScheduleStatus")
    public R<Void> editScheduleStatus(@NotNull(message = "主键不能为空")
                                            @RequestBody AppScheduleBo bo) {

        return toAjax(appScheduleService.updateStatusByBo(bo));
    }

    /**
     * 修改日程排列
     */
    @Log(title = "日程排列", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping("/updataschedule")
    public R<Object> editSchedule(@Validated(EditGroup.class) @RequestBody AppScheduleBo bo) {
        Map<String, Object> map = appScheduleService.updateByBo(bo);
        if (Boolean.valueOf(map.get("flag").toString())){
            return R.ok(map.get("appSchedule"));
        }else {
            return R.fail();
        }

    }


    /**
     * 删除日程排列
     *
     * @param ids 主键串
     */
    @Log(title = "日程排列", businessType = BusinessType.DELETE)
    @DeleteMapping("/deleteschedule/{ids}")
    public R<Void> removeSchedule(@NotEmpty(message = "主键不能为空")
                          @PathVariable Long[] ids) {
        return toAjax(appScheduleService.deleteWithValidByIds(List.of(ids), true));
    }

    /**
     * 历史
     * 查询浇水记录列表
     */
    @GetMapping("/waterLogList")
    public TableDataInfo<AppWateringLogVo> list(AppWateringLogBo bo, PageQuery pageQuery) {
        bo.setUserId(LoginHelper.getUserId());
        pageQuery.setOrderByColumn("createTime");
        pageQuery.setIsAsc("desc");
        TableDataInfo<AppWateringLogVo> appWateringLogVoTableDataInfo = appWateringLogService.queryPageList(bo, pageQuery);
        AppDeviceBo appDeviceBo = new AppDeviceBo();
        appDeviceBo.setUserId(LoginHelper.getUserId());
        List<AppDeviceVo> appDeviceVos = appDeviceService.queryList(appDeviceBo);
        List l = new ArrayList<>();
        for (AppWateringLogVo vo : appWateringLogVoTableDataInfo.getRows()){
            for(AppDeviceVo appDeviceVo:appDeviceVos){
                if (appDeviceVo.getDeviceNo().equals(vo.getDeviceNo())){
                    vo.setDeviceName(appDeviceVo.getDeviceName());
                    vo.setDeviceNo(appDeviceVo.getDeviceNo());
                }
            }
            vo.setTriggerType(vo.getTriggerType().equals("0")?"排程":"手动");
            l.add(vo);
        }
        appWateringLogVoTableDataInfo.setRows(l);
        return appWateringLogVoTableDataInfo;
    }


    /**
     * 删除浇水记录
     *
     * @param ids 主键串
     */
    @Log(title = "浇水记录", businessType = BusinessType.DELETE)
    @DeleteMapping("/waterLog/{ids}")
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
                          @PathVariable Long[] ids) {
        return toAjax(appWateringLogService.deleteWithValidByIds(List.of(ids), true));
    }


    /**
     * 数据统计
     */
    @GetMapping("/dataStatistics")
    public R<Map> dataStatistics() {
        Map map  = new HashMap();
        try {
            Long userId = LoginHelper.getUserId();
            //总浇水次数
            Long count = appWateringLogService.queryCount(userId);
            //本月浇水次数
            Long monCount = appWateringLogService.queryByMonCount(userId);
            //本周浇水次数
            Long weekCount = appWateringLogService.queryByWeekCount(userId);
            //排程浇水次数
            Long schedule = appWateringLogService.queryByTriggerTypeCount(userId, "0");
            //手动浇水次数
            Long manualNum = appWateringLogService.queryByTriggerTypeCount(userId, "1");
            //总浇水时间
            Long allTime = Optional.ofNullable(appWateringLogService.queryByTotalWateringTime(userId)).orElse(0L);
            //平均浇水时间
            Long averageTime = count == null || count == 0 ? 0L : allTime / count;

            map.put("count",count);
            map.put("monCount",monCount);
            map.put("weekCount",weekCount);
            map.put("schedule",schedule);
            map.put("manualNum",manualNum);
            map.put("allTime",allTime);
            map.put("averageTime",averageTime);
            return R.ok(map);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 获取用户信息
     *
     * @return 用户信息
     */
    @GetMapping("/getUserInfo")
    public R<UserInfoVo> getUserInfo() {
        UserInfoVo userInfoVo = new UserInfoVo();
        LoginUser loginUser = LoginHelper.getLoginUser();
        SysUserVo user = DataPermissionHelper.ignore(() -> userService.selectUserById(loginUser.getUserId()));
        if (ObjectUtil.isNull(user)) {
            return R.fail("没有权限访问用户数据!");
        }
        userInfoVo.setUser(user);
        userInfoVo.setPermissions(loginUser.getMenuPermission());
        userInfoVo.setRoles(loginUser.getRolePermission());
        return R.ok(userInfoVo);
    }

    /**
     * 修改用户
     */
   // @SaCheckPermission("system:user:edit")
    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping("/updataUser")
    public R<Void> editUser( @RequestBody SysUserBo user) {
        userService.checkUserAllowed(user.getUserId());
        userService.checkUserDataScope(user.getUserId());
        if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
            return R.fail("修改用户'" + user.getUserName() + "'失败，手机号码已存在");
        }

        return toAjax(userService.updateAppUser(user));
    }

    /**
     * 修改登录密码
     */
    @RepeatSubmit()
    @PutMapping("/updataPassword")
    public R<Void> updataPassword(@RequestBody  SysUserVo vo) {
        Long userId = LoginHelper.getUserId();
        SysUserVo sysUserVo = userService.selectUserById(userId);
        if (!BCrypt.checkpw(vo.getOldPassword(), sysUserVo.getPassword())){
            throw new UserException("user.password.not.match");
        }
        SysUserBo userBo = new SysUserBo();
        userBo.setUserId(userId);
        userBo.setPassword(vo.getPassword());
        return toAjax(userService.updateUserPas(userBo));
    }

    /**
     * app用户手机号/邮箱找回忘记密码
     */
    @ApiEncrypt
    @PostMapping("/retrievePassword")
    public R<Void> forgot(@RequestBody String body) {
        SysUserBo bo = JsonUtils.parseObject(body, SysUserBo.class);
        bo.setUserId(LoginHelper.getUserId());

        if(Validator.isEmail(bo.getUserName())){
            boolean checkPhoneFlag = userService.checkEmailUnique(bo);
            if (!checkPhoneFlag){
                throw new UserException("user.email.not.username");
            }
        }else if (Validator.isMobile(bo.getUserName())){
            boolean checkPhoneFlag = userService.checkPhoneUnique(bo);
            if (!checkPhoneFlag){
                throw new UserException("user.mobile.phone.number.not.username");
            }
        }

        return toAjax(userService.updateUserPas(bo));
    }



    @GetMapping("/test")
    public  R<Void>  test (){

        DeviceCommand deviceCommand = new DeviceCommand();

        String bindDevice = deviceCommandService.sendBindDeviceCommand("01");

        // String s = deviceCommandService.sendSwitchCommand("01", "1", "2026-06-10 14:35:31", 20);
        return R.ok(bindDevice);
    }

}
