package com.izouma.nineth.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.github.kevinsawicki.http.HttpRequest;
import com.izouma.nineth.annotations.RedisLock;
import com.izouma.nineth.domain.IdentityAuth;
import com.izouma.nineth.domain.User;
import com.izouma.nineth.dto.PageQuery;
import com.izouma.nineth.enums.AuthStatus;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.repo.IdentityAuthRepo;
import com.izouma.nineth.repo.UserRepo;
import com.izouma.nineth.utils.DateTimeUtils;
import com.izouma.nineth.utils.JpaUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.springframework.core.env.Environment;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
@Slf4j
public class IdentityAuthService {

    private IdentityAuthRepo              identityAuthRepo;
    private UserRepo                      userRepo;
    private UserService                   userService;
    private AdapayService                 adapayService;
    private RedisTemplate<String, Object> redisTemplate;
    private Environment                   env;
    private SysConfigService              sysConfigService;
    private CacheService                  cacheService;

    public Page<IdentityAuth> all(PageQuery pageQuery) {
        return identityAuthRepo
                .findAll(JpaUtils.toSpecification(pageQuery, IdentityAuth.class), JpaUtils.toPageRequest(pageQuery));
    }

    public void apply(IdentityAuth identityAuth) {
        if (identityAuth.getUserId() == null) {
            throw new BusinessException("用户不存在");
        }
        User user = userRepo.findByIdAndDelFalse(identityAuth.getUserId()).orElseThrow(new BusinessException("用户不存在"));
        List<IdentityAuth> auths = identityAuthRepo.findByUserIdAndDelFalse(identityAuth.getUserId());
        auths.stream().filter(auth -> auth.getStatus() == AuthStatus.PENDING).findAny().ifPresent(a -> {
            throw new BusinessException("正在审核中，请勿重复提交");
        });
        auths.stream().filter(auth -> auth.getStatus() == AuthStatus.SUCCESS).findAny().ifPresent(a -> {
            throw new BusinessException("已认证，请勿重复提交");
        });
        identityAuth.setStatus(AuthStatus.PENDING);
        identityAuthRepo.save(identityAuth);
        user.setAuthStatus(AuthStatus.PENDING);
        userService.save(user);
        cacheService.clearUserMy(user.getId());

        identityAuthRepo.deleteDuplicated(identityAuth.getUserId(), identityAuth.getId());
    }

    public void audit(Long id, AuthStatus status, String reason) {
        IdentityAuth auth = identityAuthRepo.findByIdAndDelFalse(id).orElseThrow(new BusinessException("申请不存在"));
        if (auth.getStatus() != AuthStatus.PENDING) {
            throw new BusinessException("已经审核过");
        }
        User user = userRepo.findByIdAndDelFalse(auth.getUserId()).orElseThrow(new BusinessException("用户不存在"));
        if (user.getAuthStatus() != AuthStatus.SUCCESS) {
            if (status == AuthStatus.SUCCESS) {
                user.setAuthId(auth.getId());
            }
            user.setAuthStatus(status);
            userService.save(user);
        }
        auth.setStatus(status);
        auth.setReason(reason);
        auth.setAutoValidated(true);
        identityAuthRepo.save(auth);
        cacheService.clearUserMy(user.getId());

        identityAuthRepo.deleteDuplicated(auth.getUserId(), auth.getId());
    }

    public List<User> repeat(String idNo, Long userId) {
        List<IdentityAuth> auths = identityAuthRepo.findAllByIdNoAndUserIdIsNotAndDelFalse(idNo, userId);
        if (auths.isEmpty()) {
            return null;
        }
        List<Long> userIds = auths.stream().map(IdentityAuth::getUserId).distinct().collect(Collectors.toList());
        return userRepo.findByIdInAndDelFalse(userIds);
    }

