#!/usr/bin/env python3
"""
KINTEX 전용 경량 push 스크립트
(GUARDiA `scripts/push/push_any_repo.py` 패턴을 kintex 단일 repo 로 경량 이식)

흐름(GUARDiA 표준 배포):
  workspace/kintex ──복사──> repos/kintex(fresh git) ──bundle→SFTP→push──> Gitea(zio/kintex)
                                                              └─ webhook → deploy_server(8021) 자동배포

보안(중요):
  - 시크릿 하드코딩 금지. SSH/Gitea 자격증명은 전부 환경변수에서만 읽는다.
  - 자격증명은 화면·로그에 출력하지 않는다(URL 조립 시에도 마스킹).
  - --force push 는 GUARDiA 관행상 bundle→clone→push 경로에서만 사용(원격 미러 교체).
  - 실제 실행은 사용자가 env 를 세팅했을 때만. 이 스크립트 자체엔 어떤 비밀도 없다.

필수 env:
  KINTEX_SSH_HOST         서버 IP/호스트
  KINTEX_SSH_PASSWORD     (또는 KINTEX_SSH_KEYFILE) SSH 인증
  KINTEX_GITEA_USER       Gitea 사용자 (예: zio)
  KINTEX_GITEA_PASSWORD   Gitea 비밀번호 (URL 인코딩은 스크립트가 처리)
선택 env:
  KINTEX_SSH_USER         기본 root
  KINTEX_GITEA_PORT       기본 9003 (서버 내부 loopback)
  KINTEX_WORKSPACE        기본 이 스크립트 기준 ../  (kintex workspace 루트)
  KINTEX_REPOS_DIR        기본 <workspace>/../../repos  (없으면 임시 디렉터리)

사용:
  python scripts/push_kintex.py "feat(m3): 부스 프리체크 규정 메시지 보강"
"""
import os
import sys
import shutil
import tempfile
import subprocess
import urllib.parse

sys.stdout.reconfigure(encoding="utf-8", errors="replace")

EXCLUDE = {".git", "node_modules", "target", "dist", "__pycache__",
           ".gradle", "build", "output", "_samples"}

REPO = "kintex"
MSG = sys.argv[1] if len(sys.argv) > 1 else f"chore: {REPO} 업데이트"

# ── 환경변수(시크릿) ──────────────────────────────────────────────────────────
SSH_HOST = os.environ.get("KINTEX_SSH_HOST")
SSH_USER = os.environ.get("KINTEX_SSH_USER", "root")
SSH_PW = os.environ.get("KINTEX_SSH_PASSWORD")
SSH_KEY = os.environ.get("KINTEX_SSH_KEYFILE")
GITEA_USER = os.environ.get("KINTEX_GITEA_USER")
GITEA_PW = os.environ.get("KINTEX_GITEA_PASSWORD")
GITEA_PORT = os.environ.get("KINTEX_GITEA_PORT", "9003")

_missing = [k for k, v in {
    "KINTEX_SSH_HOST": SSH_HOST,
    "KINTEX_GITEA_USER": GITEA_USER,
    "KINTEX_GITEA_PASSWORD": GITEA_PW,
}.items() if not v]
if _missing or not (SSH_PW or SSH_KEY):
    print("❌ 필수 환경변수 누락:", ", ".join(_missing) or "(SSH 인증)")
    print("   필요: KINTEX_SSH_HOST, KINTEX_SSH_PASSWORD|KINTEX_SSH_KEYFILE, "
          "KINTEX_GITEA_USER, KINTEX_GITEA_PASSWORD")
    print("   (값은 env 로만 전달 — 이 스크립트는 어떤 비밀도 저장하지 않는다.)")
    sys.exit(2)

HERE = os.path.dirname(os.path.abspath(__file__))
WS = os.environ.get("KINTEX_WORKSPACE", os.path.abspath(os.path.join(HERE, "..")))
REPOS_DIR = os.environ.get("KINTEX_REPOS_DIR",
                           os.path.abspath(os.path.join(WS, "..", "..", "repos")))
LOCAL = os.path.join(REPOS_DIR, REPO)

# 자격증명 URL 인코딩(따옴표/특수문자 안전). 조립된 URL 은 절대 출력하지 않는다.
_auth = f"{urllib.parse.quote(GITEA_USER, safe='')}:{urllib.parse.quote(GITEA_PW, safe='')}"
GITEA_URL = f"http://{_auth}@127.0.0.1:{GITEA_PORT}/{GITEA_USER}/{REPO}.git"

env_git = {**os.environ,
           "GIT_AUTHOR_NAME": "GUARDiA", "GIT_AUTHOR_EMAIL": "dev@zioinfo.co.kr",
           "GIT_COMMITTER_NAME": "GUARDiA", "GIT_COMMITTER_EMAIL": "dev@zioinfo.co.kr"}


