package com.bcxin.backend.domain.syncs.services.impls;

import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import com.bcxin.backend.core.AppConfigProperty;
import com.bcxin.backend.core.components.StorageProvider;
import com.bcxin.backend.core.exceptions.SaasNoSupportException;
import com.bcxin.backend.core.utils.ExceptionUtils;
import com.bcxin.backend.domain.SimpleLocalLockComponent;
import com.bcxin.backend.domain.enums.SyncType;
import com.bcxin.backend.domain.repositories.DomainSuperviseRepository;
import com.bcxin.backend.domain.syncs.components.HttpRequestProvider;
import com.bcxin.backend.domain.syncs.dtos.DataSyncParameter;
import com.bcxin.backend.domain.syncs.dtos.SyncDataMeta;
import com.bcxin.backend.domain.syncs.repositories.DataSyncQueueRepository;
import com.bcxin.backend.domain.syncs.services.CleanQueuesCacheService;
import com.bcxin.backend.domain.syncs.services.DataSyncQueueService;
import com.bcxin.runtime.domain.syncs.dtos.FormSyncConfigDto;
import com.bcxin.runtime.domain.syncs.entities.DataSyncQueueEntity;
import com.bcxin.runtime.domain.syncs.enums.SyncProcessStatus;
import com.bcxin.saas.core.components.JsonProvider;
import org.apache.http.client.utils.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

public abstract class DataSyncQueueServiceBase implements DataSyncQueueService {
    protected static Logger logger = LoggerFactory.getLogger(DataSyncQueueServiceImpl.class);
    protected final DataSyncQueueRepository dataSyncQueueRepository;
    protected final JsonProvider jsonProvider;
    protected final HttpRequestProvider httpRequestProvider;
    protected final AppConfigProperty appConfigProperty;
    protected final StorageProvider storageProvider;
    protected final DomainSuperviseRepository domainSuperviseRepository;
    private final SimpleLocalLockComponent simpleLocalLockComponent;

    public DataSyncQueueServiceBase(DataSyncQueueRepository dataSyncQueueRepository,
                                    JsonProvider jsonProvider,
                                    HttpRequestProvider httpRequestProvider,
                                    AppConfigProperty appConfigProperty,
                                    StorageProvider storageProvider,
                                    DomainSuperviseRepository domainSuperviseRepository,
                                    SimpleLocalLockComponent simpleLocalLockComponent) {
        this.dataSyncQueueRepository = dataSyncQueueRepository;
        this.jsonProvider = jsonProvider;
        this.httpRequestProvider = httpRequestProvider;
        this.appConfigProperty = appConfigProperty;
        this.storageProvider = storageProvider;
        this.domainSuperviseRepository = domainSuperviseRepository;
        this.simpleLocalLockComponent = simpleLocalLockComponent;
    }

