import cp from "node:child_process";
import ssh from "ssh2";
import archiver from "archiver";
import fs from "node:fs";
import path from "node:path";
import { getCurrentBranchName, getDeploySlug } from "./util.js";

// 当前分支名
const branchName = getCurrentBranchName();
// 服务器路径名
const deploySlug = getDeploySlug(branchName);

// 最多部署的分支个数
const MAX_DEPLOY_BRANCH_COUNT = 5;

const connectInfo = {
  host: "47.119.149.50",
  port: "22",
  username: "root",
  password: "Qdt20250205",
};

const remoteRootPath = "/usr/local/nginx/www/culture";
const remoteBranchPath = `${remoteRootPath}/${deploySlug}`;
const remoteTarPath = `${remoteBranchPath}/dist.tar.gz`;
const remoteMetaPath = `${remoteBranchPath}/.deploy-meta.json`;

const unzipDirMode = {
  spawn: "npm run build:test",
  servicePath: remoteRootPath,
  unzipDir: deploySlug,
  serviceFilePath: remoteTarPath,
};

const __dirname = path.resolve();
const distPath = path.resolve(__dirname, "dist");
const zipPath = path.resolve(__dirname, "dist.tar.gz");
const { spawn, servicePath, unzipDir, serviceFilePath } = unzipDirMode;

let conn = null;

function connectServer() {
  return new Promise((resolve, reject) => {
    conn = new ssh.Client();

    conn
      .on("ready", () => {
        console.log("SSH 连接成功");
        resolve(conn);
      })
      .on("error", (err) => {
        reject(err);
      })
      .connect(connectInfo);
  });
}

function execCommand(command) {
  return new Promise((resolve, reject) => {
    conn.exec(command, { encoding: "utf-8" }, (err, stream) => {
      if (err) {
        reject(err);
        return;
      }

      let stdout = "";
      let stderr = "";

      stream.on("data", (data) => {
        stdout += data.toString();
      });

      stream.stderr.on("data", (data) => {
        stderr += data.toString();
      });

      stream.on("close", (code) => {
        if (code === 0) {
          resolve(stdout);
          return;
        }

        reject(new Error(stderr.trim() || `命令执行失败: ${command}`));
      });
    });
  });
}

// 获取远程服务器已部署的分支文件夹列表
async function getFolderList() {
  const result = await execCommand(
    `find ${remoteRootPath} -mindepth 1 -maxdepth 1 -type d -printf "%f\\n"`,
  );

  return result
    .split("\n")
    .map((item) => item.trim())
    .filter(Boolean);
}

// 获取已部署的分支的元信息 中文名 服务器路径名 部署时间
async function getDeployEntries(folderList) {
  const entries = await Promise.all(
    folderList.map(async (slug) => {
      const metaFilePath = `${remoteRootPath}/${slug}/.deploy-meta.json`;

      try {
        const rawMeta = await execCommand(`cat '${metaFilePath}'`);
        const meta = JSON.parse(rawMeta);

        return {
          slug,
          branchName: meta.branchName || slug,
          deployedAt: meta.deployedAt || "",
        };
      } catch {
        return {
          slug,
          branchName: slug,
          deployedAt: "",
        };
      }
    }),
  );

  return entries.sort((a, b) => a.branchName.localeCompare(b.branchName, "zh-CN"));
}

