package cn.reghao.devops.web.admin.account.service.impl;

import cn.reghao.devops.web.admin.account.db.repository.RoleRepository;
import cn.reghao.devops.web.admin.account.db.repository.UserRepository;
import cn.reghao.devops.web.admin.account.model.constant.RoleType;
import cn.reghao.devops.web.admin.account.model.dto.CreateAccountDto;
import cn.reghao.devops.web.admin.account.model.po.Role;
import cn.reghao.devops.web.admin.account.model.vo.UserVO;
import cn.reghao.devops.web.admin.account.service.AccountService;
import cn.reghao.devops.web.admin.account.service.AccountSessionService;
import cn.reghao.jutil.jdk.result.Result;
import cn.reghao.jutil.jdk.result.ResultStatus;
import cn.reghao.jutil.jdk.security.RandomString;
import cn.reghao.devops.web.admin.account.model.dto.AccountProfile;
import cn.reghao.devops.web.admin.account.model.dto.AccountRole;
import cn.reghao.devops.web.admin.account.model.po.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.Predicate;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author reghao
 * @date 2020-06-19 16:36:53
 */
@Slf4j
@Service
public class AccountServiceImpl implements AccountService {
    private final UserRepository userRepository;
    private final RoleRepository roleRepository;
    private final PasswordEncoder passwordEncoder;
    private final AccountSessionService accountSessionService;

    public AccountServiceImpl(UserRepository userRepository, RoleRepository roleRepository,
                              PasswordEncoder passwordEncoder, AccountSessionService accountSessionService) {
        this.userRepository = userRepository;
        this.roleRepository = roleRepository;
        this.passwordEncoder = passwordEncoder;
        this.accountSessionService = accountSessionService;
    }

    @Override
    public Result createAccount(CreateAccountDto createAccountDto) {
        int roleId = createAccountDto.getRoleId();
        Role role = roleRepository.findById(roleId).orElse(null);
        if (role == null) {
            return Result.fail("role 不存在");
        }

        if (role.getName().equals(RoleType.ROLE_ADMIN.name())) {
            return Result.fail("只有 admin 用户才能拥有 admin role");
        }

        String username = createAccountDto.getUsername();
        User user = userRepository.findByUsername(username);
        if (user == null) {
            String password = createAccountDto.getPassword();
            String salt = RandomString.getSalt(64);
            String encodedPassword = passwordEncoder.encode(password + salt);
            user = new User(username, encodedPassword, salt, Set.of(role.getName()));
            userRepository.save(user);
            return Result.success();
        }

        String errMsg = String.format("帐号 %s 已存在", username);
        return Result.fail(errMsg);

    }

    @Override
    public void updateAccountPassword(Integer userId, String newPassword) {
        User userEntity = userRepository.findById(userId).orElse(null);
        if (userEntity == null) {
            return;
        }

        String salt = RandomString.getSalt(64);
        String encodedPassword = passwordEncoder.encode(newPassword + salt);
        userEntity.setSalt(salt);
        userEntity.setEncodedPassword(encodedPassword);
        userRepository.save(userEntity);
        accountSessionService.deactiveSession(userEntity);
    }

    @Override
    public void updateAccountProfile(AccountProfile accountProfile) {
        User userEntity = userRepository.findById(accountProfile.getUserId()).orElse(null);
        if (userEntity == null) {
            return;
        }

        userEntity.setScreenName(accountProfile.getScreenName());
        userEntity.setMobile(accountProfile.getMobile());
        userEntity.setEmail(accountProfile.getEmail());
        userRepository.save(userEntity);
    }

    @Override
    public Result updateAccountRole(AccountRole accountRole) {
        int userId = accountRole.getUserId();
        if (userId == 1) {
            return Result.fail("admin 用户的 role 只能是 admin, 不能更改");
        }

        User userEntity = userRepository.findById(userId).orElse(null);
        if (userEntity == null) {
            return Result.fail("用户不存在");
        }

        Role role = accountRole.getRole();
        if (role.getName().equals(RoleType.ROLE_ADMIN.name())) {
            return Result.fail("只有 admin 用户才能拥有 admin role");
        }

        userEntity.getRole().clear();
        userEntity.getRole().add(role.getName());
        userRepository.save(userEntity);
        accountSessionService.deactiveSession(userEntity);
        return Result.success();
    }

    @Override
    public void updateAccountStatus(Integer userId, Boolean enable) {
        User userEntity = userRepository.findById(userId).orElse(null);
        if (userEntity == null) {
            return;
        }

        userEntity.setEnabled(enable);
        userRepository.save(userEntity);
    }

    @Override
    public Result deleteAccount(Integer userId) {
        if (userId == 1) {
            return Result.result(ResultStatus.FAIL, String.format("不能删除系统默认 User"));
        }

        User userEntity = userRepository.findById(userId).orElse(null);
        if (userEntity == null) {
            return Result.result(ResultStatus.FAIL, String.format("User 不存在"));
        }

        userRepository.delete(userEntity);
        return Result.success();
    }

    @Override
    public Page<UserVO> getUserVOByPage(PageRequest pageRequest) {
        return userRepository.findAll(pageRequest).map(UserVO::new);
    }

    @Override
    public List<UserVO> getByMatchScreenName(String screenName) {
        Specification<User> specification = (root, query, cb) -> {
            String likeQuery = String.format("%%%s%%", screenName);
            Predicate predicate = cb.like(root.get("screenName"), likeQuery);
            return cb.and(predicate);
        };
        return userRepository.findAll(specification).stream().map(UserVO::new).collect(Collectors.toList());
    }

    @Override
    public User getById(Integer userId) {
        return userRepository.findById(userId).orElse(null);
    }
}
