package com.uas.search.console.b2b.support;

import com.alibaba.fastjson.JSONObject;
import com.uas.search.b2b.model.PageParams;
import com.uas.search.b2b.service.SearchService;
import com.uas.search.console.b2b.model.PageInfo;
import com.uas.search.console.b2b.util.SearchUtils;
import com.uas.search.util.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;

import java.io.File;
import java.io.PrintWriter;
import java.util.Date;
import java.util.List;

import static com.uas.search.console.b2b.service.impl.IndexServiceImpl.PAGE_SIZE;
import static com.uas.search.console.b2b.service.impl.IndexServiceImpl.SINGLE_FILE_MAX_SIZE;

/**
 * 下载线程
 *
 * @author sunyj
 * @since 2017/11/25 18:24
 */
public class DownloadService<T> {

    /**
     * @param id             线程 id
     * @param step           新增文件时，文件 id 的自增步长，（即线程数量）
     * @param startFileIndex 开始的文件
     * @param endFileIndex   结束的文件
     * @param tableName      要下载的表
     * @param sortField      排序字段
     * @param dao            dao
     * @return 下载的数量
     */
    public long download(int id, int step, int startFileIndex, int endFileIndex, SearchService.Table_name tableName, String sortField, JpaRepository<T, Long> dao) {
        long size = 0L;
        Logger logger = LoggerFactory.getLogger(getClass());
        String name = "Thread_" + id + "_" + startFileIndex + "-" + endFileIndex;
        File file = null;
        PrintWriter printWriter = null;
        try {
            if (SINGLE_FILE_MAX_SIZE % PAGE_SIZE != 0) {
                throw new IllegalArgumentException("SINGLE_FILE_MAX_SIZE 并非 PAGE_SIZE 的整数倍：" + SINGLE_FILE_MAX_SIZE + "/" + PAGE_SIZE);
            }
            if (startFileIndex > endFileIndex) {
                logger.info(name + " startFileIndex 超过 " + endFileIndex);
                return size;
            }
            Long startTime = new Date().getTime();
            logger.info(name + " 下载 " + tableName.value() + "...");

            Sort sort = new Sort(Sort.Direction.ASC, sortField);
            // 分页获取数据
            PageParams pageParams = new PageParams();
            pageParams.setPage((startFileIndex - 1) * SINGLE_FILE_MAX_SIZE / PAGE_SIZE + 1);
            pageParams.setSize(PAGE_SIZE);
            PageInfo pageInfo = new PageInfo(pageParams, sort);
            Page<T> pageResult = dao.findAll(pageInfo);

            int totalFiles = (int) Math.ceil(pageResult.getTotalElements() / (1.0 * SINGLE_FILE_MAX_SIZE));
            if (startFileIndex > totalFiles) {
                logger.info(name + " startFileIndex 超过 " + totalFiles);
                return size;
            }
            // 实际的最后一个文件
            endFileIndex = endFileIndex < totalFiles ? endFileIndex : totalFiles;

            File dataDir = new File(SearchUtils.getDataPath(tableName));
            if (!dataDir.exists()) {
                if (!dataDir.mkdirs()) {
                    logger.error("创建文件夹失败：" + dataDir.getPath());
                }
            }
            int fileIndex = startFileIndex;
            String fileName = String.format("%010d", fileIndex) + ".txt";
            file = new File(dataDir, fileName);
            printWriter = new PrintWriter(file);
            int count = 0;
            while (endFileIndex >= fileIndex) {
                // 数据以 JSON 格式写入文件
                List<T> content = pageResult.getContent();
                for (T element : content) {
                    println(printWriter, element);
                }
                count += content.size();
                size += content.size();
                logger.info(name + " " + fileName + " - Downloaded..................." + size);

                // 一个文件的函数达到指定值，写入新文件
                if (count == SINGLE_FILE_MAX_SIZE) {
                    count = 0;
                    close(printWriter);
                    fileIndex += step;
                    pageParams.setPage(pageParams.getPage() + step * SINGLE_FILE_MAX_SIZE / PAGE_SIZE);
                    // 不创建多余文件
                    if (endFileIndex >= fileIndex) {
                        fileName = String.format("%010d", fileIndex) + ".txt";
                        file = new File(dataDir, fileName);
                        printWriter = new PrintWriter(file);
                    }
                } else {
                    pageParams.setPage(pageParams.getPage() + 1);
                }
                pageInfo = new PageInfo(pageParams, sort);
                pageResult = dao.findAll(pageInfo);
                // 已经下载到最后一页
                if (CollectionUtils.isEmpty(pageResult.getContent())) {
                    break;
                }
            }
            logger.info(String.format("%s 下载完成，耗时%.2fs\n ", name, (new Date().getTime() - startTime) / 1000.0));
        } catch (Throwable e) {
            close(printWriter);
            // 出现错误时，删除最近出错的文件
            if (file != null) {
                if (!file.delete()) {
                    logger.error("出错时，清理出错文件失败：" + file.getPath());
                }
            }
            logger.error(name + " " + tableName + " 下载失败", e);
        } finally {
            close(printWriter);
        }
        return size;
    }

    protected void println(PrintWriter printWriter, T element) {
        printWriter.println(JSONObject.toJSONString(element));
    }

    private void close(PrintWriter printWriter) {
        if (printWriter != null) {
            printWriter.flush();
            printWriter.close();
        }
    }

}