package com.izouma.jmrh.web;

import com.izouma.jmrh.config.Constants;
import com.izouma.jmrh.domain.User;
import com.izouma.jmrh.dto.PageQuery;
import com.izouma.jmrh.exception.AuthenticationException;
import com.izouma.jmrh.exception.BusinessException;
import com.izouma.jmrh.repo.UserRepo;
import com.izouma.jmrh.security.Authority;
import com.izouma.jmrh.security.JwtTokenUtil;
import com.izouma.jmrh.security.JwtUserFactory;
import com.izouma.jmrh.service.UserService;
import com.izouma.jmrh.service.sms.SmsService;
import com.izouma.jmrh.utils.ObjUtils;
import com.izouma.jmrh.utils.SecurityUtils;
import com.izouma.jmrh.utils.excel.ExcelUtils;
import com.warrenstrange.googleauth.GoogleAuthenticator;
import com.warrenstrange.googleauth.GoogleAuthenticatorKey;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

@AllArgsConstructor
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController extends BaseController {
    private UserRepo              userRepo;
    private UserService           userService;
    private JwtTokenUtil          jwtTokenUtil;
    private SmsService            smsService;
    private AuthenticationManager authenticationManager;

    @PostMapping("/register")
    public User register(@RequestParam String username,
                         @RequestParam String password  /*,
                       @RequestParam String phone,
                         @RequestParam String code*/) /*throws SmsService.SmsVerifyException */ {
        /*        smsService.verify(phone, code);*/
        User user = User.builder()
                        .username(username)
                        .nickname(username)
                        .avatar(Constants.DEFAULT_AVATAR)
                        .password(new BCryptPasswordEncoder().encode(password))
                        .enabled(true)
                        /*      .phone(phone)*/
                        .authorities(Collections.singleton(new Authority(Authority.NAMES.ROLE_USER.name())))
                        .build();
        return userRepo.save(user);
    }

    @PreAuthorize("hasRole('ADMIN')")
    @PostMapping("/save")
    public User save(@RequestBody User user) {
        if (user.getId() != null) {
            User orig = userRepo.findById(user.getId()).orElseThrow(new BusinessException("无记录"));
            ObjUtils.merge(orig, user);
            return userRepo.save(orig);
        }
        return userRepo.save(user);
    }

    @PreAuthorize("hasRole('ADMIN')")
    @PostMapping("/del")
    public void save(@RequestParam Long id) {
        userRepo.deleteById(id);
    }

    /*
     * 找回密码
     * */
    @PostMapping("/updatePassWord")
    public void updatePassWord(@RequestParam String mail, @RequestParam String code, @RequestParam String password) {
        userService.updatePassWord(mail, code, password);
    }

    /*
     * 修改密码
     * */
    @PostMapping("/updatePassWordTo")
    public void updatePassWordTo(@RequestParam(name = "username") String username, @RequestParam(name = "password") String password,
                                 @RequestParam(name = "passWordTo") String passWordTo) {
        //smsService.verify(user.getPhone(), code);
        try {
            authenticate(username, password);
            User user = userRepo.findByUsername(username);
            if (user == null) {
                throw new BusinessException("用户不存在，请先注册");
            }
            user.setPassword(new BCryptPasswordEncoder().encode(passWordTo));
            userRepo.updatePassword(user);
        } catch (Exception e) {
            log.error("loginByUserPwd", e);
            throw new AuthenticationException("密码错误", e);
        }

    }

    /**
     * Authenticates the user. If something is wrong, an {@link AuthenticationException} will be thrown
     */
    private void authenticate(String username, String password) {
        Objects.requireNonNull(username);
        Objects.requireNonNull(password);
        try {
            authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
        } catch (DisabledException e) {
            throw new AuthenticationException("User is disabled!", e);
        } catch (BadCredentialsException e) {
            throw new AuthenticationException("Bad credentials!", e);
        }
    }


    /*
     * 修改手机号
     * */
    @PostMapping("/updatePhone")
    public void updatePhone(@RequestParam(name = "phone") String phone, @RequestParam(name = "id") Long id) {
        //smsService.verify(user.getPhone(), code);
        User byPhone = userRepo.findByPhone(phone);
        if (byPhone == null) {
            userRepo.updateUserPhone(phone, id);
        }
    }

    @GetMapping("/my")
    public User my() {
        return userRepo.findById(SecurityUtils.getAuthenticatedUser().getId())
                       .orElseThrow(new BusinessException("用户不存在"));
    }

    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/all")
    public Page<User> all(PageQuery pageQuery) {
        Page<User> all = userRepo.findAll(toSpecification(pageQuery, User.class), toPageRequest(pageQuery));
        return all;
    }

    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/get/{id}")
    public User get(@PathVariable Long id) {
        return userRepo.findById(id).orElseThrow(new BusinessException("无记录"));
    }

    @GetMapping("/excel")
    @ResponseBody
    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
        List<User> data = all(pageQuery).getContent();
        ExcelUtils.export(response, data);
    }

    @PostMapping("/getMaUserInfo")
    @ApiOperation(value = "获取小程序用户信息")
    public User getMaUserInfo(String sessionKey, String rawData, String signature, String encryptedData, String iv) {
        User user = userService.getMaUserInfo(sessionKey, rawData, signature, encryptedData, iv);
        if (user != null) {
            return user;
        }
        throw new BusinessException("获取用户信息失败");
    }

    @PreAuthorize("hasRole('ADMIN')")
    @PostMapping("/setPasswordAdmin")
    public String setPasswordAdmin(@RequestParam Long userId, @RequestParam String password) {
        return userService.setPassword(userId, password);
    }

    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/getToken/{userId}")
    public String getToken(@PathVariable Long userId) {
        return jwtTokenUtil.generateToken(JwtUserFactory.create(userRepo.findById(userId)
                                                                        .orElseThrow(new BusinessException("用户不存在"))));
    }

    @PostMapping("/updateProfile")
    public void updateProfile(String orgName, String occupation,
                              String contactName, String contactPhone,
                              String email, String idNo, String avatar) {
        userService.updateProfile(SecurityUtils.getAuthenticatedUser().getId(), orgName, occupation, contactName,
                contactPhone, email, idNo, avatar);
    }

    @PostMapping("/changePassword")
    public String changePassword(@RequestParam String password, @RequestParam String key, @RequestParam String code) {
        return userService.setPasswordCaptcha(SecurityUtils.getAuthenticatedUser().getId(), key, code, password);
    }

    @GetMapping("/tfaStatus")
    private Map<String, Object> tfaStatus() {
        Map<String, Object> map = new HashMap<>();
        map.put("status", !StringUtils.isBlank(SecurityUtils.getAuthenticatedUser().getGAuthKey()));
        return map;
    }

    @PostMapping("/getTFAKey")
    public String getTFAKey() {
        GoogleAuthenticator gAuth = new GoogleAuthenticator();
        final GoogleAuthenticatorKey key = gAuth.createCredentials();
        return key.getKey();
    }

    @PostMapping("/bindTFA")
    public void bindTFA(@RequestParam String password, @RequestParam String key, @RequestParam String code) {
        userService.bindTFA(password, key, code);
    }

    @PostMapping("/verifyTFA")
    public void verifyTFA(@RequestParam String code) {
        userService.verifyTAF(code);
    }

    @PostMapping("/saveAll")
    public void saveAll() {
        userRepo.saveAll(userRepo.findAll());
    }
}
