package cn.hanghui.outapi.platform.common.service.impl;

import cn.hanghui.outapi.platform.common.config.JwtConfig;
import cn.hanghui.outapi.platform.common.entity.dto.LoginDto;
import cn.hanghui.outapi.platform.common.entity.dto.LoginUserDto;
import cn.hanghui.outapi.platform.common.entity.dto.LoginUserRedisDto;
import cn.hanghui.outapi.platform.common.entity.dto.PcLoginCodeDto;
import cn.hanghui.outapi.platform.common.entity.po.Account;
import cn.hanghui.outapi.platform.common.entity.rest.RespCodeEnum;
import cn.hanghui.outapi.platform.common.exception.CommonException;
import cn.hanghui.outapi.platform.common.handler.LoginAuthorizedHandler;
import cn.hanghui.outapi.platform.common.mapper.AccountMapper;
import cn.hanghui.outapi.platform.common.service.LoginService;
import cn.hanghui.outapi.platform.common.utils.ClassUtil;
import cn.hanghui.outapi.platform.common.utils.RedisUtil;
import cn.hanghui.outapi.platform.tenant.entity.enums.AccountTypeEnum;
import com.yx.message.service.server.MessageServer;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.jwt.JWTUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class LoginServiceImpl implements LoginService {

    @Resource
    @Lazy
    private AccountMapper accountMapper;

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    private final RedisUtil redisUtil;
    private final String REIDS_CODE_KEY = "code::";//code::账号名::手机号

    @Override
    public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
        String token = redisTemplate.opsForValue().get(JwtConfig.TOKEN_PREFIX + name.toLowerCase());
        Account account;
        if (ObjectUtil.isNotEmpty(token)) {
            String session = redisTemplate.opsForValue().get(JwtConfig.SESSION_PREFIX + token);
            account = JSON.parseObject(session, Account.class);
        } else {
            account = accountMapper.selectOne(Wrappers.lambdaQuery(Account.class).eq(Account::getName, name));
        }
        if (account == null) {
            throw new CommonException(RespCodeEnum.ACCOUNT_OR_PASSWORD_ERROR);
        }
        return User.builder().username(name).password(account.getPassword()).roles("caller").build();
    }

    /**
     * 登录
     *
     * @param loginDto LoginDto
     * @return token
     */
    @Override
    public String login(LoginDto loginDto) {
        LoginAuthorizedHandler loginAuthorizedHandler = SpringUtil.getBean(LoginAuthorizedHandler.class);
        loginAuthorizedHandler.authenticate(
                new UsernamePasswordAuthenticationToken(loginDto.getAccountName(), loginDto.getPassword())
        );
        Account account = accountMapper.selectOne(Wrappers.lambdaQuery(Account.class).eq(Account::getName, loginDto.getAccountName()));
        Account tokenAccount = new Account();
        tokenAccount.setName(loginDto.getAccountName());
        tokenAccount.setPassword(loginDto.getPassword());
   
        if(AccountTypeEnum.ADMIN.getCode().equals(account.getAccountType())){
            ClassUtil.throwBusinessException(StrUtil.isBlank(loginDto.getPhone()),"手机号不能为空");
            ClassUtil.throwBusinessException(StrUtil.isBlank(loginDto.getCode()),"验证码不能为空");
            String key = REIDS_CODE_KEY + loginDto.getAccountName() + "::" + loginDto.getPhone();
            ClassUtil.throwBusinessException(!redisUtil.hasKey(key),"验证码已过期");
            LoginUserRedisDto dto = redisUtil.get(key);
            ClassUtil.throwBusinessException(!dto.getCode().equals(loginDto.getCode()),"验证码不正确");
            account.setLoginUserDtoList(CollUtil.toList(new LoginUserDto(dto.getName(),dto.getPhone())));
            tokenAccount.setName(loginDto.getAccountName()+dto.getPhone());
        }
        Map<String, Object> payload = BeanUtil.beanToMap(tokenAccount);
        
        String token = JWTUtil.createToken(payload, JwtConfig.SECRET_KEY.getBytes());
        redisTemplate.opsForValue().set(JwtConfig.TOKEN_PREFIX + tokenAccount.getName().toLowerCase(), token, JwtConfig.EXPIRE_TIME, TimeUnit.SECONDS);
        redisTemplate.opsForValue().set(JwtConfig.SESSION_PREFIX + token, JSON.toJSONString(account), JwtConfig.EXPIRE_TIME, TimeUnit.SECONDS);
        return token;
    }


    @Override
    public Boolean getCode(PcLoginCodeDto dto) {
        /**
         * 两个方法
         * 1.获取用户的手机号列表
         * 2.发送短信
         */
        Account account = accountMapper.selectOne(Wrappers.lambdaQuery(Account.class).eq(Account::getName, dto.getUsername()));
        ClassUtil.checkData(account,"账号不存在");
        ClassUtil.throwBusinessException(!AccountTypeEnum.ADMIN.getCode().equals(account.getAccountType()),"不是超级管理员，不需要获取验证码");
        List<LoginUserDto> loginUserDtoList = account.getLoginUserDtoList();
        LoginUserDto loginUserDto = null;
        for (LoginUserDto item : loginUserDtoList) {
            if (item.getPhone().equals(dto.getPhone())) {
                loginUserDto = item;
                break;
            }
        }
        ClassUtil.checkData(loginUserDto,"手机号不存在");
        String code = RandomUtil.randomNumbers(6);
        MessageServer.sendMessage(loginUserDto.getPhone(),code);
        String key = REIDS_CODE_KEY + dto.getUsername() + "::" + dto.getPhone();
        LoginUserRedisDto redisDto = new LoginUserRedisDto();
        redisDto.setCode(code);
        redisDto.setName(loginUserDto.getName());
        redisDto.setPhone(loginUserDto.getPhone());
        redisUtil.set(key,redisDto,300);
        return true;
    }
}