    //    public void validate(String name, String phone, String idno) {
//        String body = HttpRequest.post("https://jubrige.market.alicloudapi.com/mobile/3-validate-transfer")
//                .header("Authorization", "APPCODE b48bc8f6759345a79ae20a951f03dabe")
//                .contentType(HttpRequest.CONTENT_TYPE_FORM)
//                .form("idCardNo", idno)
//                .form("mobile", phone)
//                .form("name", name)
//                .body();
//        JSONObject jsonObject = JSONObject.parseObject(body);
//        if (jsonObject.getInteger("code") != 200) {
//            String msg = jsonObject.getString("msg");
//            throw new BusinessException(msg);
//        } else {
//            JSONObject data = jsonObject.getJSONObject("data");
//            int result = data.getIntValue("result");
//            String desc = data.getString("desc");
//            if (result != 0) {
//                throw new BusinessException(desc);
//            } else {
//                log.info("{} {} {} 实名认证通过", name, phone, idno);
//            }
//        }
//    }

    public static void validateV2(String name, String phone, String idno) {
        String body = HttpRequest.post("https://zid.market.alicloudapi.com/idcheck/Post")
                .header("Authorization", "APPCODE af29c2d37c4f415fac930d82f01fb559")
                .contentType(HttpRequest.CONTENT_TYPE_FORM)
                .form("cardNo", idno)
                .form("realName", name)
                .body();
        JSONObject jsonObject = JSONObject.parseObject(body);
        log.info("validate {} {} \n{}", name, idno, JSON.toJSONString(jsonObject, SerializerFeature.PrettyFormat));
        if (jsonObject.getInteger("error_code") != 0) {
            String msg = jsonObject.getString("reason");
            throw new BusinessException(msg);
        } else {
            JSONObject data = jsonObject.getJSONObject("result");
            boolean isOK = Optional.ofNullable(data.getBoolean("isok")).orElse(Boolean.FALSE);
            if (!isOK) {
                throw new BusinessException("不匹配");
            } else {
                log.info("{} {} {} 实名认证通过", name, phone, idno);
            }
        }
    }

    public static void validate(String name, String phone, String idno) {
        HttpRequest request = HttpRequest.get("https://mobilecert.market.alicloudapi.com/mobile3MetaSimple?userName="
                        + URLEncoder.encode(name, StandardCharsets.UTF_8)
                        + "&identifyNum=" + idno + "&mobile=" + phone)
                .header("Authorization", "APPCODE af29c2d37c4f415fac930d82f01fb559");
        String body = request.body();
        if (request.code() != 200) {
            throw new BusinessException(request.code() + "", request.code());
        }
        JSONObject jsonObject = JSONObject.parseObject(body);
        log.info("validate {} {} \n{}", name, idno, JSON.toJSONString(jsonObject, SerializerFeature.PrettyFormat));
        if (jsonObject.getInteger("code") != 200) {
            String msg = jsonObject.getString("message");
            throw new BusinessException(msg);
        } else {
            JSONObject data = jsonObject.getJSONObject("data");
            Integer bizCode = Optional.ofNullable(data.getInteger("bizCode")).orElse(3);
            if (bizCode == 1) {
                log.info("{} {} {} 实名认证通过", name, phone, idno);
                return;
            }
        }
        throw new BusinessException("不匹配");
    }

    public void removeDuplicated() {
        boolean hasMore = true;
        int pageNum = 0;
        AtomicInteger count = new AtomicInteger();
        while (hasMore) {
            Page<Long> page = identityAuthRepo.listUserId(PageRequest.of(pageNum, 100));
            List<Long> userIds = page.getContent();
            userIds.forEach(userId -> {
                userRepo.findById(userId).ifPresent(user -> {
                    log.info("removeDuplicated {}/{} ", count.incrementAndGet(), page.getTotalElements());
                    List<IdentityAuth> list = identityAuthRepo.findByUserId(userId);
                    if (list.size() > 1) {
                        IdentityAuth auth = list.stream()
                                .filter(i -> i.getStatus() == AuthStatus.SUCCESS)
                                .findAny().orElse(null);
                        if (auth != null) {
                            userRepo.setAuthStatus(user.getId(), auth.getStatus(), auth.getId());
                            int num = identityAuthRepo.deleteDuplicated(user.getId(), auth.getId());
                            log.info("deleted {}", num);
                            return;
                        }

                        auth = list.stream()
                                .filter(i -> i.getStatus() == AuthStatus.PENDING)
                                .findAny().orElse(null);
                        if (auth != null) {
                            userRepo.setAuthStatus(user.getId(), auth.getStatus(), auth.getId());
                            int num = identityAuthRepo.deleteDuplicated(user.getId(), auth.getId());
                            log.info("deleted {}", num);
                            return;
                        }

                        auth = list.stream()
                                .filter(i -> i.getStatus() == AuthStatus.FAIL)
                                .findAny().orElse(null);
                        if (auth != null) {
                            userRepo.setAuthStatus(user.getId(), auth.getStatus(), auth.getId());
                            int num = identityAuthRepo.deleteDuplicated(user.getId(), auth.getId());
                            log.info("deleted {}", num);
                            return;
                        }

                    } else if (list.size() == 1) {
                        userRepo.setAuthStatus(user.getId(), list.get(0).getStatus(), list.get(0).getId());
                    }
                });
            });
            hasMore = page.hasNext();
            pageNum++;
        }
    }