// 写入index.html文件
async function writeIndexHtml(entries) {
  // 剔除掉已经部署的
  const dedupedEntries = [];
  const seenSlugs = new Set();

  for (const entry of [{ slug: deploySlug, branchName }, ...entries]) {
    if (seenSlugs.has(entry.slug)) {
      continue;
    }

    seenSlugs.add(entry.slug);
    dedupedEntries.push(entry);
  }

  const indexHtml = `<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>culture</title>
  <style>
    :root {
      --bg: linear-gradient(135deg, #f4fbf8 0%, #e9f6ff 100%);
      --card-bg: rgba(255, 255, 255, 0.88);
      --card-border: rgba(137, 196, 181, 0.24);
      --text-main: #16332d;
      --text-sub: #5e7b74;
      --button-bg: linear-gradient(135deg, #8be2c8 0%, #63d4c5 100%);
      --button-shadow: 0 10px 24px rgba(99, 212, 197, 0.22);
      --button-shadow-hover: 0 14px 28px rgba(99, 212, 197, 0.3);
    }

    * {
      box-sizing: border-box;
    }

    body {
      margin: 0;
      min-height: 100vh;
      font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
      background: var(--bg);
      color: var(--text-main);
    }

    .page {
      width: min(960px, calc(100vw - 32px));
      margin: 0 auto;
      padding: 40px 0 56px;
    }

    .hero {
      margin-bottom: 24px;
      padding: 28px 28px 24px;
      border: 1px solid var(--card-border);
      border-radius: 24px;
      background: var(--card-bg);
      backdrop-filter: blur(10px);
      box-shadow: 0 20px 50px rgba(87, 143, 130, 0.12);
    }

    .hero h1 {
      margin: 0 0 10px;
      font-size: 28px;
      line-height: 1.2;
    }

    .hero p {
      margin: 0;
      color: var(--text-sub);
      font-size: 14px;
      line-height: 1.7;
    }

    .account-panel {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: 12px;
      margin-top: 18px;
      padding-top: 18px;
      border-top: 1px solid rgba(137, 196, 181, 0.18);
    }

    .account-label {
      font-size: 14px;
      font-weight: 600;
      color: var(--text-main);
    }

    .account-input {
      flex: 1 1 260px;
      min-width: 220px;
      height: 44px;
      padding: 0 14px;
      border: 1px solid rgba(108, 180, 163, 0.28);
      border-radius: 14px;
      background: rgba(255, 255, 255, 0.92);
      color: var(--text-main);
      font-size: 14px;
      outline: none;
      transition: border-color 0.2s ease, box-shadow 0.2s ease;
    }

    .account-input:focus {
      border-color: rgba(74, 180, 152, 0.8);
      box-shadow: 0 0 0 4px rgba(116, 211, 187, 0.14);
    }

    .account-tip {
      width: 100%;
      margin: 0;
      color: var(--text-sub);
      font-size: 12px;
      line-height: 1.6;
    }

    .grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
      gap: 16px;
    }

    .branch-card {
      padding: 18px;
      border: 1px solid rgba(137, 196, 181, 0.2);
      border-radius: 20px;
      background: rgba(255, 255, 255, 0.82);
      box-shadow: 0 14px 30px rgba(126, 178, 167, 0.1);
    }

    .branch-name {
      margin: 0 0 12px;
      font-size: 16px;
      font-weight: 600;
      line-height: 1.5;
      word-break: break-word;
    }

    .branch-slug {
      margin: 0 0 16px;
      color: var(--text-sub);
      font-size: 12px;
      line-height: 1.6;
      word-break: break-all;
    }

    .branch-button {
      width: 100%;
      border: 0;
      border-radius: 14px;
      padding: 12px 16px;
      background: var(--button-bg);
      color: #13312a;
      font-size: 14px;
      font-weight: 600;
      cursor: pointer;
      box-shadow: var(--button-shadow);
      transition: transform 0.2s ease, box-shadow 0.2s ease, filter 0.2s ease;
    }

    .branch-button:hover {
      transform: translateY(-2px);
      box-shadow: var(--button-shadow-hover);
      filter: saturate(1.05);
    }

    .branch-button:active {
      transform: translateY(0);
    }

    @media (max-width: 640px) {
      .page {
        width: min(100vw - 20px, 960px);
        padding-top: 20px;
      }

      .hero {
        padding: 22px 18px 18px;
        border-radius: 18px;
      }

      .hero h1 {
        font-size: 24px;
      }
    }
  </style>
</head>
<body>
  <div class="page">
    <section class="hero">
      <h1>Culture 分支导航</h1>
      <p>这里展示当前可访问的测试分支，点击卡片按钮即可进入对应环境。</p>
      <div class="account-panel">
        <label class="account-label" for="accountInput">当前账号</label>
        <input
          id="accountInput"
          class="account-input"
          type="text"
          placeholder="请输入账号"
        />
        <p class="account-tip">账号会自动保存在浏览器本地，后续点击任意分支都会带上当前输入的 code 参数。</p>
      </div>
    </section>
    <div class="grid">
      ${dedupedEntries
        .map(
          (item) => `
        <section class="branch-card">
          <h2 class="branch-name">分支「${item.branchName}」</h2>
          <p class="branch-slug">访问路径：/${item.slug}/</p>
          <button class="branch-button" onclick="goToBranch('${item.slug}')">
            进入当前分支
          </button>
        </section>`,
        )
        .join("\n")}
    </div>
  </div>
  <script>
    const STORAGE_KEY = 'culture_account_code'
    const DEFAULT_ACCOUNT = 'lijiabin'
    const accountInput = document.getElementById('accountInput')

    function getAccountCode() {
      const storedValue = window.localStorage.getItem(STORAGE_KEY)
      return storedValue && storedValue.trim() ? storedValue.trim() : DEFAULT_ACCOUNT
    }

    function setAccountCode(value) {
      const finalValue = value && value.trim() ? value.trim() : DEFAULT_ACCOUNT
      window.localStorage.setItem(STORAGE_KEY, finalValue)
      if (accountInput && accountInput.value !== finalValue) {
        accountInput.value = finalValue
      }
      return finalValue
    }

    function goToBranch(slug) {
      const code = encodeURIComponent(getAccountCode())
      window.location.href = '/' + slug + '/?code=' + code + '@yswg.com.cn'
    }

    if (accountInput) {
      accountInput.value = getAccountCode()
      accountInput.addEventListener('input', (event) => {
        setAccountCode(event.target.value)
      })
      accountInput.addEventListener('blur', (event) => {
        setAccountCode(event.target.value)
      })
    }

    setAccountCode(getAccountCode())
  </script>
</body>
</html>
`;

  return new Promise((resolve, reject) => {
    conn.sftp((err, sftp) => {
      if (err) {
        reject(new Error(`SFTP Error: ${err.message}`));
        return;
      }

      const writeStream = sftp.createWriteStream(`${remoteRootPath}/index.html`, {
        encoding: "utf8",
      });

      writeStream.on("error", (error) => {
        sftp.end();
        reject(new Error(`写入失败: ${error.message}`));
      });

      writeStream.on("close", () => {
        console.log("index.html 写入成功");
        sftp.end();
        resolve();
      });

      writeStream.end(indexHtml);
    });
  });
}

