package cn.com.ctop.material.service.impl;

import cn.com.ctop.material.entity.MaterialUploadTask;
import cn.com.ctop.material.entity.TaskVo;
import cn.com.ctop.material.enums.MaterialEnum;
import cn.com.ctop.material.mapper.MaterialUploadTaskMapper;
import cn.com.ctop.material.service.IMaterialUploadTaskService;
import cn.com.ctop.material.utils.Check;
import cn.com.ctop.material.utils.HttpConstant;
import cn.com.ctop.material.utils.HttpUtils;
import cn.com.ctop.material.utils.LoadFileUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 同步素材任务
 *
 * @author jeecg-boot 2021-10-21
 * @version V1.0
 */
@Service
@Slf4j
public class MaterialUploadTaskServiceImpl
        extends ServiceImpl<MaterialUploadTaskMapper, MaterialUploadTask>
        implements IMaterialUploadTaskService {
    @Autowired
    private MaterialUploadTaskMapper materialUploadTaskMapper;
    private static String downloadUrl = "/data/file/video/";
    // private static String downloadUrl = "D:\\file";
    private static ExecutorService kuaishouExecutorService = Executors.newFixedThreadPool(5);
    private static ExecutorService bytedanceExecutorService = Executors.newFixedThreadPool(5);
    private static ExecutorService submitExecutorService = Executors.newFixedThreadPool(5);
    private static ExecutorService bindExecutorService = Executors.newFixedThreadPool(20);

    @Override
    public void upload(String signature) {
        submitExecutorService.submit(
                new Runnable() {
                    @Override
                    public void run() {
                        TaskVo taskVo = getTaskList(signature);
                        if (Check.isNull(taskVo)) {
                            log.info("此素材md5未获取到待上传的任务列表，signature：{}", signature);
                            return;
                        }
                        List<MaterialUploadTask> taskList = taskVo.getTaskList();
                        if (Check.isNull(taskList)) {
                            log.info("此素材md5未获取到待上传的任务列表，signature：{}", signature);
                            return;
                        }
                        MaterialUploadTask materialUploadTask = taskList.get(0);
                        String materialUrl = materialUploadTask.getMaterialUrl();
                        String localUrl = LoadFileUtil.downLoadFromUrl(materialUrl, downloadUrl);
                        File file = new File(localUrl);
                        FileSystemResource resource = null;
                        Integer materialType = null;
                        if (!Check.isNull(taskVo.getKuaiShou())) {
                            resource = new FileSystemResource(file);
                            JSONObject sizeJson = materialUploadTaskMapper.getVideoSize(signature);
                            if (!Check.isNull(sizeJson)) {
                                Integer width = sizeJson.getInteger("width");
                                Integer height = sizeJson.getInteger("height");
                                Integer type = MaterialEnum.getTypeBySize(width, height);
                                if (!Check.isNull(type) && type != 0) {
                                    materialType = type;
                                }
                            }
                        }
                        FileBody fileBody = null;
                        if (!Check.isNull(taskVo.getBytedance())) {
                            fileBody = new FileBody(file);
                        }
                        for (MaterialUploadTask task : taskList) {
                            if (task.getMediaId() == 2 || task.getMediaId() == 4) {
                                uploadKuaiShou(task, resource, materialType);
                            } else if (task.getMediaId() == 1 || task.getMediaId() == 3) {
                                uploadBytedance(fileBody, task);
                            }
                        }
                    }
                });
    }

    @Override
    public void uploadKuaiShou(MaterialUploadTask task, FileSystemResource resource, Integer type) {
        kuaishouExecutorService.submit(
                new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Long accountId = task.getAccountId();
                            if (Check.isNull(accountId)) {
                                log.error("账户不能为空。");
                                return;
                            }
                            String token = materialUploadTaskMapper.getKuaiShouTokenByAccountId(accountId);
                            if (Check.isNull(token)) {
                                log.error("此账户id未获取到token,accountId:{}", accountId);
                                return;
                            }
                            Map<String, String> headerMap = new HashMap<>();
                            headerMap.put("Content-Type", "multipart/form-data");
                            headerMap.put("Access-Token", token);
                            JSONObject requestJson = new JSONObject();
                            requestJson.put("file", resource);
                            requestJson.put("signature", task.getSignature());
                            if (!Check.isNull(task.getShieldBackwardSwitch())) {
                                if (task.getShieldBackwardSwitch() == 1) {
                                    requestJson.put("shield_backward_switch", true);
                                } else {
                                    requestJson.put("shield_backward_switch", false);
                                }
                            }
                            if (!Check.isNull(type)) {
                                requestJson.put("type", type);
                            }
                            requestJson.put("photo_name", task.getMaterialName());
                            requestJson.put("advertiser_id", accountId);
                            String url = HttpConstant.VIDEO_UPLOAD;
                            String result = exceptInfoForRestTemplate(url, requestJson, headerMap);
                            JSONObject resultJson = JSONObject.parseObject(result);
                            if (!Check.isNull(resultJson)) {
                                Integer code = resultJson.getInteger("code");
                                if (code == 0) {
                                    materialUploadTaskMapper.syncKuaishou(task.getSignature());
                                    materialUploadTaskMapper.updateStatusById(task.getId(), 3, "同步成功", new Date());
                                    log.info(
                                            "快手素材同步完成，accountId：{}，code：{},返回信息：{}",
                                            accountId,
                                            task.getSignature(),
                                            resultJson);
                                } else {
                                    String message = resultJson.getString("message");
                                    if (code == 400001 && message.equals("文件操作过于频繁")) {
                                        materialUploadTaskMapper.updateStatusById(task.getId(), 0, "文件操作频繁-待重试", new Date());
                                    } else if (code == 500000 && message.equals("系统错误")) {
                                        materialUploadTaskMapper.updateStatusById(task.getId(), 0, "系统错误-待重试", new Date());
                                    } else if (code == 500000 && message.contains("限流")) {
                                        materialUploadTaskMapper.updateStatusById(task.getId(), 0, "触发限流-待重试", new Date());
                                    } else if (code == 400001 && message.contains("频繁")) {
                                        materialUploadTaskMapper.updateStatusById(task.getId(), 0, "接口请求频繁-待重试", new Date());
                                    } else {
                                        materialUploadTaskMapper.updateStatusById(task.getId(), 4, message, new Date());
                                    }
                                    log.error("快手同步素材失败，返回信息：{}，请求参数：{}", resultJson, requestJson);
                                }
                            } else {
                                materialUploadTaskMapper.updateStatusById(
                                        task.getId(), 0, "返回结果为空-待重试", new Date());
                            }
                            Thread.sleep(500L);
                        } catch (Exception e) {
                            materialUploadTaskMapper.updateStatusById(task.getId(), 0, "接口异常-待重试", new Date());
                            e.printStackTrace();
                        }
                    }
                });
    }

    @Override
    public void uploadBytedance(FileBody fileBody, MaterialUploadTask task) {
        bytedanceExecutorService.submit(
                new Runnable() {
                    @Override
                    public void run() {
                        Long accountId = task.getAccountId();
                        if (Check.isNull(accountId)) {
                            log.error("账户不能为空");
                            return;
                        }
                        String token = materialUploadTaskMapper.getTokenByAccountId(accountId);
                        if (Check.isNull(token)) {
                            log.error("此账户id未获取到token,accountId:{}", accountId);
                            return;
                        }
                        long l = System.currentTimeMillis();
                        String url = HttpConstant.BYTEDANCE_VIDEO_UPLOAD;
                        CloseableHttpResponse response = null;
                        CloseableHttpClient client = null;
                        // 构造请求
                        HttpPost httpPost = new HttpPost(url);
                        httpPost.setHeader("Access-Token", token);
                        // 文件参数
                        try {
                            MultipartEntityBuilder entityBuilder =
                                    MultipartEntityBuilder.create().addPart("video_file", fileBody);
                            // 其他参数
                            entityBuilder.addTextBody("advertiser_id", String.valueOf(task.getAccountId()));
                            entityBuilder.addTextBody("video_signature", task.getSignature());
                            entityBuilder.addTextBody(
                                    "filename",
                                    task.getMaterialName(),
                                    ContentType.create("text/plain", Charset.forName("UTF-8")));
                            org.apache.http.HttpEntity entity = entityBuilder.build();
                            client = HttpClientBuilder.create().build();
                            httpPost.setURI(URI.create(url));
                            httpPost.setEntity(entity);
                            response = client.execute(httpPost);
                            if (response != null && response.getStatusLine().getStatusCode() == 200) {
                                BufferedReader bufferedReader =
                                        new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
                                StringBuilder result = new StringBuilder();
                                String line = "";
                                while ((line = bufferedReader.readLine()) != null) {
                                    result.append(line);
                                }
                                bufferedReader.close();
                                JSONObject resultJson = JSONObject.parseObject(result.toString());
                                if (!Check.isNull(resultJson)) {
                                    if (resultJson.getInteger("code") == 0) {
                                        materialUploadTaskMapper.updateStatusById(task.getId(), 3, "同步成功", new Date());
                                        materialUploadTaskMapper.syncBytedance(task.getSignature());
                                        JSONObject data = resultJson.getJSONObject("data");
                                        if (!Check.isNull(data)) {
                                            Long projectId = materialUploadTaskMapper.getProjectIdByAccoubt(task.getAccountId());
                                            materialUploadTaskMapper.addLong(task.getSignature(), projectId, data.getString("video_id"), accountId);
                                        }
                                        String syncAccounts = task.getSyncAccounts();
                                        if (!Check.isNull(syncAccounts)) {
                                            JSONArray accounts = JSONArray.parseArray(syncAccounts);
                                            if (accounts.size() > 1) {
                                                accounts.remove(0);
                                                if (!Check.isNull(data)) {
                                                    syncBytedance(accountId, task.getMediaId(), task.getSignature(), accounts, task.getUserId(), task.getUserName(), task.getMaterialName(), task.getMaterialUrl(), data.getString("video_id"));
                                                }
                                            }
                                        }
                                        log.info(
                                                "头条素材同步完成，时间:{} s ,accountId：{}，code：{},返回信息：{}",
                                                (System.currentTimeMillis() - l) / 1000,
                                                accountId,
                                                task.getSignature(),
                                                resultJson);
                                    } else {
                                        String message = resultJson.getString("message");
                                        materialUploadTaskMapper.updateStatusById(task.getId(), 4, message, new Date());
                                        log.error("头条同步步素材失败，返回信息：{}", resultJson);
                                    }
                                }
                            } else {
                                materialUploadTaskMapper.updateStatusById(
                                        task.getId(), 0, "返回结果为空，待重试", new Date());
                            }
                        } catch (Exception e) {
                            log.error(e.getMessage(), e);
                            materialUploadTaskMapper.updateStatusById(task.getId(), 0, "接口异常，待重试", new Date());
                        } finally {
                            try {
                                if (response != null) {
                                    response.close();
                                }
                                if (null != client) {
                                    client.close();
                                }
                            } catch (IOException e) {
                                log.error(e.getMessage(), e);
                            }
                        }
                    }
                });
    }


    private void syncBytedance(Long accountId, Integer mediaId, String signature, JSONArray accountArray, String loginId, String userName, String materialName, String materialUrl, String videoId) {
        bindExecutorService.submit(
                new Runnable() {
                    @SneakyThrows
                    @Override
                    public void run() {
                        log.info("开始素材推送,code:{},账户数:{}", signature, accountArray.size());
                        Thread.sleep(3 * 60 * 1000L);
                        String token = materialUploadTaskMapper.getTokenByAccountId(accountId);
                        if (Check.isNull(token)) {
                            return;
                        }
                        List<MaterialUploadTask> tasksList = new ArrayList<>();
                        List<List<Object>> accountIds = Lists.partition(accountArray, 49);
                        for (int i = 0; i < accountIds.size(); i++) {
                            List<Object> objects = accountIds.get(i);
                            JSONObject params = new JSONObject();
                            params.put("advertiser_id", accountId);
                            params.put("target_advertiser_ids", JSONArray.toJSONString(objects));
                            JSONArray video_ids = new JSONArray();
                            video_ids.add(videoId);
                            params.put("video_ids", video_ids);
                            JSONObject jsonObject = HttpUtils.bytedancePostRequest(token, "https://ad.oceanengine.com/open_api/2/file/material/bind/", params);
                            Map<Long, String> dataMap = new HashMap<>();
                            if (!Check.isNull(jsonObject)) {
                                Integer code = jsonObject.getInteger("code");
                                if (code == 0) {
                                    log.info("素材推送成功,code:{},账户数:{}", signature, accountArray.size());
                                    JSONObject data = jsonObject.getJSONObject("data");
                                    if (!Check.isNull(data)) {
                                        JSONArray fail_list = data.getJSONArray("fail_list");
                                        if (!Check.isNull(fail_list)) {
                                            for (int j = 0; j < fail_list.size(); j++) {
                                                JSONObject failJson = fail_list.getJSONObject(j);
                                                dataMap.put(failJson.getLong("target_advertiser_id"), failJson.getString("fail_reason"));
                                            }
                                        }
                                    }
                                }
                                for (int j = 0; j < objects.size(); j++) {
                                    Long target_advertiser_id = (Long) objects.get(j);
                                    MaterialUploadTask task = new MaterialUploadTask();
                                    task.setMediaId(mediaId);
                                    task.setSignature(signature);
                                    task.setUserId(loginId);
                                    task.setUserName(userName);
                                    task.setMaterialName(materialName);
                                    task.setMaterialUrl(materialUrl);
                                    if (code != 0) {
                                        if (code == 40000 && jsonObject.getString("message").contains("与原广告主不属于同一主体")) {
                                            task.setTaskStatus(0);
                                            task.setRemarks("待重新提交-目标广告主与原广告主不属于同一主体");
                                        } else {
                                            task.setTaskStatus(4);
                                            task.setRemarks(jsonObject.getString("message"));
                                        }
                                    } else {
                                        if (!Check.isNull(dataMap)) {
                                            String fail_reason = dataMap.get(target_advertiser_id);
                                            if (Check.isNull(fail_reason)) {
                                                task.setTaskStatus(3);
                                                task.setRemarks("素材同步完成");
                                            } else {
                                                task.setTaskStatus(4);
                                                task.setRemarks("重复推送-此素材已存在广告主账户中");
                                            }
                                        }
                                    }
                                    task.setAccountId(target_advertiser_id);
                                    tasksList.add(task);
                                }
                            }
                        }
                        materialUploadTaskMapper.replaceBatch(tasksList);
                    }
                });
    }

    private TaskVo getTaskList(String signature) {
        TaskVo taskVo = new TaskVo();
        QueryWrapper<MaterialUploadTask> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("signature", signature);
        queryWrapper.eq("task_status", 1);
        List<MaterialUploadTask> materialUploadTasks =
                materialUploadTaskMapper.selectList(queryWrapper);
        if (Check.isNull(materialUploadTasks)) {
            return null;
        }
        taskVo.setTaskList(materialUploadTasks);
        List<Integer> mediaList = materialUploadTaskMapper.getMediaList(signature);
        for (int i = 0; i < mediaList.size(); i++) {
            Integer mediaId = mediaList.get(i);
            if (mediaId == 2 || mediaId == 4) {
                taskVo.setKuaiShou(1);
            }
            if (mediaId == 1 || mediaId == 3) {
                taskVo.setBytedance(1);
            }
        }
        return taskVo;
    }

    private String exceptInfoForRestTemplate(
            String url, Map<String, Object> paramMap, Map<String, String> headerMap) {
        try {
            long l = System.currentTimeMillis();
            MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
            if (!Check.isNullMap(paramMap)) {
                for (String key : paramMap.keySet()) {
                    param.add(key, paramMap.get(key));
                }
                HttpHeaders headers = new HttpHeaders();
                if (!Check.isNullMap(headerMap)) {
                    for (String key : headerMap.keySet()) {
                        headers.add(key, headerMap.get(key));
                    }
                }
                SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
                requestFactory.setConnectTimeout(1000 * 500); // 设置超时
                requestFactory.setReadTimeout(1000 * 500);
                RestTemplate rest = new RestTemplate(requestFactory);
                HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(param, headers);
                ResponseEntity<String> responseEntity =
                        rest.exchange(url, HttpMethod.POST, httpEntity, String.class);
                log.info(
                        "快手上传素材时间:{} s ,账户：{}。code:{}",
                        (System.currentTimeMillis() - l) / 1000,
                        paramMap.get("advertiser_id"),
                        paramMap.get("signature"));
                return responseEntity.getBody();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
