"""Published deterministic generator. Official 256-bit seeds are server-side only.
Example: python3 generator.py --case 0 --seed 0123456789abcdef
"""
import argparse
import hashlib
import json

VERSION = 'crossroads-v2-30'
PUBLIC_COUNT = 15
QUIET_SECONDS = 60
# n, last possible arrival + 1, distribution
RECIPES = [
    (600, 1200, 'balanced'), (1800, 1800, 'waves'),
    (3000, 1800, 'commute'), (4000, 1800, 'bursts'),
    (5000, 1800, 'mixed'), (1000, 1200, 'bursts'),
    (2500, 1800, 'commute'), (5000, 1800, 'balanced'),
    (4200, 1800, 'waves'), (5000, 1800, 'bursts'),
    (3200, 1500, 'mixed'), (5000, 1800, 'commute'),
    (4800, 1800, 'waves'), (4500, 1600, 'mixed'),
    (5000, 1800, 'mixed'),
]

# Both splits cover the same traffic profiles with independent secret seeds.
RECIPES = RECIPES * 2
TASK_COUNT = len(RECIPES)

class Random:
    """SHA-256 counter stream: reproducible independently of Python's random."""
    def __init__(self, seed):
        self.key = bytes.fromhex(seed)
        if not self.key:
            raise ValueError('Seed must contain at least one byte')
        self.counter = 0
        self.pool = []

    def below(self, n):
        if not self.pool:
            digest = hashlib.sha256(self.key + self.counter.to_bytes(8, 'big')).digest()
            self.counter += 1
            self.pool = [int.from_bytes(digest[i:i+8], 'big') for i in range(0, 32, 8)]
        return self.pool.pop() % n


def generate(case, seed):
    n, horizon, mode = RECIPES[case]
    arrival_window = horizon - QUIET_SECONDS
    if arrival_window < 1:
        raise ValueError('T must be at least 61 seconds')
    rng = Random(seed)
    centers = [rng.below(arrival_window) for _ in range(12)]
    bias = rng.below(8)
    arrivals = []
    for _ in range(n):
        t = rng.below(arrival_window)
        lane = rng.below(8)
        if mode == 'waves':
            # Traveling platoons, with lane-dependent time offsets.
            t = min(arrival_window - 1, (t // 90) * 90 + rng.below(25) + (lane % 2) * 9)
        elif mode == 'commute':
            if rng.below(100) < 78:
                lane = (rng.below(4) + (0 if t < arrival_window // 2 else 4))
        elif mode == 'bursts':
            t = max(0, min(arrival_window - 1, centers[rng.below(12)] + rng.below(61) - 30))
        elif mode == 'mixed':
            if rng.below(100) < 55:
                t = max(0, min(arrival_window - 1, centers[rng.below(12)] + rng.below(101) - 50))
            if rng.below(100) < 45:
                lane = (bias + t // 240) % 8
        arrivals.append([t, lane])
    arrivals.sort()  # same-time ordering is ascending lane, then generated order
    return {'version': VERSION, 'N': n, 'T': horizon, 'arrivals': arrivals}


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--case', type=int, choices=range(TASK_COUNT), required=True)
    parser.add_argument('--seed', required=True, help='hexadecimal seed; use 64 hex digits for 256 bits')
    args = parser.parse_args()
    print(json.dumps(generate(args.case, args.seed), separators=(',', ':')))

if __name__ == '__main__':
    main()
