#!/usr/bin/env python3
"""Minimal DiscoRL CartPole integration test.

This is a minimal script that demonstrates that DiscoRL can:
1. Load CartPole environment
2. Create an agent
3. Collect trajectories
4. Perform training steps

This serves as a proof-of-concept for integrating DiscoRL with other Gym environments.
"""

import os
import sys

# Set JAX to CPU
os.environ['JAX_PLATFORMS'] = 'cpu'

# Add paths
repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(repo_root, 'disco_rl'))

print('='*70)
print('DiscoRL ↔ Gym Integration: CartPole Proof-of-Concept')
print('='*70)

print('\n[1/4] Importing modules...')
try:
    import numpy as np
    import jax
    import jax.numpy as jnp
    from disco_rl import agent as disco_agent
    from disco_cartpole_env import DiscoCartPoleEnv
    print('  ✓ All imports successful')
except Exception as e:
    print(f'  ✗ Import failed: {e}')
    sys.exit(1)

print('\n[2/4] Creating DiscoRL environment adapter...')
try:
    env = DiscoCartPoleEnv(batch_size=2, max_steps=500)
    obs_spec = env.single_observation_spec()
    act_spec = env.single_action_spec()
    print(f'  ✓ Environment created')
    print(f'    Observation spec: {obs_spec}')
    print(f'    Action spec: {act_spec}')
except Exception as e:
    print(f'  ✗ Environment creation failed: {e}')
    sys.exit(1)

print('\n[3/4] Initializing DiscoRL agent...')
try:
    agent_settings = disco_agent.get_settings_disco()
    agent = disco_agent.Agent(
        single_observation_spec=obs_spec,
        single_action_spec=act_spec,
        agent_settings=agent_settings,
        batch_axis_name=None,
    )
    
    rng = jax.random.PRNGKey(42)
    rng, subkey = jax.random.split(rng)
    learner_state = agent.initial_learner_state(subkey)
    
    rng, subkey = jax.random.split(rng)
    actor_state = agent.initial_actor_state(subkey)
    
    rng, subkey = jax.random.split(rng)
    update_rule_params, _ = agent.update_rule.init_params(subkey)
    
    print(f'  ✓ Agent initialized')
    print(f'    Learner params shape: {jax.tree.map(lambda x: x.shape, learner_state.params)}')
except Exception as e:
    print(f'  ✗ Agent initialization failed: {e}')
    import traceback
    traceback.print_exc()
    sys.exit(1)

print('\n[4/4] Collecting trajectory and performing learner step...')
try:
    # Reset environment
    env_state, timestep = env.reset(rng_key=subkey)
    print(f'  ✓ Environment reset')
    
    # Collect 8 timesteps
    trajectory_length = 8
    observations = []
    actions = []
    rewards = []
    discounts = []
    agent_outs_list = []
    logits_list = []
    
    for t in range(trajectory_length):
        rng, subkey = jax.random.split(rng)
        
        # Agent step
        actor_timestep, actor_state = agent.actor_step(
            learner_state.params,
            subkey,
            timestep,
            actor_state,
        )
        
        # Record
        observations.append(timestep.observation['observation'])
        actions.append(actor_timestep.actions)
        agent_outs_list.append(actor_timestep.agent_outs)
        logits_list.append(actor_timestep.logits)
        
        # Env step
        rng, subkey = jax.random.split(rng)
        env_state, timestep = env.step(env_state, actor_timestep.actions)
        
        rewards.append(timestep.reward)
        discounts.append(1.0 - (timestep.step_type == 1).astype(jnp.float32))
    
    # Stack trajectory
    observations = jnp.stack(observations, axis=0)
    actions = jnp.stack(actions, axis=0)
    rewards = jnp.stack(rewards, axis=0)
    discounts = jnp.stack(discounts, axis=0)
    agent_outs_stacked = jax.tree.map(
        lambda *xs: jnp.stack(xs, axis=0),
        *agent_outs_list,
    )
    logits = jnp.stack(logits_list, axis=0)
    
    print(f'  ✓ Trajectory collected ({trajectory_length} steps)')
    print(f'    Observations shape: {observations.shape}')
    print(f'    Actions shape: {actions.shape}')
    print(f'    Rewards: mean={jnp.mean(rewards):.2f}, range=[{jnp.min(rewards):.2f}, {jnp.max(rewards):.2f}]')
    
    # Create rollout
    from disco_rl import types
    rollout = types.ActorRollout(
        observations=observations,
        actions=actions,
        rewards=rewards,
        discounts=discounts,
        agent_outs=agent_outs_stacked,
        logits=logits,
        states=actor_state,
    )
    
    print(f'  ✓ Rollout created')
    
    # Learner step
    rng, subkey = jax.random.split(rng)
    new_learner_state, new_actor_state, log_dict = agent.learner_step(
        rng=subkey,
        rollout=rollout,
        learner_state=learner_state,
        agent_net_state=actor_state,
        update_rule_params=update_rule_params,
        is_meta_training=False,
    )
    
    print(f'  ✓ Learner step completed')
    print(f'    Loss: {log_dict.get("total_loss", "N/A")}')
    
except Exception as e:
    print(f'  ✗ Trajectory collection or learner step failed: {e}')
    import traceback
    traceback.print_exc()
    sys.exit(1)

print('\n' + '='*70)
print('✓ Success! DiscoRL ↔ Gym integration works!')
print('='*70)
print(f'''
Integration achieved:
  • Gym CartPole environment wrapped for DiscoRL
  • Agent can collect trajectories from Gym environments
  • Learner can perform parameter updates using Gym trajectories
  
Template for custom environments:
  1. Create environment wrapper (see disco_cartpole_env.py)
     - Implement reset() → (state, types.EnvironmentTimestep)
     - Implement step(state, actions) → (state, types.EnvironmentTimestep)
  2. Use DiscoRL agent as shown above
  3. Adapt to your custom environment in gym_env_250326_erase.py

Files created:
  • disco_cartpole_env.py: CartPole ↔ DiscoRL adapter
  • disco_weights.py: Weights loading utilities
  • train_disco_cartpole.py: Training loop example
  • test_disco_setup.py: Comprehensive sanity tests
''')
