package com.bcxin.dbfilescanner.configs;

import cn.hutool.Hutool;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bcxin.dbfilescanner.dtos.SecurityManPictureDto;
import com.bcxin.dbfilescanner.exceptions.BadUrlException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.retry.RetryPolicy;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
 * 旧系统迁移数据在职已认证的保安员的头像不在内网的抓出来，目的 => 批量把头像数据补录进内网；
 * 区分旧系统迁移数据的方式:
 * 保安员ID的组成: tenant_users.id+'___'+tenant_employees.id+"--__qFCcCnozYNwzoG3O6Ms"
 * 对应迁移数据: tenant_users.id中会包含: --_
 * 如果是后续新入职平台的用户不会包含: --_
 * 因此；我们通过--_和___在保安员表中Id字段所在的索引位置来进行区分:
 * 当INSTR(m.ID,'--__')< INSTR(m.ID,'___')的时候; 则表示迁移数据
 */
@Component
public class DbScannerCommandLine implements CommandLineRunner, BeanFactoryAware {
    private final Logger logger = LoggerFactory.getLogger(DbScannerCommandLine.class);
    private final String api;

    public DbScannerCommandLine(@Value("${app.api}")
                                String api) {
        this.api = api;
    }

    @Override
    public void run(String... args) throws Exception {
        logger.info("开始分页获取已核验已认证的迁移保安员信息数据");
        AtomicLong executeCount = new AtomicLong(0);
        Collection<Collection<SecurityManPictureDto>> parallelSecurities = null;
        do {
            try {
                parallelSecurities = getMatchedAuthedUsers();

                parallelSecurities.parallelStream().forEach(is -> {
                    checkedIfResourceExistsAndUpdateStatus(is);
                });
            } catch (Exception ex) {
                ex.printStackTrace();
            } finally {
                executeCount.incrementAndGet();
                logger.info(String.format("第%s次执行已核验已认证迁移数据的检测", executeCount.get()));
            }
        }
        while (!CollectionUtils.isEmpty(parallelSecurities));

        logger.info(String.format("完成检查已核验已认证的保安员数据:%s", executeCount.get()));
    }


    /**
     * 取出内网已核验已认证的保安员的头像列表
     *
     * @return
     */
    private Collection<Collection<SecurityManPictureDto>> getMatchedAuthedUsers() {
        String sql = "select m.ID, m.ITEM_pirture " +
                " from tlk_securityman m where m.ITEM_isCertified='已核验' and m.ITEM_shiming='已认证'" +
                " and INSTR(m.ID,'--__')< INSTR(m.ID,'___') and AUDITUSER is null limit 500";

        JdbcTemplate jdbcTemplate = this.beanFactory.getBean(JdbcTemplate.class);
        /**
         * 读取已核验,已认证的迁移的人员数据
         */
        List<Map<String, Object>> mapLists = jdbcTemplate.queryForList(sql);
        Collection<Collection<SecurityManPictureDto>> multipleSecurities = new ArrayList<>();
        Collection<SecurityManPictureDto> sectionSecurities = null;
        for (int index = 0; index < mapLists.size(); index++) {
            if (index % 100 == 0) {
                if (!CollectionUtils.isEmpty(sectionSecurities)) {
                    multipleSecurities.add(sectionSecurities);
                }

                sectionSecurities = new ArrayList<>();
            }

            Map<String, Object> mp = mapLists.get(index);
            SecurityManPictureDto securityManPictureDto = null;
            try {
                securityManPictureDto = extractPaths(mp);
            } catch (Exception ex) {
                String id = String.valueOf(mp.get("id"));
                System.err.println(String.format("当前异常保安员Id=%s", id));
                ex.printStackTrace();
                securityManPictureDto = SecurityManPictureDto.createError(id);
            } finally {
                sectionSecurities.add(securityManPictureDto);
            }
        }

        if (!CollectionUtils.isEmpty(sectionSecurities)) {
            multipleSecurities.add(sectionSecurities);
        }

        return multipleSecurities;
    }

