# #region TestSupersetIntegration [C:3] [TYPE Module] [SEMANTICS test,superset,integration,smoke]
# @BRIEF Smoke tests verifying the Superset testcontainer starts correctly and APIs respond.
# @RELATION BINDS_TO -> [SupersetClient]
# @RELATION BINDS_TO -> [SupersetClientBase]
#
# @TEST_CONTRACT SupersetHealth ->
#   Health endpoint: GET /health returns 200 with healthy status
#   Login endpoint: GET /login/ returns 200
#   API version: GET /api/v1/ returns 200
#   Dashboard list: GET /api/v1/dashboard/ returns paginated response (authenticated)
#
# @TEST_EDGE: superset_not_healthy -> TimeoutError after 90s
# @TEST_EDGE: unauthenticated_api -> 401 on protected endpoints
# @TEST_EDGE: invalid_login_credentials -> 403/redirect on wrong password
#
# @REQUIRES Docker daemon running (testcontainers starts superset + postgres)
import pytest
import requests


# #region TestSupersetHealthCheck [C:3] [TYPE Class]
# @BRIEF Verify Superset is alive and core APIs respond.
class TestSupersetHealthCheck:
    """Smoke tests for the running Superset test container."""

    # #region test_health_endpoint [C:2] [TYPE Function]
    # @BRIEF GET /health returns 200 with OK — Superset is alive.
    def test_health_endpoint(self, superset_url):
        resp = requests.get(f"{superset_url}/health", timeout=10)
        assert resp.status_code == 200, f"Health check failed: {resp.status_code}"
        # Superset 4.x /health returns "OK" as plain text
        assert resp.text.strip() == "OK", \
            f"Expected 'OK', got '{resp.text.strip()}'"
    # #endregion test_health_endpoint


    # #region test_login_page_reachable [C:2] [TYPE Function]
    # @BRIEF GET /login/ returns 200 — the login page is served.
    def test_login_page_reachable(self, superset_url):
        resp = requests.get(f"{superset_url}/login/", timeout=10, allow_redirects=True)
        assert resp.status_code == 200, f"Login page returned {resp.status_code}"
        # Login page should contain Superset-related content
        assert "Superset" in resp.text or "superset" in resp.text.lower(), \
            "Login page does not contain 'Superset' text"
    # #endregion test_login_page_reachable


    # #region test_api_version_endpoint [C:2] [TYPE Function]
    # @BRIEF GET /api/v1/database/ returns 401 (unauthenticated) — API layer is alive.
    def test_api_version_endpoint(self, superset_url):
        # Superset 4.x doesn't expose /api/v1/ root; use a real endpoint
        resp = requests.get(
            f"{superset_url}/api/v1/database/",
            timeout=10,
            allow_redirects=False,
        )
        # API is reachable — returns 401 without auth, 200 with auth
        assert resp.status_code in (200, 401), \
            f"API returned unexpected {resp.status_code}"
    # #endregion test_api_version_endpoint


    # #region test_unauthenticated_api_rejected [C:2] [TYPE Function]
    # @BRIEF GET /api/v1/dashboard/ without auth returns 401.
    # @TEST_EDGE: unauthenticated_api — VERIFIED_BY: test_unauthenticated_api_rejected
    def test_unauthenticated_api_rejected(self, superset_url):
        resp = requests.get(
            f"{superset_url}/api/v1/dashboard/",
            timeout=10,
            allow_redirects=False,
        )
        # Either 401 Unauthorized or 302 redirect to login
        assert resp.status_code in (401, 302, 403), \
            f"Expected 401/302/403, got {resp.status_code}"
    # #endregion test_unauthenticated_api_rejected


# #endregion TestSupersetHealthCheck


# #region TestSupersetAuthenticated [C:3] [TYPE Class]
# @BRIEF Verify session-based authentication and CSRF token flow.
class TestSupersetAuthenticated:
    """Tests requiring session-based login to Superset."""

    # #region test_form_login_sets_session_cookie [C:2] [TYPE Function]
    # @BRIEF POST /login/ with valid credentials redirects and sets session cookie.
    def test_form_login_sets_session_cookie(self, superset_url, superset_admin_password):
        import re
        session = requests.Session()

        # Step 1: Get login page to obtain CSRF token
        resp = session.get(f"{superset_url}/login/", timeout=10)
        assert resp.status_code in (200, 302), \
            f"GET /login/ returned {resp.status_code}"

        csrf_match = re.search(r'csrf_token["\s:=]+[\'"]([^\'"]+)', resp.text)
        csrf_token = csrf_match.group(1) if csrf_match else ""

        # Step 2: Submit login form
        resp = session.post(
            f"{superset_url}/login/",
            data={
                "username": "admin",
                "password": superset_admin_password,
                "csrf_token": csrf_token,
            },
            headers={"Referer": f"{superset_url}/login/"},
            timeout=10,
            allow_redirects=False,
        )
        # Successful login typically redirects to /superset/welcome/
        assert resp.status_code in (200, 302), \
            f"POST /login/ returned {resp.status_code}"

        # Session cookie should contain user data
        session_cookie = session.cookies.get("session")
        assert session_cookie is not None, \
            "No session cookie set after login"
        assert len(session_cookie) > 20, \
            f"Session cookie too short: {len(session_cookie)} chars"
    # #endregion test_form_login_sets_session_cookie


    # #region test_me_endpoint_after_form_login [C:2] [TYPE Function]
    # @BRIEF GET /api/v1/me/ returns user info after form-based login.
    def test_me_endpoint_after_form_login(self, superset_url, superset_admin_password):
        import re
        session = requests.Session()

        # Login via form
        resp = session.get(f"{superset_url}/login/", timeout=10)
        csrf_match = re.search(r'csrf_token["\s:=]+[\'"]([^\'"]+)', resp.text)
        csrf_token = csrf_match.group(1) if csrf_match else ""

        session.post(
            f"{superset_url}/login/",
            data={
                "username": "admin",
                "password": superset_admin_password,
                "csrf_token": csrf_token,
            },
            headers={"Referer": f"{superset_url}/login/"},
            timeout=10,
        )

        # Access me endpoint — some Superset versions allow session-based access
        resp = session.get(
            f"{superset_url}/api/v1/me/",
            timeout=10,
            allow_redirects=False,
        )
        # May return 200 (session accepted) or 401 (JWT required)
        # Either is valid behavior depending on Superset version
        assert resp.status_code in (200, 401, 302), \
            f"/api/v1/me/ returned unexpected {resp.status_code}"
    # #endregion test_me_endpoint_after_form_login


