"""
Tests for laneswap Flask integration.

This module tests the Flask framework integration with real Flask instances.
"""

import json
from typing import Any, Dict
from unittest.mock import patch

import pytest

flask = pytest.importorskip("flask")
from flask import Flask

from laneswap import LaneSwap
from laneswap.integrations.flask import setup_flask_integration


class TestFlaskIntegration:
    """Test Flask integration functionality."""

    def test_setup_flask_integration_registers_health_endpoint(
        self, clean_env: None
    ) -> None:
        """Test that setup_flask_integration registers /health endpoint."""
        app = Flask(__name__)
        ls = LaneSwap(setup_signals=False, setup_logs=False)

        setup_flask_integration(app, ls)

        # Check that /health route exists
        with app.test_client() as client:
            response = client.get("/health")
            assert response.status_code == 200

    def test_setup_flask_integration_raises_on_non_flask_app(
        self, clean_env: None
    ) -> None:
        """Test that TypeError is raised for non-Flask apps."""
        not_flask = object()
        ls = LaneSwap(setup_signals=False, setup_logs=False)

        with pytest.raises(TypeError) as exc_info:
            setup_flask_integration(not_flask, ls)  # type: ignore

        assert "Expected Flask app" in str(exc_info.value)

    def test_health_endpoint_returns_json(self, clean_env: None) -> None:
        """Test that /health endpoint returns JSON response."""
        app = Flask(__name__)
        ls = LaneSwap(
            service_name="flask-test",
            setup_signals=False,
            setup_logs=False,
        )

        setup_flask_integration(app, ls)

        with app.test_client() as client:
            response = client.get("/health")
            assert response.content_type == "application/json"

    def test_health_endpoint_returns_correct_structure(
        self, clean_env: None, mock_time: Any
    ) -> None:
        """Test that /health endpoint returns correctly structured response."""
        app = Flask(__name__)
        ls = LaneSwap(
            service_name="flask-test",
            setup_signals=False,
            setup_logs=False,
        )

        setup_flask_integration(app, ls)

        with app.test_client() as client:
            response = client.get("/health")
            data = json.loads(response.data)

            assert data["status"] == "healthy"
            assert data["service"] == "flask-test"
            assert "timestamp" in data

    def test_health_endpoint_includes_custom_checks(
        self, clean_env: None
    ) -> None:
        """Test that /health endpoint includes custom health check results."""
        app = Flask(__name__)
        ls = LaneSwap(setup_signals=False, setup_logs=False)

        @ls.health_check
        def check_database() -> Dict[str, str]:
            return {"database": "connected"}

        @ls.health_check
        def check_cache() -> Dict[str, str]:
            return {"cache": "ready"}

        setup_flask_integration(app, ls)

        with app.test_client() as client:
            response = client.get("/health")
            data = json.loads(response.data)

            assert data["database"] == "connected"
            assert data["cache"] == "ready"

    def test_health_endpoint_handles_failing_health_checks(
        self, clean_env: None
    ) -> None:
        """Test that /health endpoint returns 503 when health checks fail."""
        app = Flask(__name__)
        ls = LaneSwap(setup_signals=False, setup_logs=False)

        @ls.health_check
        def failing_check() -> Dict[str, str]:
            raise RuntimeError("Database connection failed")

        setup_flask_integration(app, ls)

        with app.test_client() as client:
            response = client.get("/health")
            data = json.loads(response.data)

            assert response.status_code == 503
            assert data["status"] == "unhealthy"
            assert "error" in data

    def test_health_endpoint_handles_empty_checks(
        self, clean_env: None
    ) -> None:
        """Test /health endpoint works with no custom health checks."""
        app = Flask(__name__)
        ls = LaneSwap(
            service_name="simple-service",
            setup_signals=False,
            setup_logs=False,
        )

        setup_flask_integration(app, ls)

        with app.test_client() as client:
            response = client.get("/health")
            data = json.loads(response.data)

            assert response.status_code == 200
            assert data["status"] == "healthy"
            assert data["service"] == "simple-service"

    def test_health_endpoint_only_accepts_get(self, clean_env: None) -> None:
        """Test that /health endpoint only accepts GET requests."""
        app = Flask(__name__)
        ls = LaneSwap(setup_signals=False, setup_logs=False)

        setup_flask_integration(app, ls)

        with app.test_client() as client:
            # GET should work
            response = client.get("/health")
            assert response.status_code == 200

            # POST should fail
            response = client.post("/health")
            assert response.status_code == 405

            # PUT should fail
            response = client.put("/health")
            assert response.status_code == 405

            # DELETE should fail
            response = client.delete("/health")
            assert response.status_code == 405

    def test_laneswap_auto_integration_with_flask(
        self, clean_env: None
    ) -> None:
        """Test that LaneSwap automatically integrates with Flask app."""
        app = Flask(__name__)
        ls = LaneSwap(app=app, setup_signals=False, setup_logs=False)

        # /health endpoint should be automatically registered
        with app.test_client() as client:
            response = client.get("/health")
            assert response.status_code == 200

    def test_multiple_health_checks_merge_results(
        self, clean_env: None
    ) -> None:
        """Test that multiple health checks merge their results."""
        app = Flask(__name__)
        ls = LaneSwap(setup_signals=False, setup_logs=False)

        @ls.health_check
        def check1() -> Dict[str, str]:
            return {"component1": "ok", "shared_key": "value1"}

        @ls.health_check
        def check2() -> Dict[str, str]:
            return {"component2": "ok"}

        setup_flask_integration(app, ls)

        with app.test_client() as client:
            response = client.get("/health")
            data = json.loads(response.data)

            assert data["component1"] == "ok"
            assert data["component2"] == "ok"
            # Last check wins for overlapping keys
            assert "shared_key" in data

    def test_health_check_with_none_return(self, clean_env: None) -> None:
        """Test health check that returns None doesn't break endpoint."""
        app = Flask(__name__)
        ls = LaneSwap(setup_signals=False, setup_logs=False)

        @ls.health_check
        def check_returns_none() -> None:
            return None

        @ls.health_check
        def check_returns_data() -> Dict[str, str]:
            return {"data": "present"}

        setup_flask_integration(app, ls)

        with app.test_client() as client:
            response = client.get("/health")
            data = json.loads(response.data)

            assert response.status_code == 200
            assert data["data"] == "present"

    def test_teardown_handler_registered(self, clean_env: None) -> None:
        """Test that teardown handler is registered with Flask."""
        app = Flask(__name__)
        ls = LaneSwap(setup_signals=False, setup_logs=False)

        setup_flask_integration(app, ls)

        # Check that teardown handler was registered
        assert len(app.teardown_appcontext_funcs) > 0

    def test_flask_integration_with_custom_service_name(
        self, clean_env: None
    ) -> None:
        """Test Flask integration with custom service name."""
        app = Flask(__name__)
        ls = LaneSwap(
            service_name="my-custom-service",
            setup_signals=False,
            setup_logs=False,
        )

        setup_flask_integration(app, ls)

        with app.test_client() as client:
            response = client.get("/health")
            data = json.loads(response.data)

            assert data["service"] == "my-custom-service"

    def test_flask_integration_preserves_existing_routes(
        self, clean_env: None
    ) -> None:
        """Test that Flask integration doesn't interfere with existing routes."""
        app = Flask(__name__)

        @app.route("/")
        def index() -> str:
            return "Hello World"

        @app.route("/api/data")
        def api_data() -> str:
            return "API Data"

        ls = LaneSwap(setup_signals=False, setup_logs=False)
        setup_flask_integration(app, ls)

        with app.test_client() as client:
            # Existing routes should still work
            response = client.get("/")
            assert response.status_code == 200
            assert b"Hello World" in response.data

            response = client.get("/api/data")
            assert response.status_code == 200
            assert b"API Data" in response.data

            # Health endpoint should also work
            response = client.get("/health")
            assert response.status_code == 200