    protected void dispatch(SyncType syncFile) {
        StringBuilder tracking = new StringBuilder(String.format(" 开始执行待处理queues数据: %s;syncFile=%s;", this.getClass().getName(), syncFile));
        try {
            if (simpleLocalLockComponent.enter(this.getClass())) {
                try {

                    Page<DataSyncQueueEntity> dataSyncQueueEntityPage = null;
                    int pageIndex = 0;
                    boolean hasRecords = false;
                    do {
                        try {
                            Pageable pageable = Pageable.ofSize(15);
                            tracking.append(String.format("开始获取(页码=%s)数据;", pageIndex));
                            pageIndex++;

                            switch (syncFile) {
                                case DirectlyData:
                                    dataSyncQueueEntityPage =
                                            this.dataSyncQueueRepository.getPendingData(pageable);
                                    break;
                                case DirectlyFile:
                                    dataSyncQueueEntityPage =
                                            this.dataSyncQueueRepository.getPendingFiles(pageable);
                                    break;
                                case DataViaFtp:
                                    dataSyncQueueEntityPage =
                                            this.dataSyncQueueRepository.getPendingFtpData(pageable);
                                    break;
                                case FileViaFtp:
                                    dataSyncQueueEntityPage =
                                            this.dataSyncQueueRepository.getPendingFtpFilesData(pageable);
                                    break;
                                default:
                                    throw new SaasNoSupportException();
                            }
                            tracking.append(String.format("总共获得queues表(%s条)数据;",
                                    (dataSyncQueueEntityPage == null ? "0" : dataSyncQueueEntityPage.getTotalElements())
                            ));

                            if (dataSyncQueueEntityPage != null &&
                                    dataSyncQueueEntityPage.getContent().size() > 0) {
                                hasRecords = true;
                                tracking.append(
                                        String.format("开始执行数据同步项=%s;",
                                                dataSyncQueueEntityPage.getContent().stream().map(ix -> ix.getName())
                                                        .collect(Collectors.joining(";"))
                                        )
                                );

                                Collection<DataSyncQueueEntity> dataSyncQueueEntities = null;
                                try {
                                    dataSyncQueueEntities = dataSyncQueueEntityPage.get().collect(Collectors.toList());

                                    logger.info("待处理的队列列表: {}({}) !",
                                            dataSyncQueueEntities.stream()
                                                    .map(ix -> String.format("%s=%s", ix.getName(), ix.getId()))
                                                    .collect(Collectors.joining(";")));

                                    dataSyncQueueEntities.parallelStream().forEach(data -> {

                                        StringBuilder executeTracking = new
                                                StringBuilder(String.format("[%s]: ", data.getClass().getName()));

                                        executeTracking.append(String.format("正准备执行如下:%s=%s;",
                                                data.getId(), data.getName()));
                                        try {
                                            boolean needSyncFile = (syncFile == SyncType.FileViaFtp || syncFile == SyncType.DirectlyFile);
                                            this.executeSyncAction(needSyncFile, data);
                                            executeTracking.append("数据队列执行完毕!");
                                        } catch (Exception ex) {
                                            executeTracking.append(String.format("数据异常:%s", ex));
                                            logger.error("同步queues表中数据异常:{}", executeTracking);
                                        } finally {
                                            logger.error(data.getId()+"=="+data.getThirdPartyReferenceId());
                                            this.dataSyncQueueRepository.save(data);
                                        }
                                    });
                                } catch (Exception ex) {
                                    ex.printStackTrace();
                                    logger.error("DataSyncQueueServiceBase.dispatch()方法执行待处理queues数据过程，发生异常; 详细: {}", ex);
                                } finally {
                                    /*if (dataSyncQueueEntities != null) {
                                        this.dataSyncQueueRepository.saveAll(dataSyncQueueEntities);
                                    }*/
                                }

                                tracking.append("完成同步操作;");
                            } else {
                                hasRecords = false;
                            }
                        } catch (Exception ex) {
                            tracking.append(String.format("数据异常:%s;", ExceptionUtils.getStackMessage(ex)));
                        } finally {
                            logger.error(tracking.toString());
                            tracking = new StringBuilder();
                        }
                    } while (hasRecords);
                } finally {
                    simpleLocalLockComponent.exit(this.getClass());
                }
            } else {
                //每两分钟输出一条日志
                Calendar calendar = Calendar.getInstance();
                int minute = calendar.get(Calendar.MINUTE);
                int second = calendar.get(Calendar.SECOND);
                if (minute % 2 == 0 && (second % 20 == 0)) {
                    logger.error(String.format("%s:任务(%s)正在执行", calendar.getTime(), this.getClass().getName()));
                }
            }
        } catch (Exception ex) {
            tracking.append(String.format("异常=%s;", ExceptionUtils.getStackMessage(ex)));
        } finally {
            logger.error("数据同步的操作:{}", tracking);
        }
    }