# #endregion TestSupersetAuthenticated


# #region TestSupersetEnvironment [C:3] [TYPE Class]
# @BRIEF Verify SupersetClient connectivity and JWT authentication.
# @RATIONALE Superset Docker image (4.1.2) does not include psycopg2 — it silently
#   falls back to SQLite. With separate init+web containers, the SQLite DB from the
#   init container is lost, so the web container has no admin user → JWT login returns
#   401. The fix: `python -m pip install psycopg2-binary -q` in both init and web
#   containers. With psycopg2 installed, Superset connects to the shared Postgres DB
#   and JWT login works correctly.
class TestSupersetEnvironment:
    """Verify SupersetClient integration with the test container."""

    # #region test_jwt_login_success [C:2] [TYPE Function]
    # @BRIEF POST /api/v1/security/login returns access_token + refresh_token.
    @pytest.mark.asyncio
    async def test_jwt_login_success(self, superset_url, superset_admin_password):
        import httpx

        async with httpx.AsyncClient() as client:
            resp = await client.post(
                f"{superset_url}/api/v1/security/login",
                json={
                    "username": "admin",
                    "password": superset_admin_password,
                    "provider": "db",
                    "refresh": True,
                },
                timeout=10,
            )
            assert resp.status_code == 200, \
                f"JWT login failed: {resp.status_code} {resp.text[:200]}"

            data = resp.json()
            assert "access_token" in data, \
                f"No access_token in response: {list(data.keys())}"
            assert "refresh_token" in data, \
                f"No refresh_token in response"

            # Tokens should be non-empty JWT strings
            assert len(data["access_token"]) > 50, \
                f"access_token too short: {len(data['access_token'])}"
    # #endregion test_jwt_login_success


    # #region test_jwt_authenticated_api_call [C:2] [TYPE Function]
    # @BRIEF Dashboard API returns 200 with valid JWT Bearer token.
    @pytest.mark.asyncio
    async def test_jwt_authenticated_api_call(self, superset_url, superset_admin_password):
        import httpx

        async with httpx.AsyncClient() as client:
            # Step 1: Get JWT token
            login_resp = await client.post(
                f"{superset_url}/api/v1/security/login",
                json={
                    "username": "admin",
                    "password": superset_admin_password,
                    "provider": "db",
                    "refresh": True,
                },
                timeout=10,
            )
            assert login_resp.status_code == 200
            access_token = login_resp.json()["access_token"]

            # Step 2: Call protected API with Bearer token
            dash_resp = await client.get(
                f"{superset_url}/api/v1/dashboard/?q=(page_size:1,page:0)",
                headers={"Authorization": f"Bearer {access_token}"},
                timeout=10,
            )
            assert dash_resp.status_code == 200, \
                f"Dashboard API failed: {dash_resp.status_code} {dash_resp.text[:200]}"

            data = dash_resp.json()
            assert "count" in data, \
                f"Dashboard response missing count: {list(data.keys())}"
            assert isinstance(data["count"], int)
    # #endregion test_jwt_authenticated_api_call


    # #region test_superset_client_constructs [C:2] [TYPE Function]
    # @BRIEF SupersetClient initializes and constructs API URL correctly.
    @pytest.mark.asyncio
    async def test_superset_client_constructs(self, superset_url):
        from src.core.config_models import Environment
        from src.core.superset_client import SupersetClient

        env = Environment(
            id="test_superset",
            name="Test Superset Container",
            url=superset_url,
            username="admin",
            password="admin123",
            verify_ssl=False,
            timeout=10,
        )

        client = SupersetClient(env)
        assert client.client is not None
        assert client.client.base_url == superset_url, \
            f"Expected {superset_url}, got {client.client.base_url}"
    # #endregion test_superset_client_constructs


    # #region test_superset_api_base_url [C:2] [TYPE Function]
    # @BRIEF SupersetClient computes the correct /api/v1/ base path.
    @pytest.mark.asyncio
    async def test_superset_api_base_url(self, superset_url):
        from src.core.config_models import Environment
        from src.core.superset_client import SupersetClient

        env = Environment(
            id="test_api_url",
            name="Test",
            url=superset_url,
            username="admin",
            password="admin123",
            verify_ssl=False,
            timeout=10,
        )

        client = SupersetClient(env)
        assert client.client.api_base_url is not None
        assert "/api/v1" in client.client.api_base_url, \
            f"api_base_url does not contain /api/v1: {client.client.api_base_url}"
    # #endregion test_superset_api_base_url


# #endregion TestSupersetEnvironment


# #endregion TestSupersetIntegration
