import json
import os
import sys
import time
from types import SimpleNamespace

from deployment import (
    log_step,
    print_progress,
    run_cmd,
    connect_ssh,
    ssh_exec,
    parse_bool,
)


def load_frontend_env(path):
    data = {}
    with open(path, "r", encoding="utf-8") as f:
        for raw in f:
            line = raw.strip()
            if not line:
                continue
            if line.startswith("#") or line.startswith(";"):
                continue
            if "=" not in line:
                continue
            k, v = line.split("=", 1)
            k = k.strip()
            v = v.strip()
            if (v.startswith('"') and v.endswith('"')) or (v.startswith("'") and v.endswith("'")):
                v = v[1:-1]
            data[k] = v
    return {
        "server": data.get("SERVER"),
        "port": int(data.get("PORT")) if data.get("PORT") else None,
        "user": data.get("USER"),
        "password": data.get("PASSWORD"),
        "keyfile": data.get("KEYFILE"),
        "fingerprint": data.get("FINGERPRINT"),
        "env": data.get("ENV"),
        "project_dir": data.get("PROJECT_DIR"),
        "build_cmd": data.get("BUILD_CMD") or "npm run build:prod",
        "dist_dir": data.get("DIST_DIR"),
        "remote_dir": data.get("REMOTE_DIR") or "/opt/app/frontend",
        "transfer_timeout": int(data.get("TRANSFER_TIMEOUT")) if data.get("TRANSFER_TIMEOUT") else 300,
        "skip_build": parse_bool(data.get("SKIP_BUILD")),
        "dry_run": parse_bool(data.get("DRY_RUN")),
        "skip_clean_remote": parse_bool(data.get("SKIP_CLEAN_REMOTE")),
    }


def sftp_upload_dir(ssh, local_dir, remote_dir):
    sftp = ssh.open_sftp()
    try:
        try:
            sftp.stat(remote_dir)
        except IOError:
            sftp.mkdir(remote_dir)
        for root, dirs, files in os.walk(local_dir):
            rel = os.path.relpath(root, local_dir)
            if rel == ".":
                remote_root = remote_dir
            else:
                remote_root = remote_dir.rstrip("/") + "/" + rel.replace(os.sep, "/")
                try:
                    sftp.stat(remote_root)
                except IOError:
                    sftp.mkdir(remote_root)
            for name in files:
                lp = os.path.join(root, name)
                rp = remote_root.rstrip("/") + "/" + name
                sftp.put(lp, rp)
    finally:
        try:
            sftp.close()
        except Exception:
            pass


def main():
    default_env = os.path.join(os.path.dirname(__file__), "deployment.frontend.env")
    cfg_path = os.getenv("DEPLOY_FRONTEND_CONFIG") or default_env
    if not os.path.isfile(cfg_path):
        log_step("fe_config_load", "fail", {"path": cfg_path})
        print(f"前端配置文件未找到：{cfg_path}", file=sys.stderr)
        sys.exit(1)

    cfg = load_frontend_env(cfg_path)
    required = ["server", "port", "user", "project_dir", "remote_dir"]
    missing = []
    for k in required:
        v = cfg.get(k)
        if v is None or (isinstance(v, str) and v.strip() == ""):
            missing.append(k)
    if missing:
        log_step("fe_config_validate", "fail", {"missing": missing, "path": cfg_path})
        print(f"前端配置错误：缺少必填项 {', '.join(missing)}；配置文件：{cfg_path}", file=sys.stderr)
        sys.exit(1)

    args = SimpleNamespace(
        server=cfg["server"],
        port=int(cfg["port"]),
        user=cfg["user"],
        password=cfg.get("password"),
        keyfile=cfg.get("keyfile"),
        fingerprint=cfg.get("fingerprint"),
        env=cfg.get("env") or "prod",
        project_dir=cfg["project_dir"],
        build_cmd=cfg["build_cmd"],
        dist_dir=cfg.get("dist_dir"),
        remote_dir=cfg["remote_dir"],
        transfer_timeout=int(cfg.get("transfer_timeout", 300)),
        skip_build=bool(cfg.get("skip_build", False)),
        dry_run=bool(cfg.get("dry_run", False)),
        skip_clean_remote=bool(cfg.get("skip_clean_remote", False)),
    )

    if not args.dist_dir:
        args.dist_dir = os.path.join(args.project_dir, "dist")
    elif not os.path.isabs(args.dist_dir):
        args.dist_dir = os.path.join(args.project_dir, args.dist_dir)

    env_info = {
        "server": args.server,
        "port": args.port,
        "env": args.env,
        "project_dir": args.project_dir,
        "dist_dir": args.dist_dir,
        "remote_dir": args.remote_dir,
    }

    log_step("fe_start", "running", {"config_path": cfg_path}, env=env_info)
    print_progress(0, "前端部署启动", "进行中", f"配置文件：{cfg_path}")

    try:
        if not args.skip_build:
            print_progress(10, "前端构建", "进行中", args.build_cmd)
            rc, out, err = run_cmd(args.build_cmd, cwd=args.project_dir, capture_output=False)
            log_step("fe_build", "ok" if rc == 0 else "fail", {}, env=env_info)
            print_progress(20, "前端构建完成", "成功" if rc == 0 else "失败")
            if rc != 0 and not args.dry_run:
                sys.exit(1)
        else:
            print_progress(10, "跳过前端构建", "已跳过")

        if not os.path.isdir(args.dist_dir):
            log_step("fe_dist_check", "fail", {"path": args.dist_dir}, env=env_info)
            print_progress(25, "检查前端构建目录", "失败")
            if not args.dry_run:
                sys.exit(1)
        else:
            log_step("fe_dist_check", "ok", {"path": args.dist_dir}, env=env_info)
            print_progress(25, "检查前端构建目录", "成功")

        if args.dry_run:
            log_step("fe_connect", "skip", env=env_info)
            log_step("fe_upload", "skip", env=env_info)
            print_progress(100, "前端部署完成", "DRY-RUN")
            log_step("fe_done", "ok", env=env_info)
            return

        print_progress(40, "连接前端服务器", "进行中")
        ssh = connect_ssh(args.server, args.port, args.user, args.password, args.keyfile)
        print_progress(50, "连接前端服务器", "成功")

        ssh_exec(ssh, f'mkdir -p "{args.remote_dir}"')

        if not args.skip_clean_remote:
            print_progress(60, "清理远端目录", "进行中")
            ssh_exec(ssh, f'find "{args.remote_dir}" -mindepth 1 -maxdepth 1 -exec rm -rf {{}} + || true')
            print_progress(65, "清理远端目录", "完成")

        print_progress(70, "上传前端静态资源", "进行中")
        sftp_upload_dir(ssh, args.dist_dir, args.remote_dir)
        log_step("fe_upload", "ok", {"dist": args.dist_dir}, env=env_info)
        print_progress(90, "上传前端静态资源", "完成")

        log_step("fe_done", "ok", env=env_info)
        print_progress(100, "前端部署完成", "成功")
        ssh.close()
    except Exception as e:
        import traceback

        log_step("fe_exception", "fail", {"error": str(e), "trace": traceback.format_exc()}, env=env_info)
        print_progress(100, "前端部署失败", "异常", str(e))
        print(traceback.format_exc(), file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()

