package cn.reghao.tnb.account.app.db.repository;

import cn.reghao.tnb.account.api.dto.AccountInfo;
import cn.reghao.tnb.account.app.db.mapper.UserAccountMapper;
import cn.reghao.tnb.account.app.db.mapper.UserAccountRoleMapper;
import cn.reghao.tnb.account.app.db.mapper.UserRegistryMapper;
import cn.reghao.tnb.account.app.model.po.UserAccount;
import cn.reghao.tnb.account.app.model.po.UserAccountRole;
import cn.reghao.tnb.account.app.model.po.UserAuthority;
import cn.reghao.tnb.account.app.model.po.UserRegistry;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author reghao
 * @date 2023-06-01 14:12:40
 */
@Repository
public class AccountRepository {
    private final UserAccountMapper userAccountMapper;
    private final UserRegistryMapper userRegistryMapper;
    private final UserAccountRoleMapper userAccountRoleMapper;

    public AccountRepository(UserAccountMapper userAccountMapper, UserRegistryMapper userRegistryMapper,
                             UserAccountRoleMapper userAccountRoleMapper) {
        this.userAccountMapper = userAccountMapper;
        this.userRegistryMapper = userRegistryMapper;
        this.userAccountRoleMapper = userAccountRoleMapper;
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveAccount(UserAccount userAccount) {
        long userId = userAccount.getUserId();
        Set<UserAccountRole> set = userAccount.getAuthorities().stream()
                .map(role -> new UserAccountRole(userId, role.getAuthority()))
                .collect(Collectors.toSet());

        userAccountMapper.save(userAccount);
        userAccountRoleMapper.saveAll(new ArrayList<>(set));
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveAccounts(List<UserAccount> userAccounts) {
        userAccountMapper.saveAll(userAccounts);
    }

    @CacheEvict(cacheNames = "tnb:account:info", key = "#userId")
    public void updateUserScreenName(long userId, String screenName) {
        userAccountMapper.updateUserScreenName(userId, screenName);
    }

    @CacheEvict(cacheNames = "tnb:account:info", key = "#userId")
    public void updateUserAvatar(long userId, String avatarUrl) {
        userAccountMapper.updateUserAvatar(userId, avatarUrl);
    }

    public void addUserRole(long userId, String role) {
        UserAccountRole userAccountRole = new UserAccountRole(userId, role);
        userAccountRoleMapper.save(userAccountRole);
    }

    public void updateUserRegistry() {
        UserRegistry userRegistry = getUserRegistry();
        userRegistryMapper.update(userRegistry);
    }

    public synchronized long getNextUserId() {
        long userId;
        Long maxUserId = userAccountMapper.findMaxUserId();
        if (maxUserId == null) {
            userId = 10001L;
        } else {
            userId = maxUserId + 1;
        }

        return userId;
    }

    @Cacheable(cacheNames = "tnb:account:info", key = "#userId", unless = "#result == null")
    public AccountInfo getAccountInfo(long userId) {
        AccountInfo accountInfo = userAccountMapper.findAccountInfo(userId);
        return accountInfo;
    }

    public Set<String> getUserRoles(long userId) {
        return userAccountRoleMapper.findByUserId(userId).stream()
                .map(UserAccountRole::getName)
                .collect(Collectors.toSet());
    }

    public UserRegistry getUserRegistry() {
        List<UserRegistry> list = userRegistryMapper.findAll();
        if (list.isEmpty()) {
            userRegistryMapper.save(new UserRegistry());
        }

        return userRegistryMapper.findAll().get(0);
    }

    public long getUserIdByUsername(String username) {
        return userAccountMapper.findUserIdByUsername(username);
    }

    public List<AccountInfo> getAccountInfos(List<Long> userIds) {
        return userAccountMapper.findAccountInfos(userIds);
    }

    public AccountInfo getAccountInfoByMobile(String mobile) {
        AccountInfo accountInfo = userAccountMapper.findAccountInfoByMobile(mobile);
        return accountInfo;
    }

    public AccountInfo getAccountInfoByUsername(String username) {
        AccountInfo accountInfo = userAccountMapper.findAccountInfoByUsername(username);
        return accountInfo;
    }

    public UserAccount getUserAccountByUserId(long userId) {
        UserAccount userAccount = userAccountMapper.findByUserId(userId);
        return userAccount != null ? setAccountAuthorities(userAccount) : null;
    }

    public UserAccount getUserAccountByUsername(String username) {
        UserAccount userAccount = userAccountMapper.findByUsername(username);
        return userAccount != null ? setAccountAuthorities(userAccount) : null;
    }

    public UserAccount getUserAccountByEmail(String email) {
        UserAccount userAccount = userAccountMapper.findByEmail(email);
        return userAccount != null ? setAccountAuthorities(userAccount) : null;
    }

    public UserAccount getUserAccountByMobile(String mobile) {
        UserAccount userAccount = userAccountMapper.findByMobile(mobile);
        return userAccount != null ? setAccountAuthorities(userAccount) : null;
    }

    private UserAccount setAccountAuthorities(UserAccount userAccount) {
        Set<UserAuthority> set = userAccountRoleMapper.findByUserId(userAccount.getUserId()).stream()
                .map(userAccountRole -> new UserAuthority(userAccountRole.getName()))
                .collect(Collectors.toSet());
        userAccount.setAuthorities(set);
        return userAccount;
    }
}
