"""Upload a staged directory to Beget shared hosting.

This helper is intended to run on the Beget VPS, where the hosting password is
stored in a root-readable file. It never prints the credential.
"""

from __future__ import annotations

import argparse
import pathlib
import posixpath
import pexpect


def command(session: pexpect.spawn, line: str) -> None:
    session.sendline(line)
    session.expect("sftp>")


parser = argparse.ArgumentParser()
parser.add_argument("source", type=pathlib.Path)
parser.add_argument("destination")
parser.add_argument(
    "--host",
    default="max9581h.beget.tech",
)
parser.add_argument(
    "--user",
    default="max9581h_aaronseo",
)
parser.add_argument(
    "--password-file",
    type=pathlib.Path,
    default=pathlib.Path("/home/aaron/.secrets/beget-sftp-password.txt"),
)
parser.add_argument(
    "--backup-dir",
    type=pathlib.Path,
    help="Download the current remote destination here before uploading.",
)
args = parser.parse_args()

source = args.source.resolve()
if not source.is_dir():
    raise SystemExit(f"Source directory does not exist: {source}")

password = args.password_file.read_text().strip()
session = pexpect.spawn(
    "sftp",
    [
        "-o",
        "StrictHostKeyChecking=accept-new",
        f"{args.user}@{args.host}",
    ],
    encoding="utf-8",
    timeout=120,
)
match = session.expect(["[Pp]assword:", "sftp>", pexpect.EOF, pexpect.TIMEOUT])
if match == 0:
    session.sendline(password)
    session.expect("sftp>")
elif match != 1:
    raise SystemExit("Could not start SFTP session")

destination = args.destination.rstrip("/")
if args.backup_dir:
    backup_dir = args.backup_dir.resolve()
    backup_dir.mkdir(parents=True, exist_ok=True)
    command(session, f"get -r {destination} {backup_dir.as_posix()}")

parts = pathlib.PurePosixPath(destination).parts
current = ""
for part in parts:
    current = posixpath.join(current, part)
    command(session, f"-mkdir {current}")

directories = sorted(
    (path for path in source.rglob("*") if path.is_dir()),
    key=lambda path: len(path.parts),
)
for directory in directories:
    relative = directory.relative_to(source).as_posix()
    command(session, f"-mkdir {posixpath.join(destination, relative)}")

for file in sorted(path for path in source.rglob("*") if path.is_file()):
    relative = file.relative_to(source).as_posix()
    command(session, f"put {file.as_posix()} {posixpath.join(destination, relative)}")

session.sendline("bye")
session.expect(pexpect.EOF)
print(f"Uploaded {source} to {destination}")
