import pytest
from fastapi import status
from app.database import get_database
from app.repositories.agent_repository import create_agent, get_agent_by_email
from app.utils.security import create_access_token, verify_password

@pytest.fixture
async def setup_admin_and_agent():
    db = get_database()
    admin = await create_agent(
        db,
        email="admin.main@example.com",
        password="AdminPassword123!",
        name="Main Admin",
        role="ADMIN",
        status="ACTIVE"
    )
    agent = await create_agent(
        db,
        email="agent.normal@example.com",
        password="AgentPassword123!",
        name="Normal Agent",
        role="AGENT",
        status="ACTIVE"
    )
    admin_token = create_access_token({"sub": admin["agentId"], "role": "ADMIN"})
    agent_token = create_access_token({"sub": agent["agentId"], "role": "AGENT"})

    return {
        "admin": admin,
        "agent": agent,
        "admin_token": admin_token,
        "agent_token": agent_token
    }

@pytest.mark.anyio
async def test_admin_can_create_agent(client, setup_admin_and_agent):
    headers = {"Authorization": f"Bearer {setup_admin_and_agent['admin_token']}"}
    res = client.post("/api/v1/admin/agents", json={
        "name": "New Trainee",
        "email": "trainee@example.com",
        "password": "TraineePass123!",
        "role": "AGENT",
        "status": "ACTIVE"
    }, headers=headers)

    assert res.status_code == status.HTTP_201_CREATED
    data = res.json()
    assert data["name"] == "New Trainee"
    assert data["email"] == "trainee@example.com"
    assert data["role"] == "AGENT"
    assert "passwordHash" not in data

    # Verify password stored as bcrypt hash in DB
    db = get_database()
    db_agent = await get_agent_by_email(db, "trainee@example.com")
    assert db_agent is not None
    assert verify_password("TraineePass123!", db_agent["passwordHash"]) is True

@pytest.mark.anyio
async def test_agent_cannot_create_agent(client, setup_admin_and_agent):
    headers = {"Authorization": f"Bearer {setup_admin_and_agent['agent_token']}"}
    res = client.post("/api/v1/admin/agents", json={
        "name": "Unauthorized Agent",
        "email": "unauth@example.com",
        "password": "Password123!"
    }, headers=headers)

    assert res.status_code == status.HTTP_403_FORBIDDEN
    assert res.json()["detail"] == "Admin role required for this action"

def test_unauthenticated_cannot_create_agent(client):
    res = client.post("/api/v1/admin/agents", json={
        "name": "Anon Agent",
        "email": "anon@example.com",
        "password": "Password123!"
    })

    assert res.status_code == status.HTTP_401_UNAUTHORIZED

@pytest.mark.anyio
async def test_duplicate_email_returns_409_conflict(client, setup_admin_and_agent):
    headers = {"Authorization": f"Bearer {setup_admin_and_agent['admin_token']}"}
    res = client.post("/api/v1/admin/agents", json={
        "name": "Duplicate Agent",
        "email": "agent.normal@example.com",
        "password": "Password123!"
    }, headers=headers)

    assert res.status_code == status.HTTP_409_CONFLICT
    assert "already exists" in res.json()["detail"]

@pytest.mark.anyio
async def test_admin_can_list_agents(client, setup_admin_and_agent):
    headers = {"Authorization": f"Bearer {setup_admin_and_agent['admin_token']}"}
    res = client.get("/api/v1/admin/agents", headers=headers)

    assert res.status_code == status.HTTP_200_OK
    agents = res.json()
    assert len(agents) >= 2
    for a in agents:
        assert "passwordHash" not in a

@pytest.mark.anyio
async def test_agent_cannot_list_agents(client, setup_admin_and_agent):
    headers = {"Authorization": f"Bearer {setup_admin_and_agent['agent_token']}"}
    res = client.get("/api/v1/admin/agents", headers=headers)

    assert res.status_code == status.HTTP_403_FORBIDDEN

@pytest.mark.anyio
async def test_admin_deactivate_and_reactivate_agent(client, setup_admin_and_agent):
    headers = {"Authorization": f"Bearer {setup_admin_and_agent['admin_token']}"}
    target_id = setup_admin_and_agent["agent"]["agentId"]

    # 1. Deactivate agent
    patch_res = client.patch(f"/api/v1/admin/agents/{target_id}", json={"status": "INACTIVE"}, headers=headers)
    assert patch_res.status_code == status.HTTP_200_OK
    assert patch_res.json()["status"] == "INACTIVE"

    # 2. Verify inactive agent login is rejected with 401
    login_res = client.post("/api/v1/auth/login", json={
        "email": "agent.normal@example.com",
        "password": "AgentPassword123!"
    })
    assert login_res.status_code == status.HTTP_401_UNAUTHORIZED

    # 3. Reactivate agent
    reactivate_res = client.patch(f"/api/v1/admin/agents/{target_id}", json={"status": "ACTIVE"}, headers=headers)
    assert reactivate_res.status_code == status.HTTP_200_OK
    assert reactivate_res.json()["status"] == "ACTIVE"

    # 4. Verify login succeeds after reactivation
    login_res2 = client.post("/api/v1/auth/login", json={
        "email": "agent.normal@example.com",
        "password": "AgentPassword123!"
    })
    assert login_res2.status_code == status.HTTP_200_OK

@pytest.mark.anyio
async def test_cannot_deactivate_last_active_admin(client, setup_admin_and_agent):
    headers = {"Authorization": f"Bearer {setup_admin_and_agent['admin_token']}"}
    admin_id = setup_admin_and_agent["admin"]["agentId"]

    # Attempt to deactivate the only active admin
    res = client.patch(f"/api/v1/admin/agents/{admin_id}", json={"status": "INACTIVE"}, headers=headers)
    assert res.status_code == status.HTTP_409_CONFLICT
    assert "last active admin" in res.json()["detail"]

    # Attempt to demote the only active admin to AGENT
    res2 = client.patch(f"/api/v1/admin/agents/{admin_id}", json={"role": "AGENT"}, headers=headers)
    assert res2.status_code == status.HTTP_409_CONFLICT
    assert "last active admin" in res2.json()["detail"]

@pytest.mark.anyio
async def test_admin_password_reset(client, setup_admin_and_agent):
    headers = {"Authorization": f"Bearer {setup_admin_and_agent['admin_token']}"}
    target_id = setup_admin_and_agent["agent"]["agentId"]

    # Reset password for agent.normal@example.com
    reset_res = client.post(f"/api/v1/admin/agents/{target_id}/reset-password", json={
        "newPassword": "NewBrandPassword999!"
    }, headers=headers)
    assert reset_res.status_code == status.HTTP_200_OK

    # 1. Old password fails
    old_login = client.post("/api/v1/auth/login", json={
        "email": "agent.normal@example.com",
        "password": "AgentPassword123!"
    })
    assert old_login.status_code == status.HTTP_401_UNAUTHORIZED

    # 2. New password succeeds
    new_login = client.post("/api/v1/auth/login", json={
        "email": "agent.normal@example.com",
        "password": "NewBrandPassword999!"
    })
    assert new_login.status_code == status.HTTP_200_OK
