package cn.reghao.bnt.web.admin.service.impl;

import cn.reghao.bnt.web.admin.db.repository.RoleRepository;
import cn.reghao.bnt.web.admin.model.constant.RoleType;
import cn.reghao.bnt.web.admin.model.dto.*;
import cn.reghao.bnt.web.admin.model.po.Role;
import cn.reghao.bnt.web.admin.model.vo.UserVO;
import cn.reghao.bnt.web.admin.service.AccountService;
import cn.reghao.bnt.web.admin.service.UserContext;
import cn.reghao.bnt.web.devops.app.model.po.config.AppConfig;
import cn.reghao.bnt.web.devops.app.model.po.config.AppDeployConfig;
import cn.reghao.jutil.jdk.web.result.Result;
import cn.reghao.jutil.jdk.web.result.ResultStatus;
import cn.reghao.jutil.jdk.security.RandomString;
import cn.reghao.bnt.web.admin.db.repository.UserRepository;
import cn.reghao.bnt.web.admin.model.po.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
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 jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.SetJoin;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author reghao
 * @date 2023-02-18 14:42:57
 */
@Slf4j
@Service
public class AccountServiceImpl implements AccountService {
    private final UserRepository userRepository;
    private final RoleRepository roleRepository;
    private final PasswordEncoder passwordEncoder;
    private final int adminUserId = 1;
    private final int adminRoleId = 1;
    private int pageSize = 100;

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

    @Override
    public Result createAccount(CreateAccountDto createAccountDto) {
        List<Role> roles = createAccountDto.getRoles().stream()
                .map(name -> {
                    String role = "ROLE_" + name.toUpperCase(Locale.ROOT);
                    return roleRepository.findByName(role);
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (roles.isEmpty()) {
            return Result.fail("role 不存在");
        }

        String adminRole = RoleType.ROLE_DEVOPS_ADMIN.name().replace("ROLE_", "").toLowerCase(Locale.ROOT);
        if (createAccountDto.getRoles().contains(adminRole)) {
            return Result.fail("只有 admin 用户才能拥有 admin role");
        }

        Set<String> roleNames = roles.stream().map(Role::getName).collect(Collectors.toSet());
        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, roleNames);
            userRepository.save(user);
            return Result.success();
        }

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

    @Override
    public void updateAccountPassword(int 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);
    }

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

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

        Set<String> roleNames = accountRole.getRoles().stream()
                .map(roleName -> {
                    Role role = roleRepository.findByName("ROLE_" + roleName.toUpperCase(Locale.ROOT));
                    return role != null ? role.getName() : null;
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        if (roleNames.isEmpty()) {
            return Result.fail("角色不存在");
        }

        if (roleNames.contains(RoleType.ROLE_DEVOPS_ADMIN.name()) && countUsersByRole(adminRoleId) >= 2) {
            return Result.fail("系统最多允许 2 个用户拥有 admin role");
        }

        userEntity.setRole(roleNames);
        userRepository.save(userEntity);
        return Result.success();
    }

    /**
     * 获取拥有角色的用户
     * SQL 语句
     * select u.*
     * from role r
     * inner join user_role ur
     * inner join `user` u
     * on r.title=ur.role and ur.user_id=u.id and r.title='ROLE_FRONTEND'
     *
     * @param
     * @return
     * @date 2023-03-02 17:54:48
     */
    public List<User> getUsersByRole(Integer roleId, int pageNumber) {
        Role role = roleRepository.findById(roleId).orElse(null);
        if (role == null) {
            return Collections.emptyList();
        }

        String name = role.getName();
        return getUsersByRole(name, pageNumber);
    }

    private List<User> getUsersByRole(String roleName, int pageNumber) {
        Specification<User> specification = ((root, query, cb) -> {
            SetJoin<User, String> setJoin = root.joinSet("role");
            // User 中 Set 的元素是原始类型
            Predicate predicate = setJoin.as(String.class).in(roleName);
            // User 中的 Set 的元素是某个对象, fieldName 表示对象的某个字段名
            //Predicate predicate = cb.equal(setJoin.get("fieldName"), title);
            return cb.and(predicate);
        });

        PageRequest pageRequest = PageRequest.of(pageNumber-1, pageSize);
        Page<User> page = userRepository.findAll(specification, pageRequest);
        return page.getContent();
    }

    @Override
    public List<Integer> getUserIdsByRole(String roleName) {
        return getUsersByRole(roleName, 1).stream().map(User::getId).collect(Collectors.toList());
    }

    @Override
    public int countUsersByRole(int roleId) {
        Role role = roleRepository.findById(roleId).orElse(null);
        if (role == null) {
            return 0;
        }

        String name = role.getName();
        Specification<User> specification = ((root, query, cb) -> {
            SetJoin<User, String> setJoin = root.joinSet("role");
            // User 中 Set 的元素是原始类型
            Predicate predicate = setJoin.as(String.class).in(name);
            // User 中的 Set 的元素是某个对象, fieldName 表示对象的某个字段名
            //Predicate predicate = cb.equal(setJoin.get("fieldName"), title);
            return cb.and(predicate);
        });

        long total = userRepository.count(specification);
        return (int) total;
    }

    @Override
    public synchronized Result updateMyUsername(UpdateUsernameDto updateUsernameDto) {
        User user = UserContext.getUser();
        String username1 = updateUsernameDto.getNewUsername();
        if (user.getUsername().equals(username1)) {
            return Result.fail("新帐号名和当前一致");
        }

        User user1 = userRepository.findByUsername(username1);
        if (user1 != null) {
            return Result.fail("新帐号名已存在");
        }

        User userEntity = userRepository.getById(user.getId());
        userEntity.setUsername(username1);
        userRepository.save(userEntity);
        return Result.success();
    }

    @Override
    public Result updateMyPassword(UpdatePasswordDto updatePasswordDto) {
        String currentPasswd = updatePasswordDto.getCurrentPasswd();
        String newPasswd = updatePasswordDto.getCurrentPasswd();
        if (currentPasswd.equals(newPasswd)) {
            return Result.fail("新密码和旧密码一致");
        }

        User user = UserContext.getUser();
        String encodedPassword = user.getEncodedPassword();
        String currentSalt = user.getSalt();
        String currentPasswdEncoded = passwordEncoder.encode(currentPasswd + currentSalt);
        if (!currentPasswdEncoded.equals(encodedPassword)) {
            return Result.fail("输入的当前密码不正确");
        }

        String salt = RandomString.getSalt(64);
        String newPasswdEncoded = passwordEncoder.encode(newPasswd + salt);

        User userEntity = userRepository.getById(user.getId());
        userEntity.setSalt(salt);
        userEntity.setEncodedPassword(newPasswdEncoded);
        userRepository.save(userEntity);
        return Result.success();
    }

    @Override
    public Result deleteAccount(int userId) {
        if (userId == adminUserId) {
            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 不存在"));
        }

        userEntity.setRole(new HashSet<>());
        userEntity.setDeleted(true);

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

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

    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(int userId) {
        return userRepository.findById(userId).orElse(null);
    }

    @Override
    public User getByUsername(String username) {
        return userRepository.findByDeletedIsFalseAndUsername(username);
    }

    public AccountInfo getAccountInfo() {
        User user = UserContext.getUser();
        return user != null ? new AccountInfo(user) : null;
    }
}
