import pytest
import httpx
import os
import uuid
from conftest import BASE_URL

def _headers():
    uid = os.getenv("SYNC_TEST_USER_ID", "")
    return {"X-User-ID": uid}

def test_agent_lifecycle_and_api_coverage():
    """
    Test suite covering Agent API endpoints:
    1. Register Node
    2. Register Template
    3. Register Session
    4. Register Instance
    5. Register Trigger
    6. Verify Agent Periodical Execution via Session Messages
    7. List Agents
    8. Stop Agent
    9. Remove Agent
    """
    node_id = f"test-agent-node-{uuid.uuid4().hex[:8]}"
    admin_id = os.getenv("SYNC_TEST_USER_ID", "")
    
    with httpx.Client(timeout=10.0) as client:
        # 1. Register a test node specifically for this agent testing
        node_payload = {
            "node_id": node_id,
            "display_name": "Agent Test Node",
            "is_active": True,
            "skill_config": {"shell": {"enabled": True}}
        }
        r_node = client.post(f"{BASE_URL}/nodes/admin", params={"admin_id": admin_id}, json=node_payload)
        # If conflicts, clear first
        if r_node.status_code in (400, 409):
            client.delete(f"{BASE_URL}/nodes/admin/{node_id}", params={"admin_id": admin_id})
            r_node = client.post(f"{BASE_URL}/nodes/admin", params={"admin_id": admin_id}, json=node_payload)
        assert r_node.status_code == 200, f"Node registration failed: {r_node.text}"

        # 2. Register a basic Agent Template
        tmpl_payload = {
            "name": "Cron Print Agent",
            "description": "Periodically prints to the node console",
            "system_prompt_path": "You are a cron agent. Run shell tasks periodically.",
            "max_loop_iterations": 1
        }
        r_tmpl = client.post(f"{BASE_URL}/agents/templates", json=tmpl_payload, headers=_headers())
        assert r_tmpl.status_code == 200, f"Template registration failed: {r_tmpl.text}"
        template_id = r_tmpl.json()["id"]

        # 3. Register a Session for the Agent
        session_payload = {
            "title": "Agent Test Session",
            "feature_name": "agent_harness",
            "user_id": admin_id,
            "provider_name": "gemini"
        }
        r_sess = client.post(f"{BASE_URL}/sessions/", json=session_payload, headers=_headers())
        assert r_sess.status_code == 200, f"Session registration failed: {r_sess.text}"
        session_id = r_sess.json()["id"]

        # 4. Register a simple Agent Instance linked to the Session
        inst_payload = {
            "template_id": template_id,
            "mesh_node_id": node_id,
            "session_id": session_id,
            "status": "idle"  # Let the Scheduler wake it up!
        }
        r_inst = client.post(f"{BASE_URL}/agents/instances", json=inst_payload, headers=_headers())
        assert r_inst.status_code == 200, f"Instance registration failed: {r_inst.text}"
        instance_id = r_inst.json()["id"]

        # 5. Choose a basic trigger method (interval) with 5 second wakeup
        trig_payload = {
            "trigger_type": "interval",
            "interval_seconds": 5,
            "default_prompt": "Hello test agent! Just reply the word 'Acknowledged' and nothing else."
        }
        r_trig = client.post(f"{BASE_URL}/agents/{instance_id}/triggers", json=trig_payload, headers=_headers())
        assert r_trig.status_code == 200, f"Trigger logic failed: {r_trig.text}"
        trigger_id = r_trig.json()["id"]
        
        # 6. Verify Agent Periodical Execution
        print("\\n[test] Waiting 15 seconds to allow background interval scheduler to wake the agent...")
        import time
        time.sleep(15)
        
        r_msgs = client.get(f"{BASE_URL}/sessions/{session_id}/messages", headers=_headers())
        assert r_msgs.status_code == 200, f"Failed to fetch session messages: {r_msgs.text}"
        messages = r_msgs.json()["messages"]
        print(f"\\n[test] Agent Messages Count: {len(messages)}")
        assert len(messages) > 0, "The agent failed to generate any response during its execution loop! It was not invoked or crashed silently."
        assert any(m["sender"] == "assistant" for m in messages), "No assistant (agent) messages generated in history!"

        # 7. Test if agent is in the active list
        r_list = client.get(f"{BASE_URL}/agents", headers=_headers())
        assert r_list.status_code == 200
        agents = r_list.json()
        assert any(a["id"] == instance_id for a in agents), "Instance not found in active list"
        
        # Fetch triggers back
        r_trig_get = client.get(f"{BASE_URL}/agents/{instance_id}/triggers", headers=_headers())
        assert r_trig_get.status_code == 200
        assert any(t["id"] == trigger_id for t in r_trig_get.json()), "Trigger not found on instance"
        
        # 8. Test Stop Update / Config Update
        r_stop = client.patch(f"{BASE_URL}/agents/{instance_id}/status", json={"status": "stopped"}, headers=_headers())
        assert r_stop.status_code == 200
        assert r_stop.json()["status"] == "stopped"

        r_cfg = client.patch(f"{BASE_URL}/agents/{instance_id}/config", json={"name": "Updated Cron Agent", "mesh_node_id": node_id}, headers=_headers())
        assert r_cfg.status_code == 200
        
        # 9. Test Remove (delete trigger first, then agent)
        r_del_trig = client.delete(f"{BASE_URL}/agents/triggers/{trigger_id}", headers=_headers())
        assert r_del_trig.status_code == 200

        r_del_agent = client.delete(f"{BASE_URL}/agents/{instance_id}", headers=_headers())
        assert r_del_agent.status_code == 200
        
        # Cleanup Node
        client.delete(f"{BASE_URL}/nodes/admin/{node_id}", params={"admin_id": admin_id})
