import { app, ipcMain, Notification } from "electron";
import { getConfig } from "../storage/config";
import { listProxy } from "../storage/proxy";
import { getVersionById } from "../storage/version";
import treeKill from "tree-kill";

const fs = require("fs");
const path = require("path");
const { exec, spawn } = require("child_process");
const log = require("electron-log");
export let frpcProcess = null;
const runningCmd = {
  commandPath: null,
  configPath: null
};
let frpcStatusListener = null;

/**
 * 获取选择版本的工作目录
 * @param versionId 版本ID
 * @param callback
 */
const getFrpcVersionWorkerPath = (
  versionId: number,
  callback: (workerPath: string) => void
) => {
  getVersionById(versionId, (err2, version) => {
    if (!err2) {
      if (version) {
        callback(version["frpcVersionPath"]);
      }
    }
  });
};

/**
 * 生成toml配置文件
 * @param config
 * @param proxys
 */
export const genTomlConfig = (config: FrpConfig, proxys: Proxy[]) => {
  const proxyToml = proxys.map(m => {
    const rangePort =
      (m.localPort.indexOf("-") !== -1 || m.localPort.indexOf(",") !== -1) &&
      (m.type === "tcp" || m.type === "udp");
    let toml = `
${
  rangePort
    ? `{{- range $_, $v := parseNumberRangePair "${m.localPort}" "${m.remotePort}" }}`
    : ""
}
[[${m.type === "stcp" && m.stcpModel === "visitors" ? "visitors" : "proxies"}]]
${rangePort ? "" : `name = "${m.name}"\n`}
type = "${m.type}"
`;

    switch (m.type) {
      case "tcp":
      case "udp":
        if (rangePort) {
          toml += `
name = "${m.name}-{{ $v.First }}"
localPort = {{ $v.First }}
remotePort = {{ $v.Second }}
          `;
        } else {
          toml += `
localIP = "${m.localIp}"
localPort = ${m.localPort}
remotePort = ${m.remotePort}
`;
        }
        break;
      case "http":
      case "https":
        toml += `
localIP = "${m.localIp}"
localPort = ${m.localPort}
customDomains=[${m.customDomains.map(m => `"${m}"`)}]
`;
        break;
      case "stcp":
        if (m.stcpModel === "visitors") {
          // 访问者
          toml += `
serverName = "${m.serverName}"
bindAddr = "${m.bindAddr}"
bindPort = ${m.bindPort}
`;
        } else if (m.stcpModel === "visited") {
          // 被访问者
          toml += `
localIP = "${m.localIp}"
localPort = ${m.localPort}`;
        }
        toml += `
secretKey="${m.secretKey}"
`;
        break;
      default:
        break;
    }

    if (rangePort) {
      toml += `{{- end }}`;
    }
    return toml;
  });
  const toml = `
serverAddr = "${config.serverAddr}"
serverPort = ${config.serverPort}
${
  config.authMethod === "token"
    ? `
auth.method = "token"
auth.token = "${config.authToken}"
`
    : ""
}
${
  config.authMethod === "multiuser"
    ? `
user = "${config.user}"
metadatas.token = "${config.metaToken}"
`
    : ""
}
${
  config.transportHeartbeatInterval
    ? `
transport.heartbeatInterval = ${config.transportHeartbeatInterval}
`
    : ""
}
${
  config.transportHeartbeatTimeout
    ? `
transport.heartbeatTimeout = ${config.transportHeartbeatTimeout}
`
    : ""
}


log.to = "frpc.log"
log.level = "${config.logLevel}"
log.maxDays = ${config.logMaxDays}
webServer.addr = "127.0.0.1"
webServer.port = 57400
transport.tls.enable = ${config.tlsConfigEnable}
${
  config.tlsConfigEnable && config.tlsConfigCertFile
    ? `
transport.tls.certFile = "${config.tlsConfigCertFile}"
`
    : ""
}
  ${
    config.tlsConfigEnable && config.tlsConfigKeyFile
      ? `
transport.tls.keyFile = "${config.tlsConfigKeyFile}"
`
      : ""
  }
  ${
    config.tlsConfigEnable && config.tlsConfigTrustedCaFile
      ? `
transport.tls.trustedCaFile = "${config.tlsConfigTrustedCaFile}"
`
      : ""
  }
  ${
    config.tlsConfigEnable && config.tlsConfigServerName
      ? `
transport.tls.serverName = "${config.tlsConfigServerName}"
`
      : ""
  }
  

${
  config.proxyConfigEnable
    ? `
transport.proxyURL = "${config.proxyConfigProxyUrl}"
`
    : ""
}

${proxyToml.join("")}
      `;
  return toml;
};

