package org.jeecg.modules.adweb.site.service;

import jakarta.annotation.Resource;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.constant.WebsocketConst;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.adweb.common.service.FeishuService;
import org.jeecg.modules.adweb.common.service.ShellService;
import org.jeecg.modules.adweb.common.service.WebSocketService;
import org.jeecg.modules.adweb.common.util.ShellSSH2Util;
import org.jeecg.modules.adweb.server.entity.AdwebServer;
import org.jeecg.modules.adweb.server.service.IAdwebServerService;
import org.jeecg.modules.adweb.site.entity.AdwebSite;
import org.jeecg.modules.adweb.system.entity.SysException;
import org.jeecg.modules.adweb.system.service.ISysExceptionService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;

import java.io.File;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;

/**
 * @author jerry
 */
@Slf4j
@Service
@EnableAsync
public class SelfWebSiteService {

    @Resource private IAdwebSiteService siteService;

    /** 远程脚本执行服务类 */
    @Resource private ShellService shellService;

    @Resource private SelfWebSiteServiceCommon selfWebSiteServiceCommon;

    @Resource private WebSocketService webSocketService;

    @Resource private IAdwebServerService serverService;

    @Value("${AdwebSiteConnect.host}")
    private String host;

    @Value("${AdwebSiteConnect.port}")
    private int port;

    @Value("${AdwebSiteConnect.username}")
    private String username;

    @Value("${AdwebSiteConnect.password}")
    private String password;

    @Value(value = "${jeecg.path.upload}")
    private String uploadpath;

    @Resource private FeishuService openFeishuMsgService;

    @Resource private ISysExceptionService sysExceptionService;

    private String errorMsg = "";

    /**
     * 创建站点的方法
     *
     * @param user 当前登录用户
     * @param tempId 模板ID
     * @param uid 用户ID
     * @param name 站点名称
     * @param planId 套餐ID
     * @param sysUser 系统用户
     * @param giveDay 赠送天数
     * @param compensateDay 补偿天数
     */
    @Async
    public void createSite(
            LoginUser user,
            Long tempId,
            String uid,
            String name,
            String planId,
            LoginUser sysUser,
            Integer giveDay,
            Integer compensateDay) {
        // 创建站点信息，生成code，拼接域名等，并将站点信息写入数据库

        AdwebSite adwebSite = null;
        try {
            adwebSite =
                    selfWebSiteServiceCommon.saveNewSiteInfo(
                            tempId, uid, name, planId, sysUser, giveDay, compensateDay);
        } catch (Exception e) {
            log.error("新建站点时保存站点信息失败", e);
        }

        try {

            // 执行临时服务器shell脚本，生成临时站点
            String cmd =
                    "sh /opt/adweb3/shell/auto-wpsite "
                            + adwebSite.getCode()
                            + " "
                            + adwebSite.getParentCode();

            AdwebSite finalAdwebSite = adwebSite;
            shellService.createShareSiteByPwd(
                    cmd,
                    host,
                    port,
                    username,
                    password,
                    new ShellSSH2Util.StdoutListener() {
                        @Override
                        public void stdout(String line) {
                            log.info("标准 ：{}", line);

                            if (line.startsWith("执行命令成功")) {
                                log.info("站点创建成功！更新站点状态");

                                // 更新站点流程状态
                                siteService.updateSiteFlow(finalAdwebSite.getCode());

                                // 临时站点创建成功
                                finalAdwebSite.setStatus(2);
                                siteService.updateById(finalAdwebSite);

                                String[] userIds = {user.getId()};
                                // 给该站点所属的用户发送消息
                                log.info("发送创建站点通知");
                                webSocketService.sendMessage(
                                        userIds,
                                        "站点创建完成通知",
                                        "您的站点已经创建成功，请在站点管理中查看该站点详细信息",
                                        WebsocketConst.CMD_MANAGE_SITE);
                            }
                        }

                        @Override
                        public void stderr(String line) {
                            log.info("错误 ：{}", line);
                            errorMsg += line;
                        }
                    });

            if (StringUtils.isNotBlank(errorMsg)) {
                createSiteFailed(adwebSite, errorMsg, 4);
            }
        } catch (Exception e) {
            log.error("在wp服务器上生成站点文件失败");
            createSiteFailed(adwebSite, e.getMessage(), 4);
        }
    }

