"""Backend API tests for Recipe Crave."""
import os
import pytest
import requests
import uuid

BASE_URL = os.environ.get("REACT_APP_BACKEND_URL", "").rstrip("/")
if not BASE_URL:
    # Fallback: read frontend/.env
    with open("/app/frontend/.env") as f:
        for line in f:
            if line.startswith("REACT_APP_BACKEND_URL="):
                BASE_URL = line.split("=", 1)[1].strip().rstrip("/")

API = f"{BASE_URL}/api"

ADMIN_EMAIL = "jessethomas1155@gmail.com"
ADMIN_PASSWORD = "RecipeCrave2026!"


@pytest.fixture(scope="session")
def anon():
    s = requests.Session()
    s.headers.update({"Content-Type": "application/json"})
    return s


@pytest.fixture(scope="session")
def admin():
    s = requests.Session()
    s.headers.update({"Content-Type": "application/json"})
    r = s.post(f"{API}/auth/login", json={"email": ADMIN_EMAIL, "password": ADMIN_PASSWORD})
    assert r.status_code == 200, f"Admin login failed: {r.status_code} {r.text}"
    return s


# ---------- Public endpoints ----------
class TestPublic:
    def test_root(self, anon):
        r = anon.get(f"{API}/")
        assert r.status_code == 200

    def test_recipes_list(self, anon):
        r = anon.get(f"{API}/recipes")
        assert r.status_code == 200
        data = r.json()
        assert isinstance(data, list) or isinstance(data, dict)
        # Support either list or paginated response
        items = data if isinstance(data, list) else data.get("items") or data.get("recipes") or []
        assert len(items) >= 1, "Expected seeded recipes"

    def test_categories(self, anon):
        r = anon.get(f"{API}/categories")
        assert r.status_code == 200
        data = r.json()
        assert isinstance(data, list) and len(data) >= 1
        assert "slug" in data[0]

    def test_collections(self, anon):
        r = anon.get(f"{API}/collections")
        assert r.status_code == 200
        assert isinstance(r.json(), list) and len(r.json()) >= 1

    def test_authors(self, anon):
        r = anon.get(f"{API}/authors")
        assert r.status_code == 200
        data = r.json()
        assert isinstance(data, list) and len(data) >= 1
        # Jesse Thomas seeded
        slugs = [a.get("slug") for a in data]
        assert "jesse-thomas" in slugs

    def test_recipe_by_slug(self, anon):
        # Get any recipe first
        r = anon.get(f"{API}/recipes")
        items = r.json() if isinstance(r.json(), list) else r.json().get("items", [])
        assert items, "No recipes to test slug endpoint"
        slug = items[0]["slug"]
        r2 = anon.get(f"{API}/recipes/slug/{slug}")
        assert r2.status_code == 200
        data = r2.json()
        assert data["slug"] == slug
        assert "_id" not in data, "Mongo _id should not be exposed"

    def test_recipe_slug_not_found(self, anon):
        r = anon.get(f"{API}/recipes/slug/nonexistent-slug-xyz-12345")
        assert r.status_code == 404

    def test_category_by_slug(self, anon):
        r = anon.get(f"{API}/categories")
        slug = r.json()[0]["slug"]
        r2 = anon.get(f"{API}/categories/{slug}")
        assert r2.status_code == 200


# ---------- Sitemap / robots ----------
class TestSitemap:
    def test_sitemap_xml_base(self, anon):
        r = anon.get(f"{BASE_URL}/sitemap.xml")
        assert r.status_code == 200
        assert "<urlset" in r.text or "<sitemapindex" in r.text

    def test_sitemap_api_backup(self, anon):
        r = anon.get(f"{API}/sitemap.xml")
        assert r.status_code == 200

    def test_robots(self, anon):
        r = anon.get(f"{BASE_URL}/robots.txt")
        assert r.status_code == 200
        assert "Sitemap:" in r.text


