package io.jpom.controller.system;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.StrSpliter;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.system.SystemUtil;
import cn.jiangzeyin.common.DefaultSystemLog;
import cn.jiangzeyin.common.JsonMessage;
import cn.jiangzeyin.common.validator.ValidatorItem;
import cn.jiangzeyin.common.validator.ValidatorRule;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.odiszapc.nginxparser.NgxBlock;
import com.github.odiszapc.nginxparser.NgxConfig;
import com.github.odiszapc.nginxparser.NgxEntry;
import com.github.odiszapc.nginxparser.NgxParam;
import io.jpom.common.BaseAgentController;
import io.jpom.common.commander.AbstractSystemCommander;
import io.jpom.service.WhitelistDirectoryService;
import io.jpom.service.system.NginxService;
import io.jpom.util.CommandUtil;
import io.jpom.util.StringUtil;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * nginx 列表
 *
 * @author jiangzeyin
 * @date 2019/4/17
 */
@RestController
@RequestMapping("/system/nginx")
public class NginxController extends BaseAgentController {

    @Resource
    private NginxService nginxService;
    @Resource
    private WhitelistDirectoryService whitelistDirectoryService;

    /**
     * 配置列表
     *
     * @return json
     */
    @RequestMapping(value = "list_data.json", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String list(String whitePath, String name) {
        if (whitelistDirectoryService.checkNgxDirectory(whitePath)) {
            if (StrUtil.isEmpty(name)) {
                name = "/";
            }
            String newName = pathSafe(name);
            JSONArray array = nginxService.list(whitePath, newName);
            return JsonMessage.getString(200, "", array);
        }
        return JsonMessage.getString(400, "文件路径错误");
    }

    /**
     * nginx列表
     */
    @RequestMapping(value = "tree.json", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String tree() {
        JSONArray array = nginxService.tree();
        return JsonMessage.getString(200, "", array);
    }

    /**
     * 获取配置文件信息页面
     *
     * @param path 白名单路径
     * @param name 名称
     * @return 页面
     */
    @RequestMapping(value = "item_data", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String itemData(String path, String name) {
        String newName = pathSafe(name);
        if (whitelistDirectoryService.checkNgxDirectory(path)) {
            File file = FileUtil.file(path, newName);
            JSONObject jsonObject = new JSONObject();
            String string = FileUtil.readUtf8String(file);
            jsonObject.put("context", string);
            String rName = StringUtil.delStartPath(file, path, true);
            // nginxService.paresName(path, file.getAbsolutePath())
            jsonObject.put("name", rName);
            jsonObject.put("whitePath", path);
            return JsonMessage.getString(200, "", jsonObject);
//            setAttribute("data", jsonObject);
        }
        return JsonMessage.getString(400, "错误");
    }

    /**
     * 新增或修改配置
     *
     * @param name      文件名
     * @param whitePath 白名单路径
     * @param genre     操作类型
     * @return json
     */
    @RequestMapping(value = "updateNgx", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String updateNgx(String name, String whitePath, String genre) {
        if (StrUtil.isEmpty(name)) {
            return JsonMessage.getString(400, "请填写文件名");
        }
        if (!name.endsWith(".conf")) {
            return JsonMessage.getString(400, "文件后缀必须为\".conf\"");
        }
        if (!checkPathSafe(name)) {
            return JsonMessage.getString(400, "文件名存在非法字符");
        }
        if (!whitelistDirectoryService.checkNgxDirectory(whitePath)) {
            return JsonMessage.getString(400, "请选择正确的白名单");
        }
        //nginx文件
        File file = FileUtil.file(whitePath, name);
        if ("add".equals(genre) && file.exists()) {
            return JsonMessage.getString(400, "该文件已存在");
        }
        String context = getUnescapeParameter("context");
        if (StrUtil.isEmpty(context)) {
            return JsonMessage.getString(400, "请填写配置信息");
        }
        InputStream inputStream = new ByteArrayInputStream(context.getBytes());
        try {
            NgxConfig conf = NgxConfig.read(inputStream);
            List<NgxEntry> list = conf.findAll(NgxBlock.class, "server");
            if (list == null || list.size() <= 0) {
                return JsonMessage.getString(404, "内容解析为空");
            }
            for (NgxEntry ngxEntry : list) {
                NgxBlock ngxBlock = (NgxBlock) ngxEntry;
                // 检查日志路径
                NgxParam accessLog = ngxBlock.findParam("access_log");
                if (accessLog != null) {
                    FileUtil.mkParentDirs(accessLog.getValue());
                }
                accessLog = ngxBlock.findParam("error_log");
                if (accessLog != null) {
                    FileUtil.mkParentDirs(accessLog.getValue());
                }
                // 检查证书文件
                NgxParam sslCertificate = ngxBlock.findParam("ssl_certificate");
                if (sslCertificate != null && !FileUtil.exist(sslCertificate.getValue())) {
                    return JsonMessage.getString(404, "证书文件ssl_certificate,不存在");
                }
                NgxParam sslCertificateKey = ngxBlock.findParam("ssl_certificate_key");
                if (sslCertificateKey != null && !FileUtil.exist(sslCertificateKey.getValue())) {
                    return JsonMessage.getString(404, "证书文件ssl_certificate_key,不存在");
                }
                if (!checkRootRole(ngxBlock)) {
                    return JsonMessage.getString(405, "非系统管理员,不能配置静态资源代理");
                }
            }
        } catch (IOException e) {
            DefaultSystemLog.getLog().error("解析失败", e);
            return JsonMessage.getString(500, "解析失败");
        }
        try {
            FileUtil.writeString(context, file, CharsetUtil.UTF_8);
        } catch (Exception e) {
            DefaultSystemLog.getLog().error(e.getMessage(), e);
            return JsonMessage.getString(400, "操作失败:" + e.getMessage());
        }
        String msg = this.reloadNginx();
        return JsonMessage.getString(200, "提交成功" + msg);
    }

    private String reloadNginx() {
        String serviceName = nginxService.getServiceName();
        try {
            String format = StrUtil.format("{} -s reload", serviceName);
            String msg = CommandUtil.execSystemCommand(format);
            if (StrUtil.isNotEmpty(msg)) {
                DefaultSystemLog.getLog().info(msg);
                return "(" + msg + ")";
            }
        } catch (Exception e) {
            DefaultSystemLog.getLog().error("reload nginx error", e);
        }
        return StrUtil.EMPTY;
    }

    /**
     * 权限检查 防止非系统管理员配置静态资源访问
     *
     * @param ngxBlock 代码片段
     * @return false 不正确
     */
    private boolean checkRootRole(NgxBlock ngxBlock) {
//        UserModel userModel = getUser();
        //        List<NgxEntry> locationAll = ngxBlock.findAll(NgxBlock.class, "location");
        //        if (locationAll != null) {
        //            for (NgxEntry ngxEntry1 : locationAll) {
        //                NgxBlock ngxBlock1 = (NgxBlock) ngxEntry1;
        //                NgxParam locationMain = ngxBlock1.findParam("root");
        //                if (locationMain == null) {
        //                    locationMain = ngxBlock1.findParam("alias");
        //                }
        //
        //            }
        //        }
        return true;
    }

    /**
     * 删除配置
     *
     * @param path 文件路径
     * @param name 文件名
     * @return json
     */
    @RequestMapping(value = "delete", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String delete(String path, String name) {
        if (!whitelistDirectoryService.checkNgxDirectory(path)) {
            return JsonMessage.getString(400, "非法操作");
        }
        String safePath = pathSafe(path);
        String safeName = pathSafe(name);
        if (StrUtil.isEmpty(safeName)) {
            return JsonMessage.getString(400, "删除失败,请正常操作");
        }
        File file = FileUtil.file(safePath, safeName);
        try {
            FileUtil.rename(file, file.getName() + "_back", false, true);
        } catch (Exception e) {
            DefaultSystemLog.getLog().error("删除nginx", e);
            return JsonMessage.getString(400, "删除失败:" + e.getMessage());
        }
        String msg = this.reloadNginx();
        return JsonMessage.getString(200, "删除成功" + msg);
    }

    /**
     * 获取nginx状态
     *
     * @return json
     */
    @RequestMapping(value = "status", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String status() {
        String name = nginxService.getServiceName();
        if (StrUtil.isEmpty(name)) {
            return JsonMessage.getString(500, "服务名错误");
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("name", name);
        boolean serviceStatus = AbstractSystemCommander.getInstance().getServiceStatus(name);
        jsonObject.put("status", serviceStatus);
        return JsonMessage.getString(200, "", jsonObject);
    }

    /**
     * 修改nginx配置
     *
     * @param name 服务名
     * @return json
     */
    @RequestMapping(value = "updateConf", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String updateConf(@ValidatorItem(value = ValidatorRule.NOT_BLANK, msg = "服务名称错误") String name) {
        JSONObject ngxConf = nginxService.getNgxConf();
        ngxConf.put("name", name);
        nginxService.save(ngxConf);
        return JsonMessage.getString(200, "修改成功");
    }

    /**
     * 获取配置信息
     *
     * @return json
     */
    @RequestMapping(value = "config", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String config() {
        JSONObject ngxConf = nginxService.getNgxConf();
        return JsonMessage.getString(200, "", ngxConf);
    }

    /**
     * 启动nginx
     *
     * @return json
     */
    @RequestMapping(value = "open", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String open() {
        String name = nginxService.getServiceName();
        String result = AbstractSystemCommander.getInstance().startService(name);
        return JsonMessage.getString(200, "nginx服务已启动 " + result);
    }

    /**
     * 关闭nginx
     *
     * @return json
     */
    @RequestMapping(value = "close", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String close() {
        String name = nginxService.getServiceName();
        String result = AbstractSystemCommander.getInstance().stopService(name);
        return JsonMessage.getString(200, result);
    }

    /**
     * 重新加载
     *
     * @return json
     */
    @RequestMapping(value = "reload", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String reload() {
        String name = nginxService.getServiceName();
        if (SystemUtil.getOsInfo().isLinux()) {
            String result = CommandUtil.execSystemCommand(StrUtil.format("{} -t", name));
            if (StrUtil.isNotEmpty(result)) {
                return JsonMessage.getString(400, result);
            }
        } else if (SystemUtil.getOsInfo().isWindows()) {
            String result = CommandUtil.execSystemCommand("sc qc " + name);
            List<String> strings = StrSpliter.splitTrim(result, "\n", true);
            //服务路径
            File file = null;
            for (String str : strings) {
                str = str.toUpperCase().trim();
                if (str.startsWith("BINARY_PATH_NAME")) {
                    String path = str.substring(str.indexOf(":") + 1).replace("\"", "").trim();
                    file = FileUtil.file(path).getParentFile();
                    break;
                }
            }
            result = CommandUtil.execSystemCommand("nginx -t", file);
            if (StrUtil.isNotEmpty(result)) {
                return JsonMessage.getString(400, result);
            }
        }
        this.reloadNginx();
        return JsonMessage.getString(200, "重新加载成功");
    }
}