    /**
     * 删除指定的广告网站
     *
     * <p>此方法通过执行远程服务器上的Shell脚本来删除广告网站资源，并在删除成功后通知用户 如果删除失败，会记录错误信息并抛出异常
     *
     * @param user 当前操作的用户，用于发送通知
     * @param adwebSite 要删除的广告网站对象，包含网站代码和域名等信息
     */
    @Async
    public void removeSite(LoginUser user, AdwebSite adwebSite) {
        try {

            // 执行临时服务器shell脚本，生成临时站点
            String cmd = "sh /opt/adweb3/shell/remove-and-bak-wpsite.sh " + adwebSite.getCode();

            AdwebSite finalAdwebSite = adwebSite;
            shellService.createShareSiteByPwd(
                    cmd,
                    host,
                    port,
                    username,
                    password,
                    new ShellSSH2Util.StdoutListener() {
                        @Override
                        public void stdout(String line) {
                            log.info("标准 ：{}", line);

                            if (line.startsWith("执行命令成功")) {
                                log.info("站点资源删除成功！");

                                String[] userIds = {user.getId()};
                                // 给该站点所属的用户发送消息
                                log.info("发送删除站点资源通知");
                                webSocketService.sendMessage(
                                        userIds,
                                        "站点删除通知",
                                        "站点资源" + finalAdwebSite.getDomain() + "已经从系统删除！",
                                        WebsocketConst.CMD_MANAGE_SITE);
                            }
                        }

                        @Override
                        public void stderr(String line) {
                            log.info("错误 ：{}", line);
                            errorMsg += line;
                        }
                    });

            if (StringUtils.isNotBlank(errorMsg)) {
                exceShellFailed("删除站点资源", adwebSite, errorMsg);
            }
        } catch (Exception e) {
            log.error("在wp服务器上删除站点资源失败");
            exceShellFailed("删除站点资源", adwebSite, e.getMessage());
        }
    }

    /**
     * 当执行Shell命令失败时调用此方法记录错误信息
     *
     * @param cmd 执行的Shell命令，用于标识操作
     * @param adwebSite 发生错误的广告网站实例，包含站点信息
     * @param errorMsg 错误信息描述，详细说明执行失败的原因
     */
    private void exceShellFailed(String cmd, AdwebSite adwebSite, String errorMsg) {
        SysException sysException = new SysException();

        sysException.setType(1);
        sysException.setFunctionModule(cmd);
        sysException.setExceptionDetail(
                String.format(
                        "站点code:%s, 站点名称：%s，临时连接：%s",
                        adwebSite.getCode(), adwebSite.getName(), adwebSite.getDomain()));
        sysException.setExceptionDescription(errorMsg);

        sysExceptionService.save(sysException);
    }

    /**
     * 处理临时站点创建失败的逻辑 当临时站点创建失败时，此方法会被调用，更新站点状态并记录错误信息
     *
     * @param adwebSite 代表临时站点的对象，包含站点的相关信息
     * @param errorMsg 描述创建失败的错误信息
     */
    private void createSiteFailed(AdwebSite adwebSite, String errorMsg, int status) {
        adwebSite.setStatus(status); // 临时站点创建失败 或者正式站点创建失败
        siteService.updateById(adwebSite);

        SysException sysException = new SysException();

        sysException.setType(1); // 业务错误
        sysException.setFunctionModule("拉取站点错误");
        sysException.setExceptionDetail(
                String.format(
                        "站点code:%s, 站点名称：%s，临时连接：%s",
                        adwebSite.getCode(), adwebSite.getName(), adwebSite.getDomain()));
        sysException.setExceptionDescription(errorMsg);

        sysExceptionService.save(sysException);
    }