    @Override
    public void dispatch(DataSyncQueueEntity dataSyncQueueEntity) {
        this.executeSyncAction(true, dataSyncQueueEntity);
        this.executeSyncAction(false, dataSyncQueueEntity);
    }

    protected String getFilePathFromJson(JSONObject jsonObject) {
        return jsonObject.getString("path");
    }

    protected String getSyncRealData(DataSyncQueueEntity syncQueueEntity) {
        SyncDataMeta dataMeta = this.jsonProvider.getData(syncQueueEntity.getData(), SyncDataMeta.class);
        byte[] contentBytes = this.storageProvider.read(dataMeta.getPath());

        String content = new String(contentBytes);

        return content;
    }

    protected HttpResponse executePostRequest(String url, Map<String, Object> headers, String data) {
        return this.httpRequestProvider.post(url, headers, data);
    }

    protected boolean isMatchExecuteCondition(boolean isDispatchFile,
                                              DataSyncQueueEntity data) {
        if (data.getSize() == 0) {
            return false;
        }
        if (!isDispatchFile) {
            if ((data.getStatus() != SyncProcessStatus.Initialize && data.getStatus() != SyncProcessStatus.SyncViaFTP)) {
                return false;
            }
        } else {
            if ((data.getFileSyncStatus() != SyncProcessStatus.Initialize && data.getFileSyncStatus() != SyncProcessStatus.SyncViaFTP)) {
                return false;
            }
        }

        return true;
    }

    protected void printBusinessMsg(String msg) {
        Calendar calendar = Calendar.getInstance();
        if (calendar.get(Calendar.SECOND) % 30 == 0) {
            logger.error(msg);
        }
    }

    private void executeSyncAction(
            boolean isDispatchFile,
            DataSyncQueueEntity data) {
        if (!this.isMatchExecuteCondition(isDispatchFile, data)) {
            logger.error("[{}}] 未满足执行条件!", data.getName());
            return;
        }

        FormSyncConfigDto formSyncConfigDto =
                this.jsonProvider.getData(data.getFormSyncConfig(), FormSyncConfigDto.class);

        StringBuilder sb = new StringBuilder();

        try {
            DataSyncParameter dataSyncParameter =
                    DataSyncParameter.create(data, formSyncConfigDto.getMapKey(), formSyncConfigDto.getTargetConfig(),formSyncConfigDto.getOtherTargetApp());
            sb.append(String.format("完成dataSyncParameter:%s;", formSyncConfigDto.getMapKey()));

            if (isDispatchFile) {
                this.executeSyncFileCore(dataSyncParameter);
            } else {
                this.executeSyncDataCore(dataSyncParameter);
            }
            logger.info("[{}}] 执行完毕!", data.getName());
        } catch (Exception ex) {
            sb.append(String.format("queues表数据同步异常:id=%s; 异常=%s",
                    data.getId(),
                    ExceptionUtils.getStackMessage(ex)));
            if (isDispatchFile) {
                data.changeFileSyncStatus(SyncProcessStatus.Error, sb.toString());
            } else {
                data.changeDataSyncStatus(SyncProcessStatus.Error, sb.toString());
            }
        }
    }

    protected abstract void executeSyncDataCore(DataSyncParameter dataSyncParameter);

    protected abstract void executeSyncFileCore(DataSyncParameter dataSyncParameter);

    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");

    protected SyncDataMeta saveRequestData(Object requestData) {
        String path = String.format("/uploads/sync/response/ftp/%s-%s.txt", simpleDateFormat.format(new Date()),
                UUID.randomUUID());
        String data = this.jsonProvider.getJson(requestData);
        byte[] content = data.getBytes(StandardCharsets.UTF_8);
        String realPath = this.storageProvider.save(path, content);

        return SyncDataMeta.create(path, content.length, realPath, Collections.EMPTY_LIST);
    }

    public static void main(String[] args) {
        String aa = DateUtils.formatDate(new Date(), "yyyy-MM-dd");
        System.out.println(aa);
    }
}
