package com.dji.sample.manage.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dji.sample.common.model.CustomClaim;
import com.dji.sample.common.util.DesUtil;
import com.dji.sample.common.util.JwtUtil;
import com.dji.sample.component.mqtt.config.MqttPropertyConfiguration;
import com.dji.sample.configuration.CustomConfiguration;
import com.dji.sample.manage.dao.IUserMapper;
import com.dji.sample.manage.dao.IWorkspaceMapper;
import com.dji.sample.manage.log.annotation.Log;
import com.dji.sample.manage.log.enums.BusinessType;
import com.dji.sample.manage.model.dto.*;
import com.dji.sample.manage.model.entity.UserEntity;
import com.dji.sample.manage.model.entity.WorkspaceEntity;
import com.dji.sample.manage.model.enums.UserTypeEnum;
import com.dji.sample.manage.service.IDeviceService;
import com.dji.sample.manage.service.IUserService;
import com.dji.sample.manage.service.IWorkspaceService;
import com.dji.sdk.common.HttpResultResponse;
import com.dji.sdk.common.Pagination;
import com.dji.sdk.common.PaginationData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;



@Service
@Transactional
public class UserServiceImpl implements IUserService {

    @Autowired
    private IUserMapper mapper;

    @Autowired
    private MqttPropertyConfiguration mqttPropertyConfiguration;

    @Autowired
    private IWorkspaceService workspaceService;

    @Autowired
    private IDeviceService deviceService;

    @Autowired
    private IWorkspaceMapper workspaceMapper;