    /**
     * 发布站点到生产环境
     *
     * @param user 当前登录用户
     * @param serverId 服务器ID，未在方法体内使用
     * @param domain 域名，用于站点发布
     * @param siteCode 站点代码，标识特定的站点
     */
    @Async
    public void releaseSite(LoginUser user, String serverId, String domain, String siteCode) {
        log.info("===== releaseSite domain:{},siteCode:{} ====", domain, siteCode);
        StringBuilder statusSuccess = new StringBuilder();
        StringBuilder statusFail = new StringBuilder();
        AdwebSite adwebSite = siteService.getSiteByCode(siteCode);
        AdwebServer adwebServer = serverService.getById(serverId);

        if (adwebServer == null) {
            log.error("发布生产环境时未指定服务器");
            return;
        }

        String serverShellPath = "/opt/adweb3/shell";

        // 原始域名
        String originDomain = domain;
        // 检验域名是否以www开头
        if (domain.startsWith("www.")) {
            domain = domain.replace("www.", "");
        }
        String dbName = domain.replaceAll("[^a-zA-Z0-9]", "") + "_" + adwebSite.getCode();

        log.info(
                "===== releaseSite domain:{}, siteCode:{},originDomain:{},domain:{},dbName:{} ====",
                domain,
                siteCode,
                originDomain,
                domain,
                dbName);

        String cmd =
                "sudo sh "
                        + serverShellPath
                        + "/publish_wp_site.sh "
                        + adwebSite.getCode()
                        + " "
                        + domain
                        + " "
                        + dbName;

        log.info(
                "===== releaseSite originDomain:{},domain:{},siteCode:{},cmd:{} ====",
                originDomain,
                domain,
                siteCode,
                cmd);

        try {
            if (StringUtils.isBlank(adwebServer.getServerPem())) {
                String[] userIds = {user.getId()};
                // 给该站点所属的用户发送消息
                log.info("生产服务器：{}，未配置连接秘钥", adwebServer.getServerName());
                webSocketService.sendMessage(
                        userIds,
                        "站点发布失败通知",
                        "该站点发布失败，请联系管理员配置服务器连接秘钥",
                        WebsocketConst.CMD_MANAGE_SITE);
                return;
            }

            adwebSite.setStatus(5); // 正式站点正在创建
            siteService.updateById(adwebSite);

            String pemPath = adwebServer.getServerPem().replace("..", "").replace("../", "");
            if (pemPath.endsWith(SymbolConstant.COMMA)) {
                pemPath = pemPath.substring(0, pemPath.length() - 1);
            }

            String filePath = uploadpath + File.separator + pemPath;
            adwebServer.setServerPem(filePath); // 设置秘钥绝对路径

            shellService.exceShell(
                    cmd,
                    new ShellSSH2Util.StdoutListener() {
                        @Override
                        public void stdout(String line) {
                            log.info("标准 ：{}", line);

                            if ("[Creation completed]".equals(line)) {
                                log.info("======网站上线成功======");
                                statusSuccess.append("ok");
                            }
                        }

                        @Override
                        public void stderr(String line) {
                            log.info("错误 ：{}", line);
                            statusFail.append(line);
                        }
                    },
                    adwebServer);

            // 正式域名
            log.info(
                    "===== releaseSite originDomain:{},domain:{},siteCode:{},statusSuccess:{} ====",
                    originDomain,
                    domain,
                    siteCode,
                    statusSuccess);
            log.info(
                    "===== releaseSite originDomain:{},domain:{},siteCode:{},statusFail:{} ====",
                    originDomain,
                    domain,
                    siteCode,
                    statusFail);
            if (StringUtils.isNotEmpty(statusFail.toString())) {
                String title = "网站域名：" + originDomain + " 站点发布失败";
                String message = "**站点CODE:** " + siteCode + "\n";
                message += "**临时域名:** " + adwebSite.getDomainDev() + "\n";
                message += "**服务器IP:** " + adwebSite.getDevServerIp() + "\n";
                message += "**错误信息:** " + statusFail.toString() + "\n";
                sendAdWebV3FeiShuMsg(title, message);

                // 站点发布失败
                createSiteFailed(adwebSite, statusFail.toString(), 6);

            } else {
                String title = "网站域名：" + originDomain + " 站点发布成功";
                String message = "**站点CODE:** " + siteCode + "\n";
                message += "**临时域名:** " + adwebSite.getDomainDev() + "\n";
                message += "**服务器IP:** " + adwebSite.getDevServerIp() + "\n";
                sendAdWebV3FeiShuMsg(title, message); // 发送飞书消息

                String[] userIds = {user.getId()};
                // 给该站点所属的用户发送消息
                log.info("发布站点成功通知");
                webSocketService.sendMessage(
                        userIds,
                        "站点发布完成通知",
                        "您的站点已经发布成功，请在站点管理中查看该站点详细信息",
                        WebsocketConst.CMD_MANAGE_SITE);

                // 创建站点完成-操作
                adwebSite.setDomain(originDomain);
                adwebSite.setIsDomain(1);
                // 添加发布时间
                adwebSite.setIssueTime(new Date());
                // 发布成功后修改状态为发布成功状态 ：1
                adwebSite.setStatus(1);
                adwebSite.setProdServerIp(adwebServer.getServerIp());
                siteService.updateById(adwebSite);
            }
        } catch (Exception e) {
            log.error("站点发布失败，退出执行；{}", e.getMessage(), e);
            createSiteFailed(adwebSite, e.getMessage(), 6);
        }
    }