    /**
     * 截取附件路径
     *
     * @param map
     * @return
     */
    private SecurityManPictureDto extractPaths(Map<String, Object> map) {
        Object pics = map.get("ITEM_pirture");
        String id = String.valueOf(map.get("id"));
        try {
            if (pics == null) {
                return SecurityManPictureDto.createError(id);
            }

            JSONArray array = JSON.parseArray(String.valueOf(pics));
            Collection<String> paths =
                    array.stream().map(ii -> String.valueOf(((JSONObject) ii).get("path")))
                            .filter(ii -> StringUtils.hasLength(ii))
                            .map(ii -> {
                                if (ii.startsWith("http")) {
                                    return ii;
                                }
                                if (ii.startsWith("obpm")) {
                                    return String.format("%s/%s", api, ii);
                                }

                                return String.format("%s/obpm/%s", api, ii);
                            }).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(paths)) {
                return SecurityManPictureDto.createError(id);
            }

            return SecurityManPictureDto.create(id, paths);
        } catch (Exception ex) {
            throw new BadUrlException(String.valueOf(map.get("id")), pics == null ? "NULL" : String.valueOf(pics));
        }
    }

    private void checkedIfResourceExistsAndUpdateStatus(Collection<SecurityManPictureDto> securityManPictureDtos) {
        if (CollectionUtils.isEmpty(securityManPictureDtos)) {
            return;
        }

        logger.info("开始核对获得的保安员信息");
        Collection<SecurityManPictureDto> checkedSecurities = new ArrayList<>();
        for (SecurityManPictureDto securityManPictureDto : securityManPictureDtos) {
            logger.info("正在核对 {} 的头像信息:{}", securityManPictureDto.getId(), securityManPictureDto.getPaths());
            if (securityManPictureDto.isExists()) {
                if (!isResourceAllExists(securityManPictureDto.getPaths())) {
                    securityManPictureDto.makeAsNotExists();
                } else {
                    securityManPictureDto.makeWellDone();
                }
            }
            checkedSecurities.add(securityManPictureDto);
            if (checkedSecurities.size() % 50 == 0) {
                updateSecurityCheckedStatus(checkedSecurities);
                checkedSecurities.clear();
            }

            logger.info("核对结果为:{}", securityManPictureDto.isExists() ? "附件正常" : "不正常");
        }

        if (checkedSecurities.size() > 0) {
            updateSecurityCheckedStatus(checkedSecurities);
            checkedSecurities.clear();
        }

        logger.info("完成获得的保安员信息核对");
    }

    private boolean isResourceAllExists(Collection<String> paths) {
        StringBuilder sb = new StringBuilder();
        try {
            for (String p : paths) {
                if (!StringUtils.hasLength(p)) {
                    continue;
                }

                sb.append(String.format("下载文件:%s;", p));
                HttpResponse httpResponse =
                        HttpUtil.createGet(p, true)
                                .header("accessToken", "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhdXRoMCIsImV4cCI6MTY2Njg2MDAwMSwidXNlcm5hbWUiOiI3TkVMR1RYaSJ9.Fvx16149p8Grp-a_QwFRGro_sWD1xqL2QV5beL9bRcc")
                                .executeAsync();
                if (httpResponse != null &&
                        (!httpResponse.isOk() ||
                                httpResponse.contentLength() == 0)) {
                    return false;
                }
            }
        } catch (Exception ex) {
            System.err.println(String.format("下载异常:%s", sb));
            ex.printStackTrace();

            return false;
        }

        return true;
    }

    private void updateSecurityCheckedStatus(Collection<SecurityManPictureDto> checkedSecurities) {
        logger.info("即将分批更改状态的保安员数量:{}", checkedSecurities.size());
        RetryTemplate retryTemplate = new RetryTemplate();

        FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
        fixedBackOffPolicy.setBackOffPeriod(2000l);
        retryTemplate.setBackOffPolicy(fixedBackOffPolicy);

        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(10);
        retryTemplate.setRetryPolicy(retryPolicy);
        String[] batchSqls =
                checkedSecurities.stream().map(ii -> {
                    return String.format("UPDATE tlk_securityman SET AUDITUSER='%s' where id='%s'",
                            ii.isExists() ? "1" : "0", ii.getId()
                    );
                }).collect(Collectors.toList()).toArray(new String[]{});


        retryTemplate.execute(rct -> {
            try {
                JdbcTemplate jdbcTemplate = this.beanFactory.getBean(JdbcTemplate.class);
                jdbcTemplate.batchUpdate(batchSqls);
                logger.info("完成-即将分批更改状态的保安员数量:{}", checkedSecurities.size());
            } catch (Exception ex) {
                logger.error(String.format("执行批量脚本(sql=%s)发生异常; ", Arrays.stream(batchSqls).collect(Collectors.joining(";")),
                        rct.getRetryCount()));
                ex.printStackTrace();
            }

            return true;
        });
    }

    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

/*
    public static void main(String[] args) {
        DbScannerCommandLine x = new DbScannerCommandLine(null);
        Collection<String> pathes = Stream.of(
                        "https://bcxin-saas-prod.obs.cn-north-1.myhuaweicloud.com/upload%2F2021-12-15%2F1639538583170025645.jpg")
                .collect(Collectors.toList());
        boolean bk = x.isResourceAllExists(pathes);

        System.err.println(bk);
        SecurityManPictureDto sed = SecurityManPictureDto.create("dd", pathes);
        x.updateSecurityCheckedStatus(Stream.of(sed).collect(Collectors.toList()));
        Map dds = new HashMap();
        dds.put("id", "ddd");
        dds.put("ITEM_pirture", "[{\"name\":\"upload%2F2021-12-15%2F1639538583170025645.jpg\",\"originalPath\":\"/v2/sync/file/download?f=https://bcxin-saas-prod.obs.cn-north-1.myhuaweicloud.com:443/upload%252F2021-12-15%252F1639538583170025645.jpg\",\"path\":\"/uploads/2021-12-15/1639538583170025645.jpg\",\"uid\":\"manuallyupload%2F2021-12-15%2F1639538583170025645.jpg\"}]");
        SecurityManPictureDto dt = x.extractPaths(dds);
        System.err.println(dt);
    }
 */
}
