#!/usr/bin/env python3
"""
Fast build pusher — wipes server public/build/ and replaces with local copy.
Run: python scripts/push_build.py
Requires: pip install paramiko
"""

from __future__ import annotations
import getpass, io, os, sys, tarfile, time
from pathlib import Path

SERVER_HOST = "91.107.131.171"
SERVER_PORT = 2435
SSH_USER    = "root"
SSH_PASS    = os.environ.get("DANESHYAR_SSH_PASSWORD", "")
APP_DIR     = "/var/www/html"
PHP         = "php8.3"

ROOT = Path(__file__).resolve().parents[1]
LOCAL_BUILD = ROOT / "public" / "build"
REMOTE_BUILD = f"{APP_DIR}/public/build"
REMOTE_TAR   = "/tmp/daneshyar-build.tar.gz"


def _connect():
    try:
        import paramiko
    except ImportError:
        print("ERROR: pip install paramiko")
        sys.exit(1)
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    print(f"Connecting to {SSH_USER}@{SERVER_HOST}:{SERVER_PORT} ...")
    password = SSH_PASS or getpass.getpass(f"SSH password for {SSH_USER}@{SERVER_HOST}: ")
    client.connect(SERVER_HOST, port=SERVER_PORT, username=SSH_USER,
                   password=password, timeout=30)
    transport = client.get_transport()
    if transport:
        transport.set_keepalive(20)
        transport.default_window_size = 16 * 1024 * 1024
    print("Connected.\n")
    return client


def _run(client, cmd: str, check=True) -> str:
    chan = client.get_transport().open_session()
    chan.get_pty()
    chan.exec_command(cmd)
    out = b""
    while True:
        chunk = chan.recv(4096)
        if not chunk:
            break
        out += chunk
        sys.stdout.write(chunk.decode(errors="replace"))
        sys.stdout.flush()
    code = chan.recv_exit_status()
    if check and code != 0:
        print(f"\n[FAILED exit={code}]")
        sys.exit(code)
    print()
    return out.decode(errors="replace")


def build_tar() -> bytes:
    if not LOCAL_BUILD.exists():
        print(f"ERROR: {LOCAL_BUILD} not found. Run: npm run build")
        sys.exit(1)
    if not (LOCAL_BUILD / "manifest.json").exists():
        print("ERROR: manifest.json missing. Run: npm run build")
        sys.exit(1)

    print("Packing public/build/ ...")
    buf = io.BytesIO()
    file_count = 0
    with tarfile.open(fileobj=buf, mode="w:gz", compresslevel=6) as tar:
        for path in LOCAL_BUILD.rglob("*"):
            if path.is_file():
                arcname = "build/" + path.relative_to(LOCAL_BUILD).as_posix()
                tar.add(str(path), arcname=arcname)
                file_count += 1
    data = buf.getvalue()
    print(f"  {file_count} files -> {len(data)/1024:.1f} KB compressed\n")
    return data


def upload_tar(client, data: bytes) -> None:
    print(f"Uploading to server ({len(data)/1024:.1f} KB) ...")
    sftp = client.open_sftp()
    with sftp.file(REMOTE_TAR, "wb") as f:
        f.set_pipelined(True)
        chunk = 256 * 1024
        for i in range(0, len(data), chunk):
            f.write(data[i:i+chunk])
    sftp.close()
    print("  Upload complete.\n")


def main():
    if not LOCAL_BUILD.exists() or not (LOCAL_BUILD / "manifest.json").exists():
        print("ERROR: Run 'npm run build' first.")
        sys.exit(1)

    started = time.monotonic()
    client = _connect()

    data = build_tar()
    upload_tar(client, data)

    # Wipe old build dir, extract new one, fix permissions
    _run(client,
         f"rm -rf {REMOTE_BUILD} && "
         f"cd {APP_DIR}/public && tar -xzf {REMOTE_TAR} && "
         f"chown -R www-data:www-data {REMOTE_BUILD} && "
         f"rm -f {REMOTE_TAR}")

    # Bust Laravel view/config caches so new asset hashes are picked up
    _run(client,
         f"cd {APP_DIR} && "
         f"runuser -u www-data -- {PHP} artisan cache:forget partner_schools_active ; "
         f"runuser -u www-data -- {PHP} artisan view:clear && "
         f"runuser -u www-data -- {PHP} artisan view:cache && "
         f"runuser -u www-data -- {PHP} artisan config:cache")

    # Reload Apache
    _run(client, "systemctl reload apache2")

    client.close()
    elapsed = time.monotonic() - started
    print(f"Done in {elapsed:.1f}s — hard-refresh the site (Ctrl+Shift+R).")


if __name__ == "__main__":
    main()
