"""
Research API Tests with Pytest

⚠️ IMPORTANT: THESE ARE REAL INTEGRATION TESTS ⚠️

These tests run against a REAL running LDR server and perform ACTUAL operations.
They use Puppeteer for authentication and pytest for test execution and validation.
"""

import json
import subprocess
import time
import pytest
import requests
import os as os_module
from pathlib import Path
import tempfile
import os

# Base URL for tests
BASE_URL = os.environ.get("LDR_TEST_BASE_URL", "http://127.0.0.1:5000")
TEST_USERNAME = f"testuser_{int(time.time())}"
TEST_PASSWORD = "testpass123"


class AuthHelper:
    """Helper class to handle Puppeteer authentication"""

    @staticmethod
    def get_auth_cookies():
        """Use Puppeteer to authenticate and get cookies"""
        # Create a temporary file for cookie storage
        cookie_file = tempfile.NamedTemporaryFile(
            mode="w", suffix=".json", delete=False
        )
        cookie_file.close()

        # Run the Node.js auth helper
        auth_script = Path(__file__).parent / "auth_helper.js"
        cmd = [
            "node",
            str(auth_script),
            BASE_URL,
            TEST_USERNAME,
            TEST_PASSWORD,
            cookie_file.name,
        ]

        try:
            result = subprocess.run(
                cmd, capture_output=True, text=True, timeout=120
            )
            if result.returncode != 0:
                raise Exception(f"Auth failed: {result.stderr}")

            # Read cookies from file
            with open(cookie_file.name, "r") as f:
                cookies = json.load(f)

            # Convert to requests format
            cookie_dict = {c["name"]: c["value"] for c in cookies}

            # Extract CSRF token
            csrf_token = None
            for cookie in cookies:
                if cookie["name"] == "csrf_token":
                    csrf_token = cookie["value"]
                    break

            return cookie_dict, csrf_token

        finally:
            # Clean up temp file
            if Path(cookie_file.name).exists():
                os.unlink(cookie_file.name)


@pytest.fixture(scope="session")
def auth_session():
    """Session-wide fixture for authenticated requests"""
    # Skip in CI - requires running server
    if (
        os_module.environ.get("CI") == "true"
        or os_module.environ.get("GITHUB_ACTIONS") == "true"
    ):
        pytest.skip("Skipping API tests in CI - requires running server")
    cookies, csrf_token = AuthHelper.get_auth_cookies()

    session = requests.Session()
    session.cookies.update(cookies)
    session.headers.update(
        {"X-CSRFToken": csrf_token, "Accept": "application/json"}
    )

    yield session, csrf_token

    session.close()


