package com.eshore.gringotts.web.domain.service;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.eshore.gringotts.web.domain.entity.User;
import com.eshore.gringotts.web.domain.repository.UserRepository;
import java.time.LocalDateTime;
import java.util.Optional;
import lombok.Data;
import org.eclipse.collections.api.factory.Lists;
import org.eclipse.collections.api.list.ImmutableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * User服务
 *
 * @author lanyuanxiaoyao
 * @date 2024-11-14
 */
@Service
public class UserService {
    private static final Logger logger = LoggerFactory.getLogger(UserService.class);

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    private static String encryptPassword(String password) {
        String salt = "tY2gNdkt7x%%HcCAFc";
        return SecureUtil.sha256(StrUtil.format("{}{}", salt, password));
    }

    /**
     * 当数据库里没有用户的时候，增加一个默认的系统管理员
     */
    public void initial() {
        if (userRepository.count() == 0) {
            User user = new User();
            user.setUsername("administrator@eshore.com");
            user.setPassword(encryptPassword("administrator"));
            user.setRole(User.Role.ADMINISTRATOR);
            user.setState(User.State.NORMAL);
            user.setCheckedUser(user);
            user.setCheckedTime(LocalDateTime.now());
            user.setCreatedUser(user);
            user.setModifiedUser(user);
            userRepository.save(user);
        }
    }

    /**
     * 获取当前登陆账号状态
     */
    public UserInformation state() {
        try {
            long id = StpUtil.getLoginIdAsLong();
            User user = userRepository.findById(id).orElseThrow(LoginNotFoundException::new);
            // User user = userRepository.findByUsername("administrator@eshore.com").orElseThrow(LoginNotFoundException::new);
            StpUtil.login(user.getId());
            return new UserInformation(user, StpUtil.getTokenInfo());
        } catch (NotLoginException e) {
            throw new LoginNotFoundException(e);
        }
    }

    public User currentLoginUser() {
        return currentLoginUserOptional().orElseThrow(LoginNotFoundException::new);
    }

    public Optional<User> currentLoginUserOptional() {
        try {
            return userRepository.findById(StpUtil.getLoginIdAsLong());
        } catch (Throwable throwable) {
            return Optional.empty();
        }
    }

    private User findUserByUsername(String username) {
        return userRepository.findByUsername(username).orElseThrow(UserNotFoundException::new);
    }

    /**
     * 登陆操作
     */
    public UserInformation login(String username, String password) {
        User user = userRepository.findByUsername(username).orElseThrow(LoginFailureException::new);
        if (user.getState() == User.State.CHECKING) {
            throw new LoginFailureByCheckingException();
        } else if (user.getState() == User.State.DISABLED) {
            throw new LoginFailureByDisabledException();
        } else if (!StrUtil.equals(encryptPassword(password), user.getPassword())) {
            throw new LoginFailureException();
        } else {
            StpUtil.login(user.getId());
            user.setLastLoginTime(LocalDateTime.now());
            userRepository.save(user);
            return new UserInformation(user, StpUtil.getTokenInfo());
        }
    }

    /**
     * 登出操作
     */
    public void logout() {
        StpUtil.logout();
    }

    /**
     * 注册操作
     */
    public void register(String username, String password, User.Role role) {
        // 不允许通过这个入口注册管理员
        if (User.Role.ADMINISTRATOR.equals(role)) {
            throw new RegisterFailureException();
        }
        User user = new User();
        user.setUsername(username);
        user.setPassword(encryptPassword(password));
        user.setRole(role);
        user.setCreatedUser(user);
        user.setModifiedUser(user);
        userRepository.save(user);
    }

    /**
     * 管理员注册操作
     * 直接成功
     */
    public void registerFromAdministrator(String username, String password, User.Role role) {
        User loginUser = currentLoginUser();
        User user = new User();
        user.setUsername(username);
        user.setPassword(encryptPassword(password));
        user.setRole(role);
        user.setState(User.State.NORMAL);
        user.setCheckedUser(loginUser);
        user.setCheckedTime(LocalDateTime.now());
        userRepository.save(user);
    }

    /**
     * 是否存在用户名
     */
    public boolean exitsUsername(String username) {
        return userRepository.existsByUsername(username);
    }

    /**
     * 禁用账号
     */
    @Transactional
    public void disable(String username) {
        User loginUser = currentLoginUser();
        User user = findUserByUsername(username);
        user.setState(User.State.DISABLED);
        user.setModifiedUser(loginUser);
        userRepository.save(user);
    }

    /**
     * 启用账号
     */
    @Transactional
    public void enable(String username) {
        User loginUser = currentLoginUser();
        User user = findUserByUsername(username);
        user.setState(User.State.NORMAL);
        user.setModifiedUser(loginUser);
        userRepository.save(user);
    }

    @Transactional
    public void check(String username) {
        User loginUser = currentLoginUser();
        User user = findUserByUsername(username);
        user.setState(User.State.NORMAL);
        user.setCheckedUser(loginUser);
        user.setCheckedTime(LocalDateTime.now());
        user.setModifiedUser(loginUser);
        userRepository.save(user);
    }

    public ImmutableList<User> list() {
        return Lists.immutable.ofAll(userRepository.findAll());
    }

    @Transactional
    public void changePassword(String oldPassword, String newPassword) {
        long id = StpUtil.getLoginIdAsLong();
        User user = userRepository.findById(id).orElseThrow(LoginNotFoundException::new);
        if (StrUtil.equals(encryptPassword(oldPassword), user.getPassword())) {
            user.setPassword(encryptPassword(newPassword));
            user.setModifiedUser(user);
            userRepository.save(user);
        } else {
            throw new ChangePasswordFailureException();
        }
    }

    public User detail(String username) {
        return userRepository.findByUsername(username).orElseThrow(UserNotFoundException::new);
    }

    public static class UserNotFoundException extends RuntimeException {
        public UserNotFoundException() {
            super("账号不存在");
        }
    }

    public static class RegisterFailureException extends RuntimeException {
        public RegisterFailureException() {
            super("不允许注册的用户类型");
        }
    }

    public static class LoginFailureException extends RuntimeException {
        public LoginFailureException() {
            super("邮箱或密码错误");
        }
    }

    public static class LoginFailureByCheckingException extends RuntimeException {
        public LoginFailureByCheckingException() {
            super("账号正在审查中");
        }
    }

    public static class LoginFailureByDisabledException extends RuntimeException {
        public LoginFailureByDisabledException() {
            super("账号已被禁用");
        }
    }

    public static class LogoutFailureException extends RuntimeException {
        public LogoutFailureException() {
            super("账号登出失败");
        }
    }

    public static class LoginNotFoundException extends RuntimeException {
        public LoginNotFoundException() {
            super("账号未登陆");
        }

        public LoginNotFoundException(Exception exception) {
            super("账号未登陆", exception);
        }
    }

    public static class ChangePasswordFailureException extends RuntimeException {
        public ChangePasswordFailureException() {
            super("原密码不正确");
        }
    }

    @Data
    public static class UserInformation {
        private String username;
        private User.Role role;
        private String token;

        public UserInformation(User user, SaTokenInfo token) {
            this.username = user.getUsername();
            this.role = user.getRole();
            this.token = token.getTokenValue();
        }
    }
}
