"""Pull the entire DFWDS tree from AD1 to local."""
import base64, os, posixpath, paramiko, subprocess, yaml

LOCAL = r'D:\claudetools\projects\dataforth-dos\dfwds-research\source'
REMOTE_BASE = r'\\AD1\Engineering\ENGR\ATE\Test Datasheets\DFWDS'
AD2_STAGE = r'C:\Users\sysadmin\Documents\dfwds_stage'

pwd_raw = yaml.safe_load(subprocess.run(
    ['sops','-d','D:/vault/clients/dataforth/ad2.sops.yaml'],
    capture_output=True, text=True, timeout=30, check=True,
).stdout)['credentials']['password']
PWD = pwd_raw.replace('\\', '')

c = paramiko.SSHClient(); c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect('192.168.0.6', username='sysadmin', password=PWD,
          timeout=30, banner_timeout=45, look_for_keys=False, allow_agent=False)

def ps(cmd, to=300):
    enc = base64.b64encode(cmd.encode('utf-16-le')).decode()
    _, o, e = c.exec_command(f'powershell -NoProfile -EncodedCommand {enc}', timeout=to)
    return o.read().decode('utf-8','replace'), e.read().decode('utf-8','replace')

# 1. Stage on AD2 (recursive copy)
print('[1] copying tree to AD2 stage', flush=True)
out, err = ps(f'''
if (Test-Path "{AD2_STAGE}") {{ Remove-Item -Recurse -Force "{AD2_STAGE}" }}
Copy-Item -LiteralPath "{REMOTE_BASE}" -Destination "{AD2_STAGE}" -Recurse -Force -ErrorAction Stop
$f = Get-ChildItem -LiteralPath "{AD2_STAGE}" -Recurse -File
"copied: $($f.Count) files, $(($f | Measure-Object Length -Sum).Sum) bytes"
''')
print(out.rstrip())
if err.strip() and 'CLIXML' not in err: print('[stderr]', err[:400])

# 2. SFTP recursive download
print('\n[2] SFTP-pulling to local', flush=True)
sftp = c.open_sftp()

def walk_remote(base):
    """Yield (relpath, is_dir, size) for every entry under base."""
    stack = ['']
    while stack:
        rel = stack.pop()
        for entry in sftp.listdir_attr(f'{base}/{rel}' if rel else base):
            entry_rel = f'{rel}/{entry.filename}' if rel else entry.filename
            from stat import S_ISDIR
            is_dir = S_ISDIR(entry.st_mode)
            yield entry_rel, is_dir, entry.st_size
            if is_dir:
                stack.append(entry_rel)

stage_posix = AD2_STAGE.replace('\\','/')
n_files = 0
n_bytes = 0
for rel, is_dir, sz in walk_remote(stage_posix):
    local_path = os.path.join(LOCAL, rel.replace('/', os.sep))
    if is_dir:
        os.makedirs(local_path, exist_ok=True)
    else:
        os.makedirs(os.path.dirname(local_path), exist_ok=True)
        sftp.get(f'{stage_posix}/{rel}', local_path)
        n_files += 1
        n_bytes += sz

print(f'  pulled {n_files} files, {n_bytes:,} bytes', flush=True)
sftp.close()

# 3. Cleanup AD2 stage
print('\n[3] cleanup AD2 stage', flush=True)
ps(f'Remove-Item -Recurse -Force "{AD2_STAGE}"')
print('[OK]', flush=True)

c.close()