/**
 * 生成ini配置
 * @param config
 * @param proxys
 */
export const genIniConfig = (config: FrpConfig, proxys: Proxy[]) => {
  const proxyIni = proxys.map(m => {
    let ini = `
[${m.name}]
type = "${m.type}"
`;
    switch (m.type) {
      case "tcp":
      case "udp":
        ini += `
local_ip = "${m.localIp}"
local_port = ${m.localPort}
remote_port = ${m.remotePort}
`;
        break;
      case "http":
      case "https":
        ini += `
local_ip = "${m.localIp}"
local_port = ${m.localPort}
custom_domains=[${m.customDomains.map(m => `"${m}"`)}]
`;
        break;
      case "stcp":
        if (m.stcpModel === "visitors") {
          // 访问者
          ini += `
role = visitor
server_name = "${m.serverName}"
bind_addr = "${m.bindAddr}"
bind_port = ${m.bindPort}
`;
        } else if (m.stcpModel === "visited") {
          // 被访问者
          ini += `
local_ip = "${m.localIp}"
local_port = ${m.localPort}`;
        }
        ini += `
sk="${m.secretKey}"
`;
        break;
      default:
        break;
    }

    return ini;
  });
  const ini = `
[common]
server_addr = ${config.serverAddr}
server_port = ${config.serverPort}
${
  config.authMethod === "token"
    ? `
authentication_method = ${config.authMethod}
token = ${config.authToken}
`
    : ""
}
${
  config.authMethod === "multiuser"
    ? `
user = ${config.user}
meta_token = ${config.metaToken}
`
    : ""
}

${
  config.transportHeartbeatInterval
    ? `
heartbeat_interval = ${config.transportHeartbeatInterval}
`
    : ""
}
${
  config.transportHeartbeatTimeout
    ? `
heartbeat_timeout = ${config.transportHeartbeatTimeout}
`
    : ""
}

log_file = "frpc.log"
log_level = ${config.logLevel}
log_max_days = ${config.logMaxDays}
admin_addr = 127.0.0.1
admin_port = 57400
tls_enable = ${config.tlsConfigEnable}

${
  config.tlsConfigEnable && config.tlsConfigCertFile
    ? `
tls_cert_file = ${config.tlsConfigCertFile}
`
    : ""
}
  ${
    config.tlsConfigEnable && config.tlsConfigKeyFile
      ? `
tls_key_file = ${config.tlsConfigKeyFile}
`
      : ""
  }
  ${
    config.tlsConfigEnable && config.tlsConfigTrustedCaFile
      ? `
tls_trusted_ca_file = ${config.tlsConfigTrustedCaFile}
`
      : ""
  }
  ${
    config.tlsConfigEnable && config.tlsConfigServerName
      ? `
tls_server_name = ${config.tlsConfigServerName}
`
      : ""
  }

${
  config.proxyConfigEnable
    ? `
http_proxy = "${config.proxyConfigProxyUrl}"
`
    : ""
}

${proxyIni.join("")}
    `;
  return ini;
};

/**
 * 生成配置文件
 */
export const generateConfig = (
  config: FrpConfig,
  callback: (configPath: string) => void
) => {
  listProxy((err3, proxys) => {
    if (!err3) {
      const { currentVersion } = config;
      let filename = null;
      let configContent = "";
      const filtered = proxys
        .map(m => {
          if (m.status == null || m.status == undefined) {
            m.status = true;
          }
          return m;
        })
        .filter(f => f.status);
      if (currentVersion < 124395282) {
        // 版本小于v0.52.0
        filename = "frp.ini";
        configContent = genIniConfig(config, filtered);
      } else {
        filename = "frp.toml";
        configContent = genTomlConfig(config, filtered);
      }
      const configPath = path.join(app.getPath("userData"), filename);
      log.info(`生成配置成功 配置路径：${configPath}`);
      fs.writeFile(
        configPath, // 配置文件目录
        configContent, // 配置文件内容
        { flag: "w" },
        err => {
          if (!err) {
            callback(filename);
          }
        }
      );
    }
  });
};