    /**
     * 删除发布失败的站点(生产环境)
     *
     * @param domain 新域名
     * @param siteId 站点ID
     */
    public Result<?> delReleaseWebsite(String domain, Integer siteId) {
        log.info("===== delReleaseWebsite domain:{},siteId:{} ====", domain, siteId);
        StringBuilder delSuccess = new StringBuilder();
        StringBuilder delFail = new StringBuilder();
        AdwebSite adwebSite = siteService.getById(siteId);
        String usWest118ServerShellPath = "/home/ci-user/shell";
        // 检验域名是否以www开头
        if (domain.startsWith("www.")) {
            domain = domain.replace("www.", "");
        }
        // 删除已发布的站点，重新发布
        if (adwebSite.getStatus() == 5) {
            String cmd =
                    "sudo sh " + usWest118ServerShellPath + "/adweb-remove_prod_site.sh " + domain;
            log.info(
                    "===== delReleaseWebsite domain:{},siteId:{},cmd:{} ====", domain, siteId, cmd);
            try {
                String finalDomain = domain;
                //                shellService.exceShellByUsWest118(cmd, new
                // ShellSSH2Util.StdoutListener() {
                //                    @Override
                //                    public void stdout(String line) {
                //                        log.info("标准 ：" + line);
                //                        if ("remove site success".equals(line) || "remove site
                // nginx success".equals(line)) {
                //                            log.info("======执行删除旧站点，成功通知======");
                //                            String title = "网站域名：" + finalDomain + "
                // 删除发布失败的旧站点成功";
                //                            String message = "**站点ID:** " + siteId + "\n";
                //                            message += "**临时域名:** " + adwebSite.getDomainDev() +
                // "\n";
                //                            message += "**服务器IP:** " + adwebSite.getDevServerIp()
                // + "\n";
                //                            sendAdWebV3FeiShuMsg(title, message);
                //
                //                            delSuccess.append(line);
                //                        }
                //
                //                        if ("remove site failed".equals(line) || "remove site
                // nginx failed".equals(line)) {
                //                            log.info("======执行删除旧站点，失败通知======");
                //                            String title = "网站域名：" + finalDomain + "
                // 删除发布失败的旧站点失败";
                //                            String message = "**站点ID:** " + siteId + "\n";
                //                            message += "**临时域名:** " + adwebSite.getDomainDev() +
                // "\n";
                //                            message += "**服务器IP:** " + adwebSite.getDevServerIp()
                // + "\n";
                //                            message += "**错误信息:** " + line + "\n";
                //                            sendAdWebV3FeiShuMsg(title, message);
                //
                //                            delFail.append(line);
                //                        }
                //                    }
                //
                //                    @Override
                //                    public void stderr(String line) {
                //                        log.info("错误 ：" + line);
                //                        String title = "网站域名：" + finalDomain + " 删除发布失败的旧站点失败";
                //                        String message = "**站点ID:** " + siteId + "\n";
                //                        message += "**临时域名:** " + adwebSite.getDomainDev() + "\n";
                //                        message += "**服务器IP:** " + adwebSite.getDevServerIp() +
                // "\n";
                //                        message += "**错误信息:** " + line + "\n";
                //                        sendAdWebV3FeiShuMsg(title, message);
                //
                //                        delFail.append(line);
                //                    }
                //                });
            } catch (Exception e) {
                log.error("删除发布失败的旧站点失败，退出执行；" + e.getMessage(), e);
                delFail.append(e.getMessage());
            }

            if (StringUtils.isNotBlank(delFail.toString())) {
                return Result.error("旧站点删除失败，请重试！");
            } else {
                return Result.OK("旧站点删除成功！");
            }
        } else {
            return Result.error("该站点状态不是发布失败，执行失败！");
        }
    }

    /**
     * 站点启动/停止
     *
     * @param sysUser
     * @param siteId
     * @param status
     * @return
     */
    public boolean siteStartOrStop(LoginUser sysUser, Integer siteId, Integer status) {
        AdwebSite adwebSite = siteService.getById(siteId);

        // 获取站点所在生产服务器信息
        AdwebServer serverInfo = new AdwebServer();
        boolean flag = false;
        switch (status) {
            case 1:
                flag = startSiteLocal(sysUser, adwebSite);
                break;
            case 2:
                flag = stopSiteLocal(sysUser, adwebSite);
                break;
            case 3:
                flag = startSite(sysUser, adwebSite, serverInfo);
                break;
            case 4:
                flag = stopSite(sysUser, adwebSite, serverInfo);
                break;
        }
        return flag;
    }