# ---------- Auth ----------
class TestAuth:
    def test_login_success_sets_cookies(self, anon):
        s = requests.Session()
        r = s.post(f"{API}/auth/login", json={"email": ADMIN_EMAIL, "password": ADMIN_PASSWORD})
        assert r.status_code == 200
        # httpOnly cookies
        cookie_names = {c.name for c in s.cookies}
        assert "access_token" in cookie_names, f"Missing access_token cookie. Got {cookie_names}"

    def test_login_invalid(self, anon):
        r = requests.post(f"{API}/auth/login", json={"email": ADMIN_EMAIL, "password": "wrong"})
        assert r.status_code in (400, 401, 403)

    def test_me_requires_auth(self, anon):
        r = requests.get(f"{API}/auth/me")
        assert r.status_code in (401, 403)

    def test_me_with_auth(self, admin):
        r = admin.get(f"{API}/auth/me")
        assert r.status_code == 200
        data = r.json()
        assert data.get("email") == ADMIN_EMAIL


# ---------- Protected ----------
class TestProtected:
    def test_create_recipe_unauthed(self, anon):
        r = requests.post(f"{API}/recipes", json={"title": "Test"})
        assert r.status_code in (401, 403)

    def test_admin_stats(self, admin):
        r = admin.get(f"{API}/admin/stats")
        assert r.status_code == 200
        data = r.json()
        # Common keys
        for k in ("recipes", "categories", "collections"):
            assert k in data or any(k in str(x).lower() for x in data.keys())


# ---------- Contact ----------
class TestContact:
    def test_contact_submit(self, anon):
        payload = {
            "name": "TEST_User",
            "email": "test@example.com",
            "reason": "general",
            "message": "This is a test message for contact form.",
        }
        r = anon.post(f"{API}/contact", json=payload)
        assert r.status_code in (200, 201)


# ---------- Recipe CRUD ----------
class TestRecipeCRUD:
    def test_create_update_delete_recipe(self, admin):
        title = f"TEST_Recipe_{uuid.uuid4().hex[:6]}"
        payload = {
            "title": title,
            "shortDescription": "A test recipe",
            "description": "Testing recipe creation",
            "status": "draft",
            "ingredients": [{"name": "Flour", "quantity": "1", "unit": "cup"}],
            "steps": [{"description": "Mix"}],
            "prepTime": "5 min",
            "cookTime": "10 min",
            "servings": "2",
        }
        r = admin.post(f"{API}/recipes", json=payload)
        assert r.status_code in (200, 201), f"Create failed: {r.status_code} {r.text}"
        data = r.json()
        rid = data.get("id") or data.get("_id")
        assert rid, f"No id in create response: {data}"
        assert "_id" not in data

        # Update
        r2 = admin.put(f"{API}/recipes/{rid}", json={**payload, "status": "published"})
        assert r2.status_code == 200

        # Verify published visible via slug
        slug = data.get("slug") or r2.json().get("slug")
        if slug:
            r3 = requests.get(f"{API}/recipes/slug/{slug}")
            assert r3.status_code == 200

        # Delete
        r4 = admin.delete(f"{API}/recipes/{rid}")
        assert r4.status_code in (200, 204)


# ---------- Categories CRUD ----------
class TestCategoryCRUD:
    def test_category_crud(self, admin):
        name = f"TEST_Cat_{uuid.uuid4().hex[:6]}"
        r = admin.post(f"{API}/categories", json={"name": name, "slug": name.lower(), "description": "t"})
        assert r.status_code in (200, 201), f"{r.status_code} {r.text}"
        cid = r.json().get("id") or r.json().get("_id")
        assert cid
        r2 = admin.put(f"{API}/categories/{cid}", json={"name": name + "u", "slug": name.lower(), "description": "t"})
        assert r2.status_code == 200
        r3 = admin.delete(f"{API}/categories/{cid}")
        assert r3.status_code in (200, 204)