/**
 * 启动frpc子进程
 * @param cwd
 * @param commandPath
 * @param configPath
 */
const startFrpcProcess = (commandPath: string, configPath: string) => {
  log.info(`启动frpc 目录：${app.getPath("userData")} 命令：${commandPath}`);
  const command = `${commandPath} -c ${configPath}`;
  frpcProcess = spawn(command, {
    cwd: app.getPath("userData"),
    shell: true
  });
  runningCmd.commandPath = commandPath;
  runningCmd.configPath = configPath;
  frpcProcess.stdout.on("data", data => {
    log.debug(`启动输出：${data}`);
  });
  frpcProcess.stdout.on("error", data => {
    log.error(`启动错误：${data}`);
    stopFrpcProcess(() => {});
  });
  frpcStatusListener = setInterval(() => {
    const status = frpcProcessStatus();
    log.debug(`监听frpc子进程状态：${status} ${frpcStatusListener}`);
    if (!status) {
      new Notification({
        title: "Frpc Desktop",
        body: "连接已断开，请前往日志查看原因"
      }).show();
      clearInterval(frpcStatusListener);
    }
  }, 3000);
};

/**
 *  重载frpc配置
 */
export const reloadFrpcProcess = () => {
  if (frpcProcess && !frpcProcess.killed) {
    getConfig((err1, config) => {
      if (!err1) {
        if (config) {
          generateConfig(config, configPath => {
            const command = `${runningCmd.commandPath} reload -c ${configPath}`;
            log.info(`重载配置：${command}`);
            exec(command, {
              cwd: app.getPath("userData"),
              shell: true
            });
          });
        }
      }
    });
  }
};

/**
 * 停止frpc子进程
 */
export const stopFrpcProcess = (callback?: () => void) => {
  if (frpcProcess) {
    treeKill(frpcProcess.pid, (error: Error) => {
      if (error) {
        log.error(`关闭frpc子进程失败 pid：${frpcProcess.pid} error：${error}`);
        callback();
      } else {
        log.info(`关闭frpc子进程成功`);
        frpcProcess = null;
        clearInterval(frpcStatusListener);
        callback();
      }
    });
  } else {
    callback();
  }
};

/**
 * 获取frpc子进程状态
 */
export const frpcProcessStatus = () => {
  if (!frpcProcess) {
    return false;
  }
  try {
    // 发送信号给进程，如果进程存在，会正常返回
    process.kill(frpcProcess.pid, 0);
    return true;
  } catch (error) {
    // 进程不存在，抛出异常
    return false;
  }
};

/**
 * 启动frpc流程
 * @param config
 */
export const startFrpWorkerProcess = async (config: FrpConfig) => {
  getFrpcVersionWorkerPath(config.currentVersion, (frpcVersionPath: string) => {
    if (frpcVersionPath) {
      generateConfig(config, configPath => {
        const platform = process.platform;
        if (platform === "win32") {
          startFrpcProcess(path.join(frpcVersionPath, "frpc.exe"), configPath);
        } else {
          startFrpcProcess(path.join(frpcVersionPath, "frpc"), configPath);
        }
      });
    }
  });
};

export const initFrpcApi = () => {
  ipcMain.handle("frpc.running", async (event, args) => {
    return frpcProcessStatus();
  });

  ipcMain.on("frpc.start", async (event, args) => {
    getConfig((err1, config) => {
      if (!err1) {
        if (!config) {
          event.reply(
            "Home.frpc.start.error.hook",
            "请先前往设置页面，修改配置后再启动"
          );
          return;
        }
        if (!config.currentVersion) {
          event.reply(
            "Home.frpc.start.error.hook",
            "请先前往设置页面，修改配置后再启动"
          );
          return;
        }
        startFrpWorkerProcess(config);
      }
    });
  });

  ipcMain.on("frpc.stop", () => {
    if (frpcProcess && !frpcProcess.killed) {
      stopFrpcProcess(() => {});
    }
  });
};
