"""Backend tests for Stalker / MAC portal endpoints + admin shared_profiles
support for kind=='stalker'.

Covers:
- POST /api/stalker/handshake
    * MAC validation (rejects bad MAC with 422)
    * Unreachable portal returns 502
- POST /api/stalker/all-channels exists + MAC validation (422)
- POST /api/stalker/vod-categories exists + unreachable -> 502
- PUT /api/admin/settings persists shared_profiles[].kind=='stalker' with
  portal_url + mac_address fields, GET returns them
- shared_profiles cleaning preserves m3u_content for existing m3u entries
"""
import os
import uuid

import pytest
import requests

from conftest import BASE_URL as BASE, ADMIN_USER, ADMIN_PASS

# Localhost port 1 — closed port, will fast-fail with connection refused.
UNREACHABLE_PORTAL = "http://127.0.0.1:1/c/"
GOOD_MAC = "00:1A:79:11:22:33"
BAD_MAC = "ZZ:ZZ:ZZ:ZZ:ZZ:ZZ"


# ---------- fixtures ----------
@pytest.fixture(scope="module")
def admin_token():
    r = requests.post(
        f"{BASE}/api/auth/login",
        json={"username": ADMIN_USER, "password": ADMIN_PASS},
        timeout=10,
    )
    assert r.status_code == 200, f"admin login failed: {r.status_code} {r.text}"
    data = r.json()
    assert data.get("user", {}).get("role") == "admin"
    return data["access_token"]


@pytest.fixture(scope="module")
def auth_headers(admin_token):
    return {"Authorization": f"Bearer {admin_token}", "Content-Type": "application/json"}


# ---------- /api/stalker/handshake ----------
class TestStalkerHandshake:
    def test_handshake_route_exists(self):
        # Empty body -> validation error (422 on FastAPI, 400 on PHP/cPanel),
        # not 404. Accept either so the same suite runs on both backends.
        r = requests.post(f"{BASE}/api/stalker/handshake", json={}, timeout=10)
        assert r.status_code in (400, 422), f"expected 400/422, got {r.status_code}: {r.text}"

    def test_handshake_rejects_bad_mac(self):
        r = requests.post(
            f"{BASE}/api/stalker/handshake",
            json={"portal_url": UNREACHABLE_PORTAL, "mac": BAD_MAC},
            timeout=10,
        )
        assert r.status_code in (400, 422)
        body = r.json()
        # Pydantic v2 validation error shape (FastAPI) OR PHP {detail: "..."}.
        assert "detail" in body
        s = str(body["detail"]).lower()
        assert "mac" in s

    def test_handshake_unreachable_portal_502(self):
        r = requests.post(
            f"{BASE}/api/stalker/handshake",
            json={"portal_url": UNREACHABLE_PORTAL, "mac": GOOD_MAC},
            timeout=30,
        )
        assert r.status_code == 502, f"expected 502, got {r.status_code}: {r.text[:200]}"


# ---------- /api/stalker/all-channels ----------
class TestStalkerAllChannels:
    def test_all_channels_route_exists(self):
        r = requests.post(f"{BASE}/api/stalker/all-channels", json={}, timeout=10)
        assert r.status_code in (400, 422)

    def test_all_channels_bad_mac_422(self):
        r = requests.post(
            f"{BASE}/api/stalker/all-channels",
            json={"portal_url": UNREACHABLE_PORTAL, "mac": "not-a-mac"},
            timeout=10,
        )
        assert r.status_code in (400, 422)


# ---------- /api/stalker/vod-categories ----------
class TestStalkerVodCategories:
    def test_vod_categories_route_exists(self):
        r = requests.post(f"{BASE}/api/stalker/vod-categories", json={}, timeout=10)
        assert r.status_code in (400, 422)

    def test_vod_categories_bad_mac_422(self):
        r = requests.post(
            f"{BASE}/api/stalker/vod-categories",
            json={"portal_url": UNREACHABLE_PORTAL, "mac": BAD_MAC},
            timeout=10,
        )
        assert r.status_code in (400, 422)

    def test_vod_categories_unreachable_502(self):
        r = requests.post(
            f"{BASE}/api/stalker/vod-categories",
            json={"portal_url": UNREACHABLE_PORTAL, "mac": GOOD_MAC},
            timeout=30,
        )
        assert r.status_code == 502


# ---------- Admin settings — shared_profiles kind=='stalker' ----------
class TestAdminStalkerSharedProfile:
    test_stalker_id = f"TEST_stalker_{uuid.uuid4()}"
    test_m3u_id = f"TEST_m3u_{uuid.uuid4()}"

    def test_put_settings_persists_stalker_profile(self, auth_headers):
        payload = {
            "shared_profiles": [
                {
                    "id": self.test_stalker_id,
                    "name": "TEST_Stalker",
                    "kind": "stalker",
                    "portal_url": "http://example.com/c/",
                    "mac_address": "00:1A:79:AA:BB:CC",
                },
                {
                    "id": self.test_m3u_id,
                    "name": "TEST_M3U_alongside",
                    "kind": "m3u",
                    "m3u_url": "",
                    "m3u_content": "#EXTM3U\n#EXTINF:-1,Test\nhttp://example.com/s.ts\n",
                },
            ]
        }
        r = requests.put(
            f"{BASE}/api/admin/settings",
            json=payload,
            headers=auth_headers,
            timeout=15,
        )
        assert r.status_code == 200, r.text
        profs = r.json().get("shared_profiles", [])
        st = [p for p in profs if p.get("id") == self.test_stalker_id]
        assert len(st) == 1
        assert st[0]["kind"] == "stalker"
        assert st[0]["portal_url"] == "http://example.com/c/"
        # MAC should be uppercased on save
        assert st[0]["mac_address"] == "00:1A:79:AA:BB:CC"

        # m3u entry alongside still preserves m3u_content
        m3u = [p for p in profs if p.get("id") == self.test_m3u_id]
        assert len(m3u) == 1
        assert m3u[0]["m3u_content"].startswith("#EXTM3U")

    def test_get_settings_returns_stalker_fields(self, auth_headers):
        r = requests.get(f"{BASE}/api/admin/settings", headers=auth_headers, timeout=10)
        assert r.status_code == 200
        profs = r.json().get("shared_profiles", [])
        st = [p for p in profs if p.get("id") == self.test_stalker_id]
        assert len(st) == 1
        assert "portal_url" in st[0]
        assert "mac_address" in st[0]
        assert st[0]["portal_url"] == "http://example.com/c/"
        assert st[0]["mac_address"] == "00:1A:79:AA:BB:CC"

    def test_cleanup(self, auth_headers):
        r = requests.get(f"{BASE}/api/admin/settings", headers=auth_headers, timeout=10)
        profs = r.json().get("shared_profiles", [])
        kept = [
            p for p in profs
            if not (
                str(p.get("id", "")).startswith("TEST_")
                or str(p.get("name", "")).startswith("TEST_")
            )
        ]
        r2 = requests.put(
            f"{BASE}/api/admin/settings",
            json={"shared_profiles": kept},
            headers=auth_headers,
            timeout=10,
        )
        assert r2.status_code == 200
