"""Export vah natrenovanej siete do formatu pre cisto-Python inferenciu.

Torch je len trenovacia zavislost (host); produkcia hra cez rl/pure_net.py,
ktory cita tento subor bez torch/numpy. Vahy sa uladaju ako base64 float32
little-endian (bit-exact kopia checkpointu, ziadna strata presnosti).

Pouzitie:
    py -m rl.export rl/checkpoints/latest.pt rl/weights/neural-bot.json
"""

import argparse
import base64
import json
import os

from rl.encoding import OBS_DIM
from rl.train import load_checkpoint

DEFAULT_WEIGHTS_PATH = os.path.join('rl', 'weights', 'neural-bot.json')


def _pack(tensor) -> dict:
    """Tensor -> {shape, base64 float32 LE}. Cez struct, bez numpy -- tolist()
    vracia presne hodnoty float32, takze zapis je bit-exact."""
    import struct
    data = tensor.detach().to('cpu').contiguous().float()
    flat = data.reshape(-1).tolist()
    return {
        'shape': list(data.shape),
        'data': base64.b64encode(struct.pack(f'<{len(flat)}f', *flat)).decode('ascii'),
    }


def export(checkpoint_path: str, out_path: str) -> dict:
    net = load_checkpoint(checkpoint_path)
    hidden = net.trunk[0].out_features
    payload = {
        'obs_dim': OBS_DIM,
        'hidden': hidden,
        'weights': {
            'trunk0_w': _pack(net.trunk[0].weight),
            'trunk0_b': _pack(net.trunk[0].bias),
            'trunk2_w': _pack(net.trunk[2].weight),
            'trunk2_b': _pack(net.trunk[2].bias),
            'guess_w': _pack(net.guess_head.weight),
            'guess_b': _pack(net.guess_head.bias),
            'play_w': _pack(net.play_head.weight),
            'play_b': _pack(net.play_head.bias),
        },
    }
    os.makedirs(os.path.dirname(out_path), exist_ok=True)
    with open(out_path, 'w') as f:
        json.dump(payload, f)
    return payload


def main():
    parser = argparse.ArgumentParser(description='Export vah pre pure-Python inferenciu')
    parser.add_argument('checkpoint', nargs='?', default='rl/checkpoints/latest.pt')
    parser.add_argument('out', nargs='?', default=DEFAULT_WEIGHTS_PATH)
    args = parser.parse_args()
    payload = export(args.checkpoint, args.out)
    size = os.path.getsize(args.out)
    print(f'Exportovane: {args.checkpoint} (hidden={payload["hidden"]}) '
          f'-> {args.out} ({size / 1024:.0f} kB)')


if __name__ == '__main__':
    main()
