package com.jsc.oscm.service;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson.JSON;
import com.jsc.oscm.dao.CloudServerRepository;
import com.jsc.oscm.dao.ProjectRepository;
import com.jsc.oscm.entity.CloudServer;
import com.jsc.oscm.entity.Project;
import com.jsc.oscm.model.ScrapydListProject;
import com.jsc.oscm.model.ScrapydListSpiders;
import com.jsc.oscm.util.HttpUtil;

import okhttp3.Call;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

@Service
public class ProjectService {
    static final Logger logger = LogManager.getLogger(ProjectService.class.getName());

    @Value("${crawl.project.file.path}")
    String savePath;

    @Autowired
    CloudServerRepository cloudNodeRepository;

    @Autowired
    CloudServerService nodeService;

    @Autowired
    ProjectRepository projectRepository;

    public List<Project> findAllProject() {
        return projectRepository.findAll();
    }


    public List<Project> findProjectByName(String projectName) {
        Project exampleProject = new Project();
        exampleProject.setProjectName(projectName);

        Example<Project> example = Example.of(exampleProject);

        return projectRepository.findAll(example);
    }

    public List<Project> findAllPageable(int index, int size) {
        List<Project> projects;
        Pageable page = PageRequest.of(index, size, Sort.by(Order.desc("createDate")));
        Page<Project> projectPage = projectRepository.findAll(page);
        projects = projectPage.getContent();
        return projects;
    }

    public long findAllCount() {
        return projectRepository.count();
    }

    public String saveFile(MultipartFile file) {
//		String pathString = ProjectService.class.getResource("/").toString().substring(6);
        File toFile = new File(savePath + file.getOriginalFilename());
        try {
            File pathDirFile = new File(savePath);
            if (!pathDirFile.exists()) {
                pathDirFile.mkdirs();
            }
            file.transferTo(toFile);
        } catch (Exception e) {
            // TODO: handle exception
            logger.error(e.getMessage());
        }
        return toFile.getName();
    }

