"""Only one live maker may run at a time.

On 2026-08-16 a relaunch happened before the previous process exited. Two makers
ran against one account, each posted its own orders, and every leg ended up with
two resting. The duplicate guard could not recover: it cancels BOTH orders on a
leg and the other process re-posted immediately, so resting orders climbed
54 -> 81 while the guard fired continuously.

Every other safety in mm_maker_live assumes a single writer -- the duplicate
guard, the resting-over-cap abort, the post budget, the ratchet floors held in
state. This test holds the assumption that makes them valid.

No network, no orders.
"""
import json
import os
import sys
import time

sys.path.insert(0, r'E:\soccer\main')
import mm_maker_live as ML

fails = []


def check(label, cond, extra=''):
    print(f'  [{"pass" if cond else "FAIL"}] {label}{("  " + extra) if extra else ""}')
    if not cond:
        fails.append(label)


saved = ML.LOCK_FILE.read_text(encoding='utf-8') if ML.LOCK_FILE.exists() else None
try:
    if ML.LOCK_FILE.exists():
        ML.LOCK_FILE.unlink()

    print('=== a free lock can be taken ===')
    check('acquire on a clean slate', ML.lock_acquire() is True)
    check('lock file written', ML.LOCK_FILE.exists())
    d = json.loads(ML.LOCK_FILE.read_text(encoding='utf-8'))
    check('records our pid', d.get('pid') == os.getpid(), f'pid {d.get("pid")}')

    print('\n=== a live lock held by ANOTHER pid blocks a second maker ===')
    # Must be a REALLY running pid. The first version used os.getpid()+99999,
    # which stopped working the moment the lock started checking liveness -- a
    # made-up pid is indistinguishable from a dead one, and a dead holder is
    # exactly what must NOT block. The parent process is alive and is not us.
    other = os.getppid()
    ML.LOCK_FILE.write_text(json.dumps({'pid': other, 'heartbeat': time.time()}),
                            encoding='utf-8')
    check('second instance refused while holder lives',
          ML.lock_acquire() is False, f'holder pid {other}')

    print('\n=== a lock held by a DEAD pid does not block ===')
    dead = os.getpid() + 999999
    ML.LOCK_FILE.write_text(json.dumps({'pid': dead, 'heartbeat': time.time()}),
                            encoding='utf-8')
    check('dead holder ignored despite a fresh heartbeat',
          ML.lock_acquire() is True,
          'a force-killed maker never releases its lock')

    print('\n=== our own pid is never blocked by our own lock ===')
    ML.LOCK_FILE.write_text(json.dumps({'pid': os.getpid(),
                                        'heartbeat': time.time()}),
                            encoding='utf-8')
    check('same pid may re-acquire', ML.lock_acquire() is True)

    print('\n=== a STALE lock is taken over, not honoured forever ===')
    ML.LOCK_FILE.write_text(json.dumps(
        {'pid': os.getpid() + 99999,
         'heartbeat': time.time() - ML.LOCK_STALE_S - 5}), encoding='utf-8')
    check('stale lock does not block', ML.lock_acquire() is True,
          f'stale after {ML.LOCK_STALE_S}s')

    print('\n=== heartbeat keeps it fresh, release frees it ===')
    before = json.loads(ML.LOCK_FILE.read_text(encoding='utf-8'))['heartbeat']
    time.sleep(1.1)
    ML.lock_beat()
    after = json.loads(ML.LOCK_FILE.read_text(encoding='utf-8'))['heartbeat']
    check('heartbeat advances', after > before, f'{after-before:.1f}s')
    ML.lock_release()
    check('release removes the lock', not ML.LOCK_FILE.exists())

    print('\n=== a corrupt lock file must not wedge the maker shut ===')
    ML.LOCK_FILE.write_text('not json at all', encoding='utf-8')
    check('garbage lock is ignored', ML.lock_acquire() is True)
    ML.lock_release()

    print('\n=== the stale window covers several poll cycles ===')
    check('stale window > 2 polls', ML.LOCK_STALE_S > 2 * ML.POLL_SECS,
          f'{ML.LOCK_STALE_S}s vs poll {ML.POLL_SECS}s')
finally:
    if ML.LOCK_FILE.exists():
        ML.LOCK_FILE.unlink()
    if saved is not None:
        ML.LOCK_FILE.write_text(saved, encoding='utf-8')

print('\n' + ('ALL PASSED' if not fails else f'FAILURES: {fails}'))
sys.exit(1 if fails else 0)