// 写入部署元信息
async function writeDeployMeta() {
  const meta = {
    branchName,
    deploySlug,
    deployedAt: new Date().toLocaleString("zh-CN", {
      hour12: false,
      timeZone: "Asia/Shanghai",
    }),
  };

  const metaContent = JSON.stringify(meta, null, 2);

  await execCommand(`cat <<'EOF' > '${remoteMetaPath}'
${metaContent}
EOF`);
}

async function start() {
  try {
    await connectServer();

    const folderList = await getFolderList();
    const deployEntries = await getDeployEntries(folderList);
    const alreadyDeployed = deployEntries.some((item) => item.slug === deploySlug);

    if (!alreadyDeployed && deployEntries.length >= MAX_DEPLOY_BRANCH_COUNT) {
      console.log(
        `当前已部署分支数量已达到上限 ${MAX_DEPLOY_BRANCH_COUNT}，请先手动删除以下分支中的一个或多个：`,
      );
      deployEntries.forEach((item, index) => {
        console.log(`${index + 1}. ${item.branchName} (${item.slug})`);
      });
      return;
    }

    await writeIndexHtml(deployEntries);
    await build();
    await startZip();
    await uploadTar();
    await shellCmd();
    await writeDeployMeta();

    console.log(`部署完成，当前分支「${branchName}」对应目录：${deploySlug}`);
  } catch (error) {
    console.error("Error:", error.message);
  } finally {
    if (conn) {
      conn.end();
    }
    delZip();
  }
}

start();

function build() {
  return new Promise((resolve, reject) => {
    const pro = cp.spawn(spawn, {
      shell: true,
      stdio: "inherit",
    });

    pro.on("exit", (code) => {
      if (code === 0) {
        console.log("---构建成功---");
        resolve();
        return;
      }

      reject(new Error("构建失败"));
    });
  });
}

function startZip() {
  return new Promise((resolve, reject) => {
    console.log("开始打包 tar");

    const archive = archiver("tar", {
      gzip: true,
      gzipOptions: { level: 9 },
    });

    archive.on("error", (err) => {
      reject(err);
    });

    console.log("zipPath", zipPath);
    const output = fs.createWriteStream(zipPath);

    output.on("close", () => {
      console.log("目标打包完成");
      resolve(true);
    });

    output.on("error", (err) => {
      reject(err);
    });

    archive.pipe(output);
    archive.directory(distPath, false);
    archive.finalize();
  });
}

async function uploadTar() {
  await execCommand(`mkdir -p ${servicePath}/${unzipDir}`);

  return new Promise((resolve, reject) => {
    conn.sftp((err, sftp) => {
      if (err) {
        reject(err);
        return;
      }

      sftp.fastPut(zipPath, serviceFilePath, {}, (putErr) => {
        sftp.end();

        if (putErr) {
          reject(putErr);
          return;
        }

        console.log("文件上传成功");
        resolve();
      });
    });
  });
}

async function shellCmd() {
  const command = `cd ${remoteBranchPath} && tar -xzf dist.tar.gz && rm -f dist.tar.gz`;
  console.log("终端执行命令:", command);
  await execCommand(command);
}

function delZip() {
  if (!fs.existsSync(zipPath)) {
    return;
  }

  fs.unlink(zipPath, (err) => {
    if (err) {
      console.error("删除文件失败:", err.message);
      return;
    }

    console.log(`文件: ${zipPath} 删除成功！`);
  });
}