class TestFlaskIntegrationEdgeCases:
    """Test edge cases in Flask integration."""

    def test_health_check_exception_contains_error_message(
        self, clean_env: None
    ) -> None:
        """Test that exception message is included in error response."""
        app = Flask(__name__)
        ls = LaneSwap(setup_signals=False, setup_logs=False)

        @ls.health_check
        def failing_check() -> Dict[str, str]:
            raise ValueError("Specific error message")

        setup_flask_integration(app, ls)

        with app.test_client() as client:
            response = client.get("/health")
            data = json.loads(response.data)

            assert "Specific error message" in data["error"]

    def test_concurrent_health_check_requests(self, clean_env: None) -> None:
        """Test that health endpoint can handle concurrent requests."""
        app = Flask(__name__)
        ls = LaneSwap(setup_signals=False, setup_logs=False)

        call_count = []

        @ls.health_check
        def check() -> Dict[str, str]:
            call_count.append(1)
            return {"count": len(call_count)}

        setup_flask_integration(app, ls)

        with app.test_client() as client:
            # Make multiple requests
            responses = [client.get("/health") for _ in range(3)]

            # All should succeed
            for response in responses:
                assert response.status_code == 200

    def test_health_endpoint_response_headers(self, clean_env: None) -> None:
        """Test that health endpoint has correct response headers."""
        app = Flask(__name__)
        ls = LaneSwap(setup_signals=False, setup_logs=False)

        setup_flask_integration(app, ls)

        with app.test_client() as client:
            response = client.get("/health")

            assert "Content-Type" in response.headers
            assert "application/json" in response.headers["Content-Type"]
