package cn.com.qmth.stmms.biz.file.service.impl;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.lang.math.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import cn.com.qmth.stmms.biz.config.service.impl.SystemCache;
import cn.com.qmth.stmms.biz.file.enums.FileType;
import cn.com.qmth.stmms.biz.file.enums.FormatType;
import cn.com.qmth.stmms.biz.file.service.FileService;
import cn.com.qmth.stmms.biz.file.store.FileStore;
import cn.com.qmth.stmms.biz.file.store.impl.DiskStore;
import cn.com.qmth.stmms.biz.file.store.impl.OssStore;

@Service("fileService")
public class FileServiceImpl implements FileService, InitializingBean, DisposableBean {

    private static final String COMMON_PLACEHOLDER = "common";

    private static final int DEFAULT_SUFFIX_LENGTH = 3;

    public static final String SERVER_SPLIT = ",";

    @Autowired
    private SystemCache systemCache;

    @Value("${file.store}")
    private String fileStore;

    private FileStore store;

    private String getSuffix(String input) {
        return StringUtils.trimToEmpty(input).substring(Math.max(0, input.length() - DEFAULT_SUFFIX_LENGTH));
    }

    private boolean checkFormat(FormatType input, FormatType... types) {
        for (FormatType type : types) {
            if (type.equals(input)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public String getFileServer() {
        return getFileServers().get(0);

    }

    @Override
    public String getRandomFileServer() {
        List<String> fileServers = getFileServers();
        return fileServers.get(RandomUtils.nextInt(fileServers.size()));
    }

    public List<String> getFileServers() {
        List<String> fileServers = new ArrayList<>();
        String[] servers = StringUtils.split(systemCache.getFileServer(), SERVER_SPLIT);
        for (String server : servers) {
            server = StringUtils.trimToNull(server);
            if (server != null) {
                if (!server.endsWith("/")) {
                    server = server.concat("/");
                }
                fileServers.add(server);
            }
        }
        return fileServers;
    }

    public String getFileStore() {
        return fileStore;
    }

    public void setFileStore(String fileStore) {
        this.fileStore = fileStore;
    }

    @Override
    public byte[] downloadSheet(int examId, String examNumber, int index) throws Exception {
        return store.read(getSheetUri(examId, examNumber, index));
    }

    @Override
    public byte[] downloadSlice(int examId, String secretNumber, int index) throws Exception {
        return store.read(getSliceUri(examId, secretNumber, index));
    }

    @Override
    public byte[] downloadPackage(int examId, String packageCode, int index) throws Exception {
        return store.read(getPackageUri(examId, packageCode, index));
    }

    @Override
    public void uploadSheet(InputStream ins, String md5, int examId, String examNumber, int index) throws Exception {
        store.write(getSheetUri(examId, examNumber, index), ins, md5);
    }

    @Override
    public void uploadSlice(InputStream ins, String md5, int examId, String secretNumber, int index) throws Exception {
        store.write(getSliceUri(examId, secretNumber, index), ins, md5);
    }

    @Override
    public void uploadJson(InputStream ins, String md5, int examId, String secretNumber) throws Exception {
        store.write(getJsonUri(examId, secretNumber), ins, md5);
    }

    @Override
    public void uploadPackage(InputStream ins, String md5, int examId, String packageCode, int index) throws Exception {
        store.write(getPackageUri(examId, packageCode, index), ins, md5);
    }

    @Override
    public void uploadPaper(InputStream ins, String md5, int examId, String subjectCode, FormatType type)
            throws Exception {
        if (!checkFormat(type, FormatType.PDF, FormatType.JSON)) {
            throw new RuntimeException("format type invalid");
        }
        store.write(getPaperUri(examId, subjectCode, type), ins, md5);
    }

    @Override
    public void uploadAnswer(InputStream ins, String md5, int examId, String subjectCode, FormatType type)
            throws Exception {
        if (!checkFormat(type, FormatType.PDF, FormatType.JSON)) {
            throw new RuntimeException("format type invalid");
        }
        store.write(getAnswerUri(examId, subjectCode, type), ins, md5);
    }

    @Override
    public void uploadCard(InputStream ins, String md5, int examId, FormatType type) throws Exception {
        if (!checkFormat(type, FormatType.ZIP, FormatType.JSON)) {
            throw new RuntimeException("format type invalid");
        }
        store.write(getCardUri(examId, COMMON_PLACEHOLDER, type), ins, md5);
    }

    @Override
    public void uploadCard(InputStream ins, String md5, int examId, String subjectCode, FormatType type)
            throws Exception {
        if (!checkFormat(type, FormatType.ZIP, FormatType.JSON)) {
            throw new RuntimeException("format type invalid");
        }
        store.write(getCardUri(examId, subjectCode, type), ins, md5);
    }

    @Override
    public String getSheetUri(int examId, String examNumber, int index) {
        return FileType.SHEET.getPath(examId, getSuffix(examNumber), examNumber, index, FormatType.JPG.getExtName());
    }

    @Override
    public List<String> getSheetUris(int examId, String examNumber, int start, int end) {
        List<String> list = new LinkedList<>();
        for (int i = start; i <= end; i++) {
            list.add(getSheetUri(examId, examNumber, i));
        }
        return list;
    }

    @Override
    public String getSliceUri(int examId, String secretNumber, int index) {
        return FileType.SLICE
                .getPath(examId, getSuffix(secretNumber), secretNumber, index, FormatType.JPG.getExtName());
    }

    @Override
    public List<String> getSliceUris(int examId, String secretNumber, int start, int end) {
        List<String> list = new LinkedList<>();
        for (int i = start; i <= end; i++) {
            list.add(getSliceUri(examId, secretNumber, i));
        }
        return list;
    }

    @Override
    public String getJsonUri(int examId, String secretNumber) {
        return FileType.JSON.getPath(examId, getSuffix(secretNumber), secretNumber, FormatType.JSON.getExtName());
    }

    @Override
    public String getPackageUri(int examId, String packageCode, int index) {
        return FileType.PACKAGE.getPath(examId, packageCode, index, FormatType.JPG.getExtName());
    }

    @Override
    public List<String> getPackageUris(int examId, String packageCode, int start, int end) {
        List<String> list = new LinkedList<>();
        for (int i = start; i <= end; i++) {
            list.add(getPackageUri(examId, packageCode, i));
        }
        return list;
    }

    @Override
    public String getPaperUri(int examId, String subjectCode, FormatType type) {
        return FileType.PAPER.getPath(examId, subjectCode, type.getExtName());
    }

    @Override
    public String getAnswerUri(int examId, String subjectCode, FormatType type) {
        return FileType.ANSWER.getPath(examId, subjectCode, type.getExtName());
    }

    @Override
    public String getCardUri(int examId, String subjectCode, FormatType type) {
        return FileType.CARD.getPath(examId, subjectCode, type.getExtName());
    }

    @Override
    public String getCardUri(int examId, FormatType type) {
        return FileType.CARD.getPath(examId, COMMON_PLACEHOLDER, type.getExtName());
    }

    @Override
    public boolean sheetExist(int examId, String examNumber, int index) {
        return store.exist(getSheetUri(examId, examNumber, index));
    }

    @Override
    public boolean sliceExist(int examId, String secretNumber, int index) {
        return store.exist(getSliceUri(examId, secretNumber, index));
    }

    @Override
    public boolean jsonExist(int examId, String secretNumber) {
        return store.exist(getJsonUri(examId, secretNumber));
    }

    @Override
    public void afterPropertiesSet() {
        String fileServerString = systemCache.getFileServer();
        fileStore = StringUtils.trimToNull(fileStore);
        if (fileServerString == null) {
            throw new RuntimeException("invald property: ${file.server} should not be empty");
        }
        if (fileStore == null) {
            throw new RuntimeException("invald property: ${file.store} should not be empty");
        }
        // 按逗号拆分多个文件服务器地址
        List<String> fileServers = new ArrayList<>();
        String[] servers = StringUtils.split(fileServerString, SERVER_SPLIT);
        for (String server : servers) {
            server = StringUtils.trimToNull(server);
            if (server != null) {
                if (!server.endsWith("/")) {
                    server = server.concat("/");
                }
                fileServers.add(server);
            }
        }
        if (fileServers.isEmpty()) {
            throw new RuntimeException("invald property: ${file.server} should not be empty");
        }
        // 按前缀解析文件存储引擎
        if (fileStore.startsWith("oss")) {
            store = new OssStore(fileStore);
        } else {
            store = new DiskStore(fileStore);
        }
    }

    @Override
    public void destroy() throws Exception {
        store.close();
    }

    @Override
    public byte[] downloadJson(int examId, String secretNumber) throws Exception {
        return store.read(getJsonUri(examId, secretNumber));
    }

    @Override
    public void uploadExchange(InputStream ins, String md5, String name) throws Exception {
        store.write(getExchangeUri(name), ins, md5);
    }

    @Override
    public String getExchangeUri(String name) {
        return FileType.EXCHANGE.getPath(name);
    }

    @Override
    public void uploadAnswerCard(InputStream ins, String md5, int examId, int number) throws Exception {
        store.write(getAnswerCardUri(examId, number), ins, md5);
    }

    @Override
    public String getAnswerCardUri(int examId, int number) {
        return FileType.ANSWER_CARD.getPath(examId, number, FormatType.JSON.getExtName());
    }

    @Override
    public void uploadReport(InputStream ins, String md5, int examId, String subjectCode, String name, FormatType type)
            throws Exception {
        store.write(getReportUri(examId, subjectCode, name, type), ins, md5);
    }

    @Override
    public String getReportUri(int examId, String subjectName, String reportTitle, FormatType type) {
        return FileType.REPORT.getPath(examId, subjectName, reportTitle, type.getExtName());
    }

    @Override
    public byte[] downloadReport(int examId, String subjectName, String reportTitle, FormatType type) throws Exception {
        return store.read(getReportUri(examId, subjectName, reportTitle, type));
    }

    @Override
    public boolean reportExist(int examId, String subjectName, String reportTitle, FormatType type) {
        return store.exist(getReportUri(examId, subjectName, reportTitle, type));
    }

    @Override
    public void uploadIncomplete(InputStream ins, String md5, Integer examId, String name) throws Exception {
        store.write(getIncompleteUri(examId, name), ins, md5);
    }

    @Override
    public String getIncompleteUri(Integer examId, String name) {
        return FileType.INCOMPLETE.getPath(examId, name, FormatType.JPG.getExtName());
    }

    public static void main(String[] args) throws Exception {
        FileServiceImpl service = new FileServiceImpl();
        // service.fileServerString = "123";

        service.fileStore = "oss://LTAI4FnJ2pgV6aGceYcCkeEi:ktrMEVE7PfoxRPeJUPDFeygOIH4aU7@qmth-test.oss-cn-shenzhen.aliyuncs.com";
        service.afterPropertiesSet();

        // DiskStore ds = new DiskStore("/Users/luoshi/Downloads");
        // String md5 =
        // BinaryUtil.toBase64String(BinaryUtil.calculateMd5(ds.read("123456.jpg")));
        // service.uploadSheet(new
        // FileInputStream("/Users/luoshi/Downloads/123456.jpg"),
        // "7e9b368ff5da88ff2413c2c9083c481d", 1, "123456", 1);
        service.downloadSheet(1, "16110003", 1);
        System.out.println("success");
    }

}
