import shutil
import pytest
from fastapi.testclient import TestClient
from pathlib import Path

from app.core.config import settings
from app.core.database import Base, engine
from app.main import app
from tests._safety_guards import safe_drop_all, safe_rmtree


@pytest.fixture(autouse=True)
def _setup_db():
    Base.metadata.create_all(engine)
    with TestClient(app):
        pass
    yield
    safe_drop_all(engine, Base.metadata)
    # Clean up project directories
    proj_dir = Path(settings.projects_dir)
    if proj_dir.exists():
        safe_rmtree(proj_dir)


@pytest.fixture()
def client():
    with TestClient(app, raise_server_exceptions=False) as c:
        yield c


def _admin_login(client: TestClient):
    resp = client.post("/api/v1/auth/login", json={"username": "admin", "password": "admin123"})
    assert resp.status_code == 200


def _creator_login(client: TestClient):
    resp = client.post("/api/v1/auth/login", json={"username": "creator", "password": "creator123"})
    assert resp.status_code == 200


def _get_creator_id(client: TestClient) -> str:
    """Login as admin, get the creator user's id, then logout."""
    _admin_login(client)
    resp = client.get("/api/v1/users/")
    users = resp.json()["items"]
    creator = [u for u in users if u["username"] == "creator"][0]
    client.post("/api/v1/auth/logout")
    return creator["id"]


def test_create_project(client: TestClient):
    _admin_login(client)
    resp = client.post("/api/v1/projects/", json={"name": "Test Project", "description": "A test"})
    assert resp.status_code == 200
    data = resp.json()
    assert data["name"] == "Test Project"
    assert data["status"] == "active"


def test_list_projects_as_admin(client: TestClient):
    _admin_login(client)
    client.post("/api/v1/projects/", json={"name": "Proj1"})
    resp = client.get("/api/v1/projects/")
    assert resp.status_code == 200
    assert resp.json()["total"] >= 1


def test_list_projects_as_creator(client: TestClient):
    # Admin creates a project (creator won't be a member)
    _admin_login(client)
    client.post("/api/v1/projects/", json={"name": "Admin Only Proj"})
    client.post("/api/v1/auth/logout")

    # Creator creates their own project
    _creator_login(client)
    client.post("/api/v1/projects/", json={"name": "Creator Proj"})
    resp = client.get("/api/v1/projects/")
    assert resp.status_code == 200
    names = [p["name"] for p in resp.json()["items"]]
    assert "Creator Proj" in names
    assert "Admin Only Proj" not in names


def test_get_project(client: TestClient):
    _admin_login(client)
    create_resp = client.post("/api/v1/projects/", json={"name": "Get Me"})
    assert create_resp.status_code == 200
    project_id = create_resp.json()["id"]
    resp = client.get(f"/api/v1/projects/{project_id}")
    assert resp.status_code == 200
    assert resp.json()["name"] == "Get Me"
    assert "member_count" in resp.json()


def test_update_project(client: TestClient):
    _admin_login(client)
    create_resp = client.post("/api/v1/projects/", json={"name": "Old Name"})
    assert create_resp.status_code == 200
    project_id = create_resp.json()["id"]
    resp = client.patch(f"/api/v1/projects/{project_id}", json={"name": "New Name"})
    assert resp.status_code == 200
    assert resp.json()["name"] == "New Name"


def test_delete_project(client: TestClient):
    _admin_login(client)
    create_resp = client.post("/api/v1/projects/", json={"name": "Delete Me"})
    assert create_resp.status_code == 200
    project_id = create_resp.json()["id"]
    resp = client.delete(f"/api/v1/projects/{project_id}")
    assert resp.status_code == 200
    assert resp.json()["status"] == "deleted"


def test_add_member(client: TestClient):
    creator_id = _get_creator_id(client)
    _admin_login(client)
    create_resp = client.post("/api/v1/projects/", json={"name": "Member Proj"})
    assert create_resp.status_code == 200
    project_id = create_resp.json()["id"]
    resp = client.post(f"/api/v1/projects/{project_id}/members", json={
        "user_id": creator_id,
        "role": "member",
    })
    assert resp.status_code == 200
    assert resp.json()["user_id"] == creator_id


def test_remove_member(client: TestClient):
    creator_id = _get_creator_id(client)
    _admin_login(client)
    create_resp = client.post("/api/v1/projects/", json={"name": "Remove Proj"})
    assert create_resp.status_code == 200
    project_id = create_resp.json()["id"]
    client.post(f"/api/v1/projects/{project_id}/members", json={
        "user_id": creator_id,
        "role": "member",
    })
    resp = client.delete(f"/api/v1/projects/{project_id}/members/{creator_id}")
    assert resp.status_code == 200


def test_cannot_remove_owner(client: TestClient):
    _admin_login(client)
    create_resp = client.post("/api/v1/projects/", json={"name": "Owner Proj"})
    assert create_resp.status_code == 200
    project_id = create_resp.json()["id"]
    me_resp = client.get("/api/v1/auth/me")
    admin_id = me_resp.json()["id"]
    resp = client.delete(f"/api/v1/projects/{project_id}/members/{admin_id}")
    assert resp.status_code == 400


def test_member_role_change(client: TestClient):
    creator_id = _get_creator_id(client)
    _admin_login(client)
    create_resp = client.post("/api/v1/projects/", json={"name": "Role Proj"})
    assert create_resp.status_code == 200
    project_id = create_resp.json()["id"]
    client.post(f"/api/v1/projects/{project_id}/members", json={
        "user_id": creator_id,
        "role": "member",
    })
    resp = client.patch(f"/api/v1/projects/{project_id}/members/{creator_id}", json={
        "role": "owner",
    })
    assert resp.status_code == 200
    assert resp.json()["role"] == "owner"


def test_project_activities(client: TestClient):
    _admin_login(client)
    create_resp = client.post("/api/v1/projects/", json={"name": "Activity Proj"})
    assert create_resp.status_code == 200
    project_id = create_resp.json()["id"]
    resp = client.get(f"/api/v1/projects/{project_id}/activities")
    assert resp.status_code == 200
    assert resp.json()["total"] >= 1


def test_access_denied(client: TestClient):
    _admin_login(client)
    create_resp = client.post("/api/v1/projects/", json={"name": "Private Proj"})
    assert create_resp.status_code == 200
    project_id = create_resp.json()["id"]
    client.post("/api/v1/auth/logout")

    _creator_login(client)
    resp = client.get(f"/api/v1/projects/{project_id}")
    assert resp.status_code == 403