    @Override
    public HttpResultResponse getUserByUsername(String username, String workspaceId) {

        UserEntity userEntity = this.getUserByUsername(username);
        if (userEntity == null) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.UNAUTHORIZED.value())
                    .setMessage("invalid username");
        }

        UserDTO user = this.entityConvertToDTO(userEntity);
        user.setWorkspaceId(workspaceId);

        return HttpResultResponse.success(user);
    }

    @Override
    public HttpResultResponse userLogin(String username, String password, Integer flag, String gatewaySn) {
        // check user
        UserEntity userEntity = this.getUserByUsername(username);
        if (userEntity == null) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.UNAUTHORIZED.value())
                    .setMessage("invalid username");
        }
        if (flag.intValue() != userEntity.getUserType().intValue()) {
            return HttpResultResponse.error("The account type does not match.");
        }

        //密码加密
        password = DesUtil.getEncryptData(password,userEntity.getSalt());

        if (!password.equals(userEntity.getPassword())) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.UNAUTHORIZED.value())
                    .setMessage("invalid password");
        }

        Optional<WorkspaceDTO> workspaceOpt = workspaceService.getWorkspaceByWorkspaceId(userEntity.getWorkspaceId());
        if (workspaceOpt.isEmpty()) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.UNAUTHORIZED.value())
                    .setMessage("invalid workspace id");
        }

        CustomClaim customClaim = new CustomClaim(userEntity.getUserId(),
                userEntity.getUsername(), userEntity.getUserType(),
                workspaceOpt.get().getWorkspaceId());

        // create token
        String token = JwtUtil.createToken(customClaim.convertToMap());

        UserDTO userDTO = entityConvertToDTO(userEntity);
        userDTO.setMqttAddr(MqttPropertyConfiguration.getBasicMqttAddress());
        userDTO.setAccessToken(token);
        userDTO.setWorkspaceId(workspaceOpt.get().getWorkspaceId());

        //飞行员登录，如果设备原绑定工作空间不一致，则解绑
        if(UserTypeEnum.PILOT.getVal() == flag.intValue() && StringUtils.hasText(gatewaySn)) {
            Optional<DeviceDTO> optDevice = deviceService.getDeviceBySn(gatewaySn);
            if(optDevice.isPresent() && !userDTO.getWorkspaceId().equals(optDevice.get().getWorkspaceId())) {
                if(StringUtils.hasText(optDevice.get().getChildDeviceSn())) {
                    deviceService.unbindDevice(optDevice.get().getChildDeviceSn(),customClaim);
                } else {
                    deviceService.unbindDevice(gatewaySn,customClaim);
                }
            }
        }

        return HttpResultResponse.success(userDTO);
    }

    @Override
    public HttpResultResponse getToken(String clientId, String username, String password) {
        UserEntity userEntity = this.getClientUser(clientId);
        if (userEntity == null) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.UNAUTHORIZED.value())
                    .setMessage("clientId不存在");
        }
        if (UserTypeEnum.CLIENT.getVal() != userEntity.getUserType().intValue()) {
            return HttpResultResponse.error("用户类型不匹配");
        }

        //密码加密
        //String encryptPsw = DesUtil.getEncryptData(password,userEntity.getSalt());
        if (!password.equals(userEntity.getPassword()) || !username.equals(userEntity.getUsername())) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.UNAUTHORIZED.value())
                    .setMessage("用户名或密码不匹配");
        }

        CustomClaim customClaim = new CustomClaim(userEntity.getUserId(),
                userEntity.getUsername(), userEntity.getUserType(),
                "");

        // create token
        String token = JwtUtil.createToken(customClaim.convertToMap());

        return HttpResultResponse.success(token);
    }

    @Override
    public Optional<UserDTO> refreshToken(String token) {
        if (!StringUtils.hasText(token)) {
            return Optional.empty();
        }
        CustomClaim customClaim;
        try {
            DecodedJWT jwt = JwtUtil.verifyToken(token);
            customClaim = new CustomClaim(jwt.getClaims());
        } catch (TokenExpiredException e) {
            customClaim = new CustomClaim(JWT.decode(token).getClaims());
        } catch (Exception e) {
            e.printStackTrace();
            return Optional.empty();
        }
        String refreshToken = JwtUtil.createToken(customClaim.convertToMap());

        UserDTO user = entityConvertToDTO(this.getUserByUsername(customClaim.getUsername()));
        if (Objects.isNull(user)) {
            return Optional.empty();
        }
        user.setWorkspaceId(customClaim.getWorkspaceId());
        user.setAccessToken(refreshToken);
        return Optional.of(user);
    }

    @Override
    public PaginationData<UserListDTO> getUsersByWorkspaceId(long page, long pageSize, String workspaceId,String searchInfo) {
        LambdaQueryWrapper<UserEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserEntity::getWorkspaceId, workspaceId);
        queryWrapper.and(wrapper -> {
            wrapper.eq(UserEntity::getUserType, UserTypeEnum.WEB.getVal());
            wrapper.or().eq(UserEntity::getUserType, UserTypeEnum.PILOT.getVal());
        });
        if(searchInfo != null) {
            queryWrapper.like(UserEntity::getUsername,searchInfo);
        }

        Page<UserEntity> userEntityPage = mapper.selectPage(
                new Page<>(page, pageSize),
                queryWrapper);

        List<UserListDTO> usersList = userEntityPage.getRecords()
                .stream()
                .map(this::entity2UserListDTO)
                .collect(Collectors.toList());
        return new PaginationData<>(usersList, new Pagination(userEntityPage.getCurrent(), userEntityPage.getSize(), userEntityPage.getTotal()));
    }

    @Override
    public HttpResultResponse saveApiUser(String clientId, String username, String password) {
        if(!StringUtils.hasText(clientId)) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.UNAUTHORIZED.value())
                    .setMessage("clientId不能为空");
        }

        UserEntity userEntity = this.getClientUser(clientId);
        if (userEntity == null) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.UNAUTHORIZED.value())
                    .setMessage("clientId不存在");
        }

        if(!StringUtils.hasText(username)) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.BAD_REQUEST.value())
                    .setMessage("用户名不能为空");
        }

        if(!StringUtils.hasText(password)) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.BAD_REQUEST.value())
                    .setMessage("密码不能为空");
        }
        //密码加密
        String salt = DesUtil.genSalt();
        String encryptPsw = DesUtil.getEncryptData(password,salt);

        long curTime = System.currentTimeMillis();
        userEntity = UserEntity.builder().
                userId(UUID.randomUUID().toString())
                .username(username)
                .password(encryptPsw)
                .userType(UserTypeEnum.API.getVal())
                .createTime(curTime)
                .updateTime(curTime)
                .clientId(clientId)
                .salt(salt)
                .build();
        UserEntity apiUser = getApiUser(clientId);
        if(apiUser == null) {
            int cnt = mapper.insert(userEntity);
            return cnt > 0 ? HttpResultResponse.success():HttpResultResponse.error("添加失败");
        } else {
            userEntity.setUsername(username);
            userEntity.setPassword(encryptPsw);
            userEntity.setSalt(salt);
            userEntity.setUpdateTime(System.currentTimeMillis());
            int cnt = mapper.update(userEntity, new LambdaUpdateWrapper<UserEntity>()
                    .eq(UserEntity::getClientId, clientId)
                    .eq(UserEntity::getUserType, UserTypeEnum.API.getVal()));
            return cnt > 0 ? HttpResultResponse.success():HttpResultResponse.error("更新失败");
        }
    }

    @Override
    public Boolean updateUser(String workspaceId, String userId, UserListDTO user) {
        UserEntity userEntity = mapper.selectOne(
                new LambdaQueryWrapper<UserEntity>()
                        .eq(UserEntity::getUserId, userId)
                        .eq(UserEntity::getWorkspaceId, workspaceId));
        if (userEntity == null) {
            return false;
        }
        userEntity.setMqttUsername(user.getMqttUsername());
        userEntity.setMqttPassword(user.getMqttPassword());
        userEntity.setUpdateTime(System.currentTimeMillis());
        int id = mapper.update(userEntity, new LambdaUpdateWrapper<UserEntity>()
                .eq(UserEntity::getUserId, userId)
                .eq(UserEntity::getWorkspaceId, workspaceId));

        return id > 0;
    }

    @Override
    public Boolean updateUserWorkspace(String newWorkspaceId, String userId) {
        UserEntity userEntity = mapper.selectOne(
                new LambdaQueryWrapper<UserEntity>()
                        .eq(UserEntity::getUserId, userId));
        if (userEntity == null) {
            return false;
        }
        userEntity.setWorkspaceId(newWorkspaceId);
        userEntity.setUpdateTime(System.currentTimeMillis());
        int id = mapper.update(userEntity, new LambdaUpdateWrapper<UserEntity>()
                .eq(UserEntity::getUserId, userId));

        return id > 0;
    }

    /**
     * Convert database entity objects into user data transfer object.
     * @param entity
     * @return
     */
    private UserListDTO entity2UserListDTO(UserEntity entity) {
        UserListDTO.UserListDTOBuilder builder = UserListDTO.builder();
        if (entity != null) {
            builder.userId(entity.getUserId())
                    .username(entity.getUsername())
                    .userType(UserTypeEnum.find(entity.getUserType()).getDesc())
                    .createTime(LocalDateTime.ofInstant(
                            Instant.ofEpochMilli(entity.getCreateTime()), ZoneId.systemDefault()));
            Optional<WorkspaceDTO> workspaceOpt = workspaceService.getWorkspaceByWorkspaceId(entity.getWorkspaceId());
            workspaceOpt.ifPresent(workspace -> builder.workspaceName(workspace.getWorkspaceName()));
        }

        return builder.build();
    }

    /**
     * Query a user by username.
     * @param username
     * @return
     */
    @Override
    public UserEntity getUserByUsername(String username) {
        return mapper.selectOne(new QueryWrapper<UserEntity>()
                .eq("username", username));
    }

    @Override
    public HttpResultResponse userLogin(SignLoginDTO signLoginDTO) {
        String username = signLoginDTO.getUsername();
        String sign = signLoginDTO.getSignature();
        String workSpaceId = signLoginDTO.getWorkspace_id();
        String workSpaceName = signLoginDTO.getWorkspace_name();
        String clientId = signLoginDTO.getClient_id();
        Long timestamp = signLoginDTO.getTimestamp();
        if(!StringUtils.hasText(username)
            || !StringUtils.hasText(sign)
            || !StringUtils.hasText(workSpaceId)
            || !StringUtils.hasText(workSpaceName)
            || !StringUtils.hasText(clientId)
            || timestamp == null) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.UNAUTHORIZED.value())
                    .setMessage("无效的参数");
        }

        if(!CustomConfiguration.parentSysClientId.equals(clientId)) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.UNAUTHORIZED.value())
                    .setMessage("无效的 clientId");
        }

        long btwTime = System.currentTimeMillis() - timestamp;

        // Step 3: Check if the difference is greater than 5 minutes
        boolean isMoreThanFiveMinutes = btwTime > 5 * 1000 * 60;
        if (isMoreThanFiveMinutes) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.UNAUTHORIZED.value())
                    .setMessage("请求已过期，请重新发起请求");
        }
        if (sign != null) {
            String dataInput  = clientId+timestamp+username+workSpaceId+workSpaceName;
            String generatedHash = DigestUtil.sha256Hex(dataInput.toUpperCase()+CustomConfiguration.signKey);
            if(!StrUtil.equals(generatedHash, sign)) {
                return new HttpResultResponse()
                        .setCode(HttpStatus.UNAUTHORIZED.value())
                        .setMessage("签名验证失败");
            }
        } else {
            return new HttpResultResponse()
                    .setCode(HttpStatus.UNAUTHORIZED.value())
                    .setMessage("无效的签名");
        }
        //用户入库
        QueryWrapper<UserEntity> userWrapper = new QueryWrapper<>();
        userWrapper.lambda().eq(UserEntity::getUserType,UserTypeEnum.WEB.getVal())
                .eq(UserEntity::getClientId,clientId)
                .eq(UserEntity::getUsername,username);
        UserEntity userEntity = mapper.selectOne(userWrapper);
        if(userEntity == null) {
            userEntity = new UserEntity();
            userEntity.setUsername(username);
            userEntity.setUserId(UUID.randomUUID().toString());
            userEntity.setUserType(UserTypeEnum.WEB.getVal());
            userEntity.setPassword("");
            userEntity.setWorkspaceId(workSpaceId);
            userEntity.setMqttUsername(mqttPropertyConfiguration.mqttConnectOptions().getUserName());
            userEntity.setMqttPassword(String.valueOf(mqttPropertyConfiguration.mqttConnectOptions().getPassword()));
            userEntity.setClientId(clientId);
            mapper.insert(userEntity);
        } else {
            if(!mqttPropertyConfiguration.mqttConnectOptions().getUserName().equals(userEntity.getMqttUsername())
            || !String.valueOf(mqttPropertyConfiguration.mqttConnectOptions().getPassword()).equals(userEntity.getMqttPassword())
            || !username.equals(userEntity.getUsername())
            || !workSpaceId.equals(userEntity.getWorkspaceId())) {
                userEntity.setMqttUsername(mqttPropertyConfiguration.mqttConnectOptions().getUserName());
                userEntity.setMqttPassword(String.valueOf(mqttPropertyConfiguration.mqttConnectOptions().getPassword()));
                userEntity.setUsername(username);
                userEntity.setWorkspaceId(workSpaceId);
                mapper.updateById(userEntity);
            }
        }

        //工作空间入库
        QueryWrapper<WorkspaceEntity> workSpaceWrapper = new QueryWrapper<>();
        workSpaceWrapper.lambda().eq(WorkspaceEntity::getWorkspaceId,workSpaceId);
        WorkspaceEntity workspace = workspaceMapper.selectOne(workSpaceWrapper);
        if(workspace == null) {
            workspace = new WorkspaceEntity();
            workspace.setWorkspaceId(workSpaceId);
            workspace.setWorkspaceName(workSpaceName);
            workspace.setPlatformName("私有云平台");
            workspace.setBindCode(RandomUtil.randomString(6));
            workspaceMapper.insert(workspace);
        } else {
            if(StringUtils.hasText(workSpaceName) && !workSpaceName.equals(workspace.getWorkspaceName())) {
                workspace.setWorkspaceName(workSpaceName);
                workspaceMapper.updateById(workspace);
            }
        }

        CustomClaim customClaim = new CustomClaim(userEntity.getUserId(),
                username, UserTypeEnum.WEB.getVal(),
                workSpaceId);

        // create token
        String token = JwtUtil.createToken(customClaim.convertToMap());

        UserDTO userDTO = new UserDTO();
        userDTO.setUsername(username);
        userDTO.setWorkspaceId(workSpaceId);
        userDTO.setUserId(userEntity.getUserId());
        userDTO.setMqttUsername(userEntity.getMqttUsername());
        userDTO.setMqttPassword(userEntity.getMqttPassword());
        userDTO.setUserType(UserTypeEnum.WEB.getVal());
        userDTO.setMqttAddr(MqttPropertyConfiguration.getBasicMqttAddress());
        userDTO.setAccessToken(token);
        return HttpResultResponse.success(userDTO);
    }

    @Override
    public HttpResultResponse savePilotUser(String username, String password,String workspaceId) {

        if(!StringUtils.hasText(username)) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.BAD_REQUEST.value())
                    .setMessage("用户名不能为空");
        }

        if(!StringUtils.hasText(password)) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.BAD_REQUEST.value())
                    .setMessage("密码不能为空");
        }

        if(!StringUtils.hasText(workspaceId)) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.BAD_REQUEST.value())
                    .setMessage("工作空间不能为空");
        }
        UserEntity userEntity = getUserByUsername(username);
        if(userEntity != null) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.BAD_REQUEST.value())
                    .setMessage("用户已存在");
        }
        Optional<WorkspaceDTO> workspace = workspaceService.getWorkspaceByWorkspaceId(workspaceId);
        if(workspace.isEmpty()) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.BAD_REQUEST.value())
                    .setMessage("工作空间不存在");
        }
        //密码加密
        String salt = DesUtil.genSalt();
        String encryptPsw = DesUtil.getEncryptData(password,salt);

        long curTime = System.currentTimeMillis();
        userEntity = UserEntity.builder().
                userId(UUID.randomUUID().toString())
                .username(username)
                .password(encryptPsw)
                .workspaceId(workspaceId)
                .userType(UserTypeEnum.PILOT.getVal())
                .mqttUsername(mqttPropertyConfiguration.mqttConnectOptions().getUserName())
                .mqttPassword(String.valueOf(mqttPropertyConfiguration.mqttConnectOptions().getPassword()))
                .createTime(curTime)
                .updateTime(curTime)
                .salt(salt)
                .build();
        int cnt = mapper.insert(userEntity);
        return cnt > 0 ? HttpResultResponse.success():HttpResultResponse.error("添加失败");

    }

    @Override
    public HttpResultResponse getPwd(String userId) {
        QueryWrapper<UserEntity>  wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(UserEntity::getUserId,userId);
        UserEntity userEntity = mapper.selectOne(wrapper);
        if (userEntity == null) {
            return new HttpResultResponse()
                    .setCode(HttpStatus.BAD_REQUEST.value())
                    .setMessage("用户不存在");
        }
        String decryptPwd = DesUtil.getDecryptData(userEntity.getPassword(),userEntity.getSalt());
        return new HttpResultResponse().success(decryptPwd);
    }

    private UserEntity getApiUser(String clientId) {
        return mapper.selectOne(new QueryWrapper<UserEntity>()
                .eq("client_id", clientId)
                .eq("user_type",UserTypeEnum.API.getVal()));
    }

    private UserEntity getClientUser(String clientId) {
        return mapper.selectOne(new QueryWrapper<UserEntity>()
                .eq("client_id", clientId)
                .eq("user_type",UserTypeEnum.CLIENT.getVal()));
    }

    /**
     * Convert database entity objects into user data transfer object.
     * @param entity
     * @return
     */
    private UserDTO entityConvertToDTO(UserEntity entity) {
        if (entity == null) {
            return null;
        }
        return UserDTO.builder()
                .userId(entity.getUserId())
                .username(entity.getUsername())
                .userType(entity.getUserType())
                .mqttUsername(entity.getMqttUsername())
                .mqttPassword(entity.getMqttPassword())
                .mqttAddr(MqttPropertyConfiguration.getBasicMqttAddress())
                .build();
    }

    private static String extractDateTime(String inputString,String userCode) {
        // Step 1: Define the pattern
        Pattern pattern = Pattern.compile(userCode+"(\\d+)"+userCode);
        // Step 2: Create a matcher for the input string
        Matcher matcher = pattern.matcher(inputString);
        // Step 3: Check if the pattern matches
        if (matcher.find()) {
            // Step 4: Extract the number
            String number = matcher.group(1); // group(1) refers to the first capturing group
            return number;
        } else {
            return null;
        }
    }

    private static LocalDateTime parseTimestamp(String timestampString) {
        // Convert the timestamp to LocalDateTime
        long timestamp = Long.parseLong(timestampString);
        return LocalDateTime.ofInstant(
                Instant.ofEpochSecond(timestamp),
                ZoneId.systemDefault()
        );
    }
}