    public boolean deployEgg(String[] scrapydAddressList, String projectName, String filename) {
        List<Byte> bytesList = new ArrayList<>();
        FileInputStream fileInputStream = null;
        try {
            File file = new File(savePath + filename);
            fileInputStream = new FileInputStream(file);

            int temp;
            while ((temp = fileInputStream.read()) != -1) {
                bytesList.add((byte) temp);
            }
            byte[] bytes = new byte[bytesList.size()];
            for (int i = 0; i < bytesList.size(); i++) {
                bytes[i] = bytesList.get(i);
            }
            Map<String, Object> fileMap = new HashMap<>();
            fileMap.put("name", "egg");
            fileMap.put("filename", filename);
            fileMap.put("bytes", bytes);

            Map<String, Object> paramsMap = new HashMap<>();
            paramsMap.put("project", projectName);
            paramsMap.put("version", "0.0.1");
            for (String scrapydAddress : scrapydAddressList) {
                HttpUtil.multiPartRequest(String.format("http://%s/addversion.json", scrapydAddress), paramsMap, fileMap);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public boolean deployZip(String[] scrapydAddressList, String projectName, String filename) {
        logger.info("开始发布");
//		String pathString = ProjectService.class.getResource("/").toString().substring(6);
        //将上传的压缩文件进行解压
        String unZipDirName = unzip(savePath + filename, savePath);
        if (unZipDirName.length() == 0) {
            return false;
        }
        //文件解压缩目录
        String unzipPath = savePath + unZipDirName;
        //获取scrapy.cfg文件，并添加发布相关参数
        File cfgFile = new File(unzipPath + "/scrapy.cfg");
        OutputStreamWriter writer = null;
        try {
            writer = new OutputStreamWriter(new FileOutputStream(cfgFile, true), StandardCharsets.UTF_8);
            for (int i = 0; i < scrapydAddressList.length; i++) {
                writer.write(String.format("[deploy:target%d]%n", i));
                writer.write(String.format("url = http://%s/%n", scrapydAddressList[i]));
                writer.write(String.format("project = %s%n", projectName));
            }
            writer.flush();
        } catch (Exception e) {
            logger.error(e.getMessage());
            return false;
        } finally {
            try {
                writer.close();
            } catch (IOException e) {
                logger.error(e.getMessage());
            }
        }
        //利用scrapyd-client发布项目,需要先判断操作系统
        try {
            ProcessBuilder builder = new ProcessBuilder();
            if (System.getProperty("os.name").toLowerCase().contains("linux")) {
                builder.command("sh", "-c", "scrapyd-deploy", "-a", "-p", projectName);
            } else if (System.getProperty("os.name").toLowerCase().contains("windows")) {
                builder.command("cmd.exe", "/c", "scrapyd-deploy", "-a", "-p", projectName);
            } else {
                return false;
            }
            builder.directory(new File(unzipPath));
            Process process = builder.start();
            InputStreamReader iReader = new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8);
            char[] temp = new char[1024];
            while (iReader.read(temp) > 0) {
                logger.info(new String(temp));
            }
            iReader.close();
            int exitValue = process.exitValue();
        } catch (Exception e) {
            logger.error(e.getMessage());
            return false;
        }
        //查询是否发布成功
//		List<CrawlProject> projects = new ArrayList<CrawlProject>();
//		for (int i = 0; i < scrapydAddressList.length; i++) {
//			List<CrawlProject> temp = queryProjectByNodeServer(scrapydAddressList[i]);
//			CloudNode node = nodeService.queryNodeByServer(scrapydAddressList[i]);
//			for (CrawlProject crawlProject : temp) {
//				if (crawlProject.getProjectName().equals(projectName)) {
//					crawlProject.setHostName(node.getHostName());
//					crawlProject.setNodeName(node.getNodeName());
//					crawlProject.setRemark(remark);
//					projects.add(crawlProject);
//				}	
//			}
//		}
//		if (projects.size() == 0) {
//			return false;
//		}
//		//增加project记录
//		for (CrawlProject crawlProject : projects) {
//			crawlProject.setCreateDate(new Date());
//			crawlProjectRepository.save(crawlProject);	
//		}
        return true;
    }

    public List<Project> saveProject(List<Project> projects) {
        projects.forEach(e -> {
            if (e.getId() == null) {
                Example<Project> example = Example.of(e);
                Optional<Project> optional = projectRepository.findOne(example);
                optional.ifPresent(project -> e.setId(project.getId()));
            }
            e.setCreateDate(new Date());
        });
        return projectRepository.saveAll(projects);
    }

    public List<Project> queryProject() {
        CloudServer exampleNode = new CloudServer();
        exampleNode.setStatus("1");
        Example<CloudServer> example = Example.of(exampleNode);
        List<CloudServer> cloudNodes = cloudNodeRepository.findAll(example);
        List<Project> scrapyProjects = new ArrayList<>();
        OkHttpClient client = new OkHttpClient();
        for (CloudServer cloudNode : cloudNodes) {
            String requestUrlString = "http://" + cloudNode.getIpPort() + "/listprojects.json";
            Request request = new Request.Builder().url(requestUrlString).method("GET", null).build();
            Call call = client.newCall(request);
            try {
                Response response = call.execute();
                String data = response.body().string();
                ScrapydListProject scrapydListProject = JSON.parseObject(data, ScrapydListProject.class);
                if (!scrapydListProject.getStatus().equals("ok")) {
                    continue;
                }
                for (String projectName : scrapydListProject.getProjects()) {
                    Project project = new Project();
                    project.setHostName(cloudNode.getHostName());
                    project.setIpPort(cloudNode.getIpPort());
                    project.setNodeName(cloudNode.getNodeName());
                    project.setProjectName(projectName);
                    scrapyProjects.add(project);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return scrapyProjects;
    }

    public List<Project> queryProjectByNodeServer(String server) {
        List<Project> projects = new ArrayList<>();
        OkHttpClient client = new OkHttpClient();
        String requestUrlString = "http://" + server + "/listprojects.json";
        Request request = new Request.Builder().url(requestUrlString).method("GET", null).build();
        Call call = client.newCall(request);
        try {
            Response response = call.execute();
            String data = response.body().string();
            ScrapydListProject scrapydListProject = JSON.parseObject(data, ScrapydListProject.class);
            if (!scrapydListProject.getStatus().equals("ok")) {
                return projects;
            }
            for (String projectName : scrapydListProject.getProjects()) {
                Project project = new Project();
                project.setProjectName(projectName);
                project.setIpPort(server);
                projects.add(project);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return projects;
    }

    public List<String> querySpidersOfProject(String server, String project) {
        List<String> spiders = new ArrayList<>();
        OkHttpClient client = new OkHttpClient();
        String requestUrlString = "http://" + server + "/listspiders.json?project=" + project;
        Request request = new Request.Builder().url(requestUrlString).method("GET", null).build();
        Call call = client.newCall(request);
        try {
            Response response = call.execute();
            String data = response.body().string();
            ScrapydListSpiders scrapydListSpiders = JSON.parseObject(data, ScrapydListSpiders.class);
            if (!scrapydListSpiders.getStatus().equals("ok")) {
                return spiders;
            }
            spiders.addAll(scrapydListSpiders.getSpiders());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return spiders;
    }

    public boolean delProjects(List<Project> projects) {
        OkHttpClient client = new OkHttpClient();
        for (Project scrapyProject : projects) {
            String requestUrlString = "http://" + scrapyProject.getIpPort() + "/delproject.json";
            FormBody requestBody = new FormBody.Builder()
                    .add("project", scrapyProject.getProjectName())
                    .build();
            Request request = new Request.Builder().url(requestUrlString).post(requestBody).build();
            Call call = client.newCall(request);
            try {
                Response response = call.execute();
                String data = response.body().string();
                ScrapydListProject scrapydListProject = JSON.parseObject(data, ScrapydListProject.class);
                if (scrapydListProject.getStatus().equals("ok")) {
                    Example<Project> example = Example.of(scrapyProject);
                    Optional<Project> project = projectRepository.findOne(example);
                    if (project.isPresent()) {
                        projectRepository.delete(project.get());
                    }
                }
            } catch (Exception e) {
                logger.error(e.getMessage());
            }
        }
        return true;
    }


    static final int BUFFER = 2048;

    private String unzip(String filePath, String unzipPath) {
        String unZipDirName = "";
        try {
            ZipFile zipFile = new ZipFile(filePath);
            @SuppressWarnings("rawtypes")
            Enumeration enumeration = zipFile.entries();
            while (enumeration.hasMoreElements()) {
                ZipEntry entry = (ZipEntry) enumeration.nextElement();
                if (entry.isDirectory()) {
                    if ("".equals(unZipDirName)) {
                        unZipDirName = entry.getName().split("/")[0];
                    }
                    new File(unzipPath + entry.getName()).mkdirs();
                    continue;
                }
                BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(entry));
                File file = new File(unzipPath + entry.getName());
                //加入这个的原因是zipfile读取文件是随机读取的，这就造成可能先读取一个文件
                //而这个文件所在的目录还没有出现过，所以要建出目录来。
                File parent = file.getParentFile();
                if (parent != null && (!parent.exists())) {
                    parent.mkdirs();
                }
                FileOutputStream fos = new FileOutputStream(file);
                BufferedOutputStream bos = new BufferedOutputStream(fos, BUFFER);
                int count;
                byte[] data = new byte[BUFFER];
                while ((count = bis.read(data, 0, BUFFER)) != -1) {
                    bos.write(data, 0, count);
                }
                bos.flush();
                bos.close();
                bis.close();
            }
            zipFile.close();
        } catch (Exception e) {
            logger.error(e.getMessage());
            return "";
        }
        return unZipDirName;
    }
}
