#!/usr/bin/env python3
"""YMServer SSH pull sync, first implementation. Run as root for local unix_socket receiver.
Expects trusted key/known_hosts, fixed target, no network listener.
"""
import argparse
import hashlib
import json
import os
import subprocess
import sys
import uuid

KEY = '/home/fjweber/.ssh/id_ed25519_yachtmemory_deploy'
KNOWN_HOSTS = '/home/fjweber/.ssh/known_hosts'
REMOTE = 'ymdeploy@10.99.0.8'
AGENT = '/usr/local/lib/yachtmemory/ym-sync-agent.py'
RECEIVER = '/opt/yachtmemory/deploy/ym-sync-receiver-0.1.0.php'
VESSEL = '17b19e77-0746-4cd6-9e37-31b2564ebddb'


def ssh_command(action):
    return ['ssh', '-T', '-i', KEY, '-o', 'BatchMode=yes', '-o', 'StrictHostKeyChecking=yes',
            '-o', 'UserKnownHostsFile=' + KNOWN_HOSTS, '-o', 'ConnectTimeout=15',
            '-o', 'ServerAliveInterval=20', '-o', 'ServerAliveCountMax=3', REMOTE,
            'sudo -n -u ymprocessor /usr/bin/python3 ' + AGENT + ' ' + action]


def execute(command, data=None):
    result = subprocess.run(command, input=data, text=True, capture_output=True, timeout=120)
    if result.returncode:
        raise RuntimeError(f"Command failed (exit {result.returncode}): {result.stderr.strip()}")
    return result.stdout


def main():
    p = argparse.ArgumentParser()
    p.add_argument('--dry-run', action='store_true')
    p.add_argument('--run', action='store_true')
    args = p.parse_args()
    if args.dry_run == args.run:
        p.error('choose exactly --dry-run or --run')
    if os.geteuid() != 0:
        raise PermissionError('run as root via sudo (receiver uses local MySQL root socket)')
    lines = execute(ssh_command('export')).splitlines()
    requests = []
    seen = set()
    for line in lines:
        req = json.loads(line)
        if not isinstance(req, dict) or req.get('vessel_id') != VESSEL:
            raise ValueError('unexpected vessel')
        seq = req.get('seq_no')
        if type(seq) is not int or seq in seen:
            raise ValueError('invalid or duplicate sequence')
        seen.add(seq)
        payload = req.get('payload_json')
        digest = req.get('content_hash')
        if not isinstance(payload, str) or not isinstance(digest, str) or hashlib.sha256(payload.encode('utf-8')).hexdigest() != digest:
            raise ValueError(f'invalid payload hash for seq={seq}')
        requests.append(req)
    print('Open entries:', len(requests), flush=True)
    if args.dry_run:
        for r in requests:
            metric = json.loads(r['payload_json']).get('metric_key')
            print(f"seq={r['seq_no']} metric={metric} hash={r['content_hash']}", flush=True)
        return
    acknowledgements = []
    for r in requests:
        reply = json.loads(execute(['php', RECEIVER], json.dumps(r, separators=(',', ':'))))
        if reply.get('ok') is not True or reply.get('seq_no') != r['seq_no'] or reply.get('status') not in ('inserted', 'updated', 'already_received'):
            raise ValueError(f"unexpected receiver reply for seq={r['seq_no']}: {reply}")
        print(f"seq={r['seq_no']}: {reply['status']}", flush=True)
        acknowledgements.append({'vessel_id': VESSEL, 'seq_no': r['seq_no'], 'content_hash': r['content_hash']})
    if acknowledgements:
        result = json.loads(execute(ssh_command('ack'), json.dumps(acknowledgements, separators=(',', ':'))))
        if result.get('ok') is not True or result.get('acknowledged', 0) + result.get('already_acknowledged', 0) != len(acknowledgements):
            raise ValueError(f'ack failed: {result}')
        print('Onboard ACK:', result, flush=True)
    print('SYNC OK', flush=True)


if __name__ == '__main__':
    try:
        main()
    except Exception as exc:
        print('SYNC CONTROLLER ERROR:', exc, file=sys.stderr, flush=True)
        sys.exit(1)