@pytest.mark.skipif(
    os_module.environ.get("CI") == "true"
    or os_module.environ.get("GITHUB_ACTIONS") == "true",
    reason="API integration tests require running server - skipped in CI",
)
class TestResearchAPI:
    """Test suite for research API endpoints"""

    def test_start_research_with_model(self, auth_session):
        """Test starting research with specific model and verify it's actually used"""
        session, csrf_token = auth_session

        # Start research
        research_data = {
            "query": f"Test research with model validation {time.time()}",
            "search_engine": "auto",
            "model": "gemma3n:e2b",
            "model_provider": "OLLAMA",
            "mode": "quick",
            "iterations": 1,
            "questions_per_iteration": 1,
        }

        response = session.post(
            f"{BASE_URL}/api/start_research", json=research_data
        )

        # Basic assertions
        assert response.status_code in [200, 201, 202], (
            f"Failed to start research: {response.text}"
        )

        data = response.json()
        assert "research_id" in data, "No research_id in response"

        research_id = data["research_id"]
        assert len(research_id) == 36, "Invalid research_id format"

        print(f"Started research with ID: {research_id}")

        # Wait for research to start processing
        time.sleep(2)  # allow: unmarked-sleep

        # Check research status and logs
        status_response = session.get(
            f"{BASE_URL}/api/research/{research_id}/status"
        )
        assert status_response.status_code == 200, (
            f"Failed to get status: {status_response.text}"
        )

        status_data = status_response.json()
        assert "status" in status_data
        print(f"Research status: {status_data['status']}")

        # Get logs to verify model is being used
        logs_response = session.get(
            f"{BASE_URL}/api/research/{research_id}/logs"
        )
        if logs_response.status_code == 200:
            logs_data = logs_response.json()
            # Handle both list and dict responses
            if isinstance(logs_data, list):
                logs = logs_data
            else:
                logs = logs_data.get("logs", [])

            # Check if model appears in logs
            model_found = False
            for log in logs:
                if "gemma3n:e2b" in str(log) or "model" in str(log).lower():
                    print(f"Log entry mentioning model: {log}")
                    model_found = True

            # This should fail if model isn't being passed!
            assert model_found, (
                "Model 'gemma3n:e2b' not found in research logs - model parameter may not be passed correctly!"
            )

        # Wait a bit more for research to progress
        time.sleep(5)  # allow: unmarked-sleep

        # Check if research actually produces results
        report_response = session.get(f"{BASE_URL}/api/report/{research_id}")
        if report_response.status_code == 200:
            report_data = report_response.json()

            # Verify report has actual content (not just empty structure)
            assert "title" in report_data or "content" in report_data, (
                "Report has no content"
            )

            # If model isn't being used, report will be minimal/empty
            if "content" in report_data:
                assert len(str(report_data["content"])) > 100, (
                    "Report content is too short - model may not be running"
                )

    def test_research_without_required_fields(self, auth_session):
        """Test validation of required fields"""
        session, csrf_token = auth_session

        invalid_requests = [
            {},  # Empty request
            {"model": "gemma3n:e2b"},  # Missing query
            {"query": "test"},  # Missing model
            {"query": "", "model": "gemma3n:e2b"},  # Empty query
            {"query": "test", "model": ""},  # Empty model
        ]

        for data in invalid_requests:
            response = session.post(f"{BASE_URL}/api/start_research", json=data)

            assert response.status_code in [400, 422], (
                f"Expected validation error for {data}, got {response.status_code}"
            )
            print(f"✓ Correctly rejected invalid request: {data}")

    def test_research_completion_time(self, auth_session):
        """Test that research actually completes within reasonable time"""
        session, csrf_token = auth_session

        # Start research
        research_data = {
            "query": "Quick test for completion time",
            "search_engine": "auto",
            "model": "gemma3n:e2b",
            "model_provider": "OLLAMA",
            "mode": "quick",
            "iterations": 1,
            "questions_per_iteration": 1,
        }

        response = session.post(
            f"{BASE_URL}/api/start_research", json=research_data
        )

        assert response.status_code in [200, 201, 202]
        research_id = response.json()["research_id"]

        # Poll for completion
        start_time = time.time()
        max_wait = 60  # 1 minute max
        completed = False

        while time.time() - start_time < max_wait:
            status_response = session.get(
                f"{BASE_URL}/api/research/{research_id}/status"
            )
            if status_response.status_code == 200:
                status = status_response.json().get("status")
                print(
                    f"Status after {int(time.time() - start_time)}s: {status}"
                )

                if status == "completed":
                    completed = True
                    break
                if status == "failed":
                    # Get logs to understand failure
                    logs_response = session.get(
                        f"{BASE_URL}/api/research/{research_id}/logs"
                    )
                    if logs_response.status_code == 200:
                        print("Research failed. Logs:")
                        for log in logs_response.json().get("logs", [])[
                            -10:
                        ]:  # Last 10 logs
                            print(f"  {log}")
                    pytest.fail("Research failed - check logs above")

            time.sleep(2)  # allow: unmarked-sleep

        assert completed, f"Research did not complete within {max_wait} seconds"
        print(
            f"✓ Research completed in {int(time.time() - start_time)} seconds"
        )


if __name__ == "__main__":
    # Run tests with verbose output
    pytest.main([__file__, "-v", "-s"])
