from datetime import timedelta
import pytest
from fastapi import status
from app.database import get_database
from app.repositories.agent_repository import create_agent, get_agent_by_email, get_agent_by_id
from app.utils.security import (
    hash_password,
    verify_password,
    create_access_token,
    decode_access_token
)

@pytest.mark.anyio
async def test_agent_creation_and_password_hashing():
    db = get_database()
    agent = await create_agent(
        db,
        email="agent.test@example.com",
        password="SecretPassword123!",
        name="Test Agent",
        role="AGENT"
    )

    assert agent["agentId"].startswith("agent_")
    assert agent["email"] == "agent.test@example.com"
    assert agent["passwordHash"] != "SecretPassword123!"
    assert verify_password("SecretPassword123!", agent["passwordHash"]) is True
    assert verify_password("WrongPassword", agent["passwordHash"]) is False

@pytest.mark.anyio
async def test_successful_agent_login(client):
    db = get_database()
    await create_agent(
        db,
        email="login.agent@example.com",
        password="ValidPassword123!",
        name="Login Agent",
        role="ADMIN"
    )

    login_res = client.post("/api/v1/auth/login", json={
        "email": "login.agent@example.com",
        "password": "ValidPassword123!"
    })

    assert login_res.status_code == status.HTTP_200_OK
    data = login_res.json()
    assert "accessToken" in data
    assert data["tokenType"] == "bearer"
    assert data["agent"]["email"] == "login.agent@example.com"
    assert data["agent"]["role"] == "ADMIN"
    assert "passwordHash" not in data["agent"]

    # Decode and verify JWT token payload
    payload = decode_access_token(data["accessToken"])
    assert payload is not None
    assert payload["sub"] == data["agent"]["agentId"]
    assert payload["role"] == "ADMIN"

def test_login_invalid_password_returns_401(client):
    client.post("/api/v1/tenant/register", json={"domain": "shop.com", "name": "Store"})
    
    login_res = client.post("/api/v1/auth/login", json={
        "email": "login.agent@example.com",
        "password": "WrongPassword!"
    })

    assert login_res.status_code == status.HTTP_401_UNAUTHORIZED
    assert login_res.json()["detail"] == "Invalid email or password"

def test_login_unknown_email_returns_401(client):
    login_res = client.post("/api/v1/auth/login", json={
        "email": "nonexistent.agent@example.com",
        "password": "Password123!"
    })

    assert login_res.status_code == status.HTTP_401_UNAUTHORIZED
    assert login_res.json()["detail"] == "Invalid email or password"

def test_protected_endpoint_rejects_missing_token(client):
    res = client.get("/api/v1/auth/me")
    assert res.status_code == status.HTTP_401_UNAUTHORIZED

def test_protected_endpoint_rejects_invalid_token(client):
    headers = {"Authorization": "Bearer invalid_garbage_token"}
    res = client.get("/api/v1/auth/me", headers=headers)
    assert res.status_code == status.HTTP_401_UNAUTHORIZED

def test_protected_endpoint_rejects_expired_token(client):
    expired_token = create_access_token(
        data={"sub": "agent_test_999", "role": "AGENT"},
        expires_delta=timedelta(seconds=-10)
    )
    headers = {"Authorization": f"Bearer {expired_token}"}
    res = client.get("/api/v1/auth/me", headers=headers)
    assert res.status_code == status.HTTP_401_UNAUTHORIZED

@pytest.mark.anyio
async def test_protected_endpoint_accepts_valid_token(client):
    db = get_database()
    agent = await create_agent(
        db,
        email="me.agent@example.com",
        password="MyPassword123!",
        name="Me Agent",
        role="AGENT"
    )

    token = create_access_token({"sub": agent["agentId"], "role": "AGENT"})
    headers = {"Authorization": f"Bearer {token}"}

    res = client.get("/api/v1/auth/me", headers=headers)
    assert res.status_code == status.HTTP_200_OK
    profile = res.json()
    assert profile["agentId"] == agent["agentId"]
    assert profile["email"] == "me.agent@example.com"
    assert profile["role"] == "AGENT"

@pytest.mark.anyio
async def test_authenticated_identity_comes_from_jwt_token(client):
    db = get_database()
    agent_a = await create_agent(db, email="agent.a@example.com", password="Pass123!", name="Agent A")
    agent_b = await create_agent(db, email="agent.b@example.com", password="Pass123!", name="Agent B")

    # Issue token for Agent A
    token_a = create_access_token({"sub": agent_a["agentId"], "role": "AGENT"})
    headers = {"Authorization": f"Bearer {token_a}"}

    # Call /auth/me with Agent A's token
    res = client.get("/api/v1/auth/me", headers=headers)
    assert res.status_code == status.HTTP_200_OK
    assert res.json()["agentId"] == agent_a["agentId"]
    assert res.json()["agentId"] != agent_b["agentId"]

def test_jwt_secret_configuration_validation(monkeypatch):
    from app.config import Settings
    monkeypatch.delenv("PYTEST_CURRENT_TEST", raising=False)
    monkeypatch.delenv("ENV", raising=False)
    monkeypatch.delenv("JWT_SECRET", raising=False)
    
    with pytest.raises(ValueError, match="JWT_SECRET environment variable is missing or empty"):
        Settings(jwt_secret="")