    /**
     * 站点启动脚本调用(未发布)
     *
     * @param sysUser
     * @param adwebSite
     * @return
     */
    private boolean startSiteLocal(LoginUser sysUser, AdwebSite adwebSite) {
        // 站点启动
        String cmd = "/home/ci-user/shell/start_site " + adwebSite.getCode() + " ";
        shellService.exceShellByPwd(
                cmd,
                host,
                port,
                username,
                password,
                new ShellSSH2Util.StdoutListener() {
                    @Override
                    public void stdout(String line) {
                        log.info("标准 ：" + line);
                        if ("ERROR:FILE_NAME IS EXISTS".equals(line)) {
                            // TODO ----
                            log.error("恢复测试站点异常：" + line);
                        }
                    }

                    @Override
                    public void stderr(String line) {
                        log.info("错误 ：" + line);
                    }
                });
        return siteStatusUpdate(sysUser, adwebSite, 0);
    }

    /**
     * 站点停止脚本调用（未发布）
     *
     * @param sysUser
     * @param adwebSite
     * @return
     */
    private boolean stopSiteLocal(LoginUser sysUser, AdwebSite adwebSite) {
        String cmd = "/home/ci-user/shell/stop_site " + adwebSite.getCode() + " ";
        shellService.exceShellByPwd(
                cmd,
                host,
                port,
                username,
                password,
                new ShellSSH2Util.StdoutListener() {
                    @Override
                    public void stdout(String line) {
                        log.info("标准 ：" + line);
                        if ("ERROR:FILE_NAME IS EXISTS".equals(line)) {
                            // TODO ----
                            log.error("停止测试站点异常：" + line);
                        }
                    }

                    @Override
                    public void stderr(String line) {
                        log.info("错误 ：" + line);
                    }
                });
        return siteStatusUpdate(sysUser, adwebSite, 3);
    }

    /**
     * 站点启动脚本调用(生产环境)
     *
     * @param sysUser
     * @param adwebSite
     * @return
     */
    private boolean startSite(LoginUser sysUser, AdwebSite adwebSite, AdwebServer serverInfo) {
        // 站点启动
        String cmd =
                "./shell/start_site " + serverInfo.getServerIp() + " " + adwebSite.getCode() + " ";
        shellService.exceShell(
                cmd,
                new ShellSSH2Util.StdoutListener() {
                    @Override
                    public void stdout(String line) {
                        log.info("MJJ控制台 ：" + line);
                        if ("ERROR:FILE_NAME IS EXISTS".equals(line)) {
                            // TODO ----
                            log.error("恢复站点异常：" + line);
                        }
                    }

                    @Override
                    public void stderr(String line) {
                        log.info("错误 ：" + line);
                    }
                },
                serverInfo);
        return siteStatusUpdate(sysUser, adwebSite, 0);
    }

    /**
     * 站点停止脚本调用（生产环境）
     *
     * @param sysUser
     * @param adwebSite
     * @return
     */
    private boolean stopSite(LoginUser sysUser, AdwebSite adwebSite, AdwebServer serverInfo) {
        String cmd =
                "./shell/stop_site " + serverInfo.getServerIp() + " " + adwebSite.getCode() + " ";
        shellService.exceShell(
                cmd,
                new ShellSSH2Util.StdoutListener() {
                    @Override
                    public void stdout(String line) {
                        log.info("MJJ控制台 ：" + line);
                        if ("ERROR:FILE_NAME IS EXISTS".equals(line)) {
                            // TODO ----
                            log.error("停止站点异常：" + line);
                        }
                    }

                    @Override
                    public void stderr(String line) {
                        log.info("错误 ：" + line);
                    }
                },
                serverInfo);
        return siteStatusUpdate(sysUser, adwebSite, 3);
    }

    /**
     * 站点状态更改
     *
     * @param sysUser
     * @param adwebSite
     * @param runStatus
     * @return
     */
    private boolean siteStatusUpdate(LoginUser sysUser, AdwebSite adwebSite, Integer runStatus) {
        if (runStatus.equals(3)) {
            adwebSite.setStopTime(new Date());
            adwebSite.setStopUid(sysUser.getId());
        }
        adwebSite.setRunStatus(runStatus);
        return siteService.updateById(adwebSite);
    }

    /**
     * @param msgTitle
     * @param message
     */
    public void sendAdWebV3FeiShuMsg(String msgTitle, String message) {
        try {
            openFeishuMsgService.sendAdWebV3FeiShuMsg(msgTitle, message);
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            throw new RuntimeException(e);
        }
    }
}