    @Scheduled(fixedRate = 60000)
    @RedisLock(value = "autoValidate", expire = 30, unit = TimeUnit.MINUTES)
    public void autoValidate() {
        if (!sysConfigService.getBoolean("auto_validate")) return;
        log.info("autoValidate");
        if (Arrays.asList(env.getActiveProfiles()).contains("dev")) {
            return;
        }
        try {
            List<IdentityAuth> list = identityAuthRepo.findByStatusAndAutoValidated(AuthStatus.PENDING, false);
            new ForkJoinPool(2).submit(() -> {
                list.parallelStream().forEach(identityAuth -> {
                    Map<String, Object> map = auth(identityAuth);
                    audit(identityAuth.getId(), (AuthStatus) map.get("status"), (String) map.get("reason"));
                });
            }).get();

        } catch (Exception e) {
            log.error("批量自动实名出错", e);
        }
    }

    public Map<String, Object> auth(IdentityAuth identityAuth) {
        log.info("实名 {}", identityAuth.getRealName());
        Map<String, Object> result = new HashMap<>();
        String reason = null;
        User user = userRepo.findById(identityAuth.getUserId()).orElseThrow(new BusinessException("用户不存在"));
        if (user.getAuthStatus() == AuthStatus.SUCCESS) {
            result.put("status", AuthStatus.SUCCESS);
        } else if (identityAuth.getIdNo() == null ||
                !Pattern.matches("[1-9]{1}[0-9]{5}(19|20)[0-9]{2}((0[1-9]{1})|(1[0-2]{1}))((0[1-9]{1})|([1-2]{1}[0-9]{1}|(3[0-1]{1})))[0-9]{3}[0-9x]{1}",
                        identityAuth.getIdNo().toLowerCase())) {
            result.put("status", AuthStatus.FAIL);
            result.put("reason", "身份证格式错误");
        } else {
            LocalDate birth = DateTimeUtils.toLocalDate(identityAuth.getIdNo().substring(6, 14), "yyyyMMdd");
            long age = ChronoUnit.YEARS.between(birth, LocalDate.now());
            if (user.getPhone().startsWith("170") ||
                    user.getPhone().startsWith("171") ||
                    user.getPhone().startsWith("162") ||
                    user.getPhone().startsWith("165") ||
                    user.getPhone().startsWith("167")) {
                result.put("status", AuthStatus.FAIL);
                result.put("reason", "虚拟号");
            } else if (age < 18) {
                result.put("status", AuthStatus.FAIL);
                result.put("reason", "未满18岁");
            } else if (age > 60) {
                result.put("status", AuthStatus.FAIL);
                result.put("reason", "超过60岁");
            } else {
                int count = identityAuthRepo.countByIdNoAndStatus(identityAuth.getIdNo(), AuthStatus.SUCCESS);

                if (count >= 3) {
                    result.put("status", AuthStatus.FAIL);
                    result.put("reason", "同一身份证注册超过3个");
                } else {
                    try {
                        validateV2(identityAuth.getRealName(), identityAuth.getPhone(), identityAuth.getIdNo());
                        result.put("status", AuthStatus.SUCCESS);
                    } catch (Exception e) {
                        log.error("自动实名出错", e);
                        if (e instanceof BusinessException && ((BusinessException) e).getCode() == 403) {
                            result.put("status", AuthStatus.PENDING);
                        } else {
                            result.put("status", AuthStatus.FAIL);
                            result.put("reason", e.getMessage());
                        }
                    }
                }
            }
        }
        return result;
    }
}