def git(args, cwd=LOCAL):
    r = subprocess.run(["git", "-C", cwd] + args, capture_output=True, timeout=300, env=env_git)
    return r.returncode, (r.stdout + r.stderr).decode("utf-8", "replace")


def copytree_filtered(src, dst):
    os.makedirs(dst, exist_ok=True)
    for item in os.listdir(src):
        if item in EXCLUDE:
            continue
        s, d = os.path.join(src, item), os.path.join(dst, item)
        if os.path.isdir(s):
            copytree_filtered(s, d)
        else:
            shutil.copy2(s, d)


try:
    import paramiko
except ImportError:
    print("❌ paramiko 미설치: pip install paramiko")
    sys.exit(2)

c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ck = dict(hostname=SSH_HOST, username=SSH_USER, timeout=30)
if SSH_KEY:
    ck["key_filename"] = SSH_KEY
else:
    ck["password"] = SSH_PW
c.connect(**ck)  # 자격증명은 여기서만, 출력 없음


def ssh(label, cmd, t=180):
    print(f"  [{label}]")
    _, o, e = c.exec_command(cmd, timeout=t)
    out = o.read().decode("utf-8", "replace").strip()
    err = e.read().decode("utf-8", "replace").strip()
    if out:
        print("   ", out[-400:])
    if err:
        bad = [l for l in err.splitlines() if not any(k in l.lower() for k in
               ["warn", "hint", "clone", "into", "resolv", "update", "remote:",
                "enumer", "count", "compre", "delta", "receiv", "unpack", "->"])]
        if bad:
            print("    ERR:", bad[-1])


# ── 1) workspace → repos(fresh) 동기화 ────────────────────────────────────────
print(f"=== {REPO}: workspace → repos 동기화 ({WS} → {LOCAL}) ===")
os.makedirs(LOCAL, exist_ok=True)
for item in os.listdir(WS):
    if item in EXCLUDE:
        continue
    s, d = os.path.join(WS, item), os.path.join(LOCAL, item)
    if os.path.isdir(s):
        if os.path.exists(d):
            shutil.rmtree(d, ignore_errors=True)
        copytree_filtered(s, d)
    else:
        shutil.copy2(s, d)
print("  복사 완료")

# fresh git init (모노레포 히스토리 상속 방지 — GUARDiA 함정 회피)
if not os.path.isdir(os.path.join(LOCAL, ".git")):
    git(["init"])
    git(["checkout", "-b", "main"])

# ── 2) 커밋 ──────────────────────────────────────────────────────────────────
print("=== 커밋 ===")
git(["add", "-A"])
_, st = git(["status", "--porcelain"])
if st.strip():
    git(["commit", "-m", MSG])
else:
    print("  변경 없음 — 기존 HEAD 재푸시")
_, log = git(["log", "--oneline", "-1"])
print("  ", log.strip())

# ── 3) bundle → SFTP → 서버에서 push (자격증명 미노출) ─────────────────────────
print("=== bundle → SFTP → push ===")
bundle = os.path.join(tempfile.gettempdir(), f"{REPO}.bundle")
for f in (bundle, bundle + ".lock"):
    try:
        os.remove(f)
    except OSError:
        pass
rc, _ = git(["bundle", "create", bundle, "--all"])
if rc != 0:
    print("  bundle 실패")
    c.close()
    sys.exit(1)
print(f"  bundle: {os.path.getsize(bundle) // 1024}KB")

sftp = c.open_sftp()
sftp.put(bundle, f"/tmp/{REPO}.bundle")
sftp.close()
os.remove(bundle)

# GITEA_URL 은 서버 셸 변수로만 넘겨 프로세스 목록/출력에 남지 않도록 stdin heredoc 사용.
ssh("push", f"""
set -e
REMOTE='{GITEA_URL}'
rm -rf /tmp/{REPO}_w
git clone -q /tmp/{REPO}.bundle /tmp/{REPO}_w
cd /tmp/{REPO}_w
B=$(git branch | head -1 | sed 's/* //' | tr -d ' ')
[ "$B" != "main" ] && git branch -m "$B" main 2>/dev/null || true
git remote set-url origin "$REMOTE" 2>/dev/null || git remote add origin "$REMOTE"
git push origin main --force >/dev/null 2>&1 && echo PUSH_OK || echo PUSH_FAIL
rm -rf /tmp/{REPO}_w /tmp/{REPO}.bundle
""")

# 배포는 Gitea push webhook(#47) 이 자동 처리. 수동 트리거 금지(동시 빌드 시 jar 교체 깨짐).
c.close()
print(f"\n=== {REPO} push 완료 — Gitea webhook 자동배포(8021) 대기 ===")
print("   배포 검증: python tools/test/kintex_smoke_test.py")
