"""
Unit tests for FastAPI endpoints
"""
import pytest
from fastapi.testclient import TestClient
from unittest.mock import patch, AsyncMock, Mock
from datetime import datetime

# Import the FastAPI app
from main import app


class TestAPIRoot:
    """Tests for root endpoint"""

    def test_root_endpoint(self):
        """Test root endpoint returns API info"""
        client = TestClient(app)
        response = client.get("/")
        assert response.status_code == 200
        data = response.json()
        assert data["message"] == "Ohm Stream Downloader API"
        assert data["status"] == "running"
        assert "version" in data
        assert "endpoints" in data

    def test_root_endpoint_version(self):
        """Test that API version is present"""
        client = TestClient(app)
        response = client.get("/")
        data = response.json()
        assert data["version"] in ["2.1", "2.2"]


class TestAPIHealth:
    """Tests for health check endpoint"""

    def test_health_check(self):
        """Test health check endpoint"""
        client = TestClient(app)
        response = client.get("/health")
        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "healthy"


class TestAPIProviders:
    """Tests for providers endpoint"""

    def test_providers_list(self):
        """Test getting list of providers"""
        client = TestClient(app)
        response = client.get("/api/providers")
        assert response.status_code == 200
        data = response.json()
        assert "anime_providers" in data
        assert "file_hosts" in data
        assert isinstance(data["anime_providers"], dict)
        assert isinstance(data["file_hosts"], dict)
        # Check for known providers
        assert "anime-sama" in data["anime_providers"]
        assert "neko-sama" in data["anime_providers"]


class TestAPIDownloadCreate:
    """Tests for download creation endpoint"""

    def test_create_download_success(self):
        """Test creating a new download"""
        client = TestClient(app)
        response = client.post(
            "/api/download",
            json={"url": "https://example.com/file.mp4"}
        )
        assert response.status_code == 200
        data = response.json()
        assert "task_id" in data
        # Status is in the task object
        assert "task" in data
        assert data["task"]["status"] == "pending"

    def test_create_download_with_filename(self):
        """Test creating download with custom filename"""
        client = TestClient(app)
        response = client.post(
            "/api/download",
            json={
                "url": "https://example.com/file.mp4",
                "filename": "custom_name.mp4"
            }
        )
        assert response.status_code == 200
        data = response.json()
        assert "task_id" in data

    def test_create_download_invalid_url(self):
        """Test creating download with invalid URL"""
        client = TestClient(app)
        response = client.post(
            "/api/download",
            json={"url": "not-a-valid-url"}
        )
        # API accepts the URL even if invalid (will fail later)
        assert response.status_code == 200
        data = response.json()
        assert "task_id" in data

    def test_create_download_missing_url(self):
        """Test creating download without URL"""
        client = TestClient(app)
        response = client.post("/api/download", json={})
        assert response.status_code == 422


class TestAPIDownloadList:
    """Tests for download list endpoint"""

    def test_list_downloads_empty(self):
        """Test listing downloads when none exist"""
        client = TestClient(app)
        response = client.get("/api/downloads")
        assert response.status_code == 200
        data = response.json()
        assert "downloads" in data
        assert isinstance(data["downloads"], list)
        # May or may not be empty depending on test order

    def test_list_downloads_after_creation(self):
        """Test listing downloads after creating one"""
        client = TestClient(app)
        # Create a download first
        create_response = client.post(
            "/api/download",
            json={"url": "https://example.com/test.mp4"}
        )
        assert create_response.status_code == 200

        # List downloads
        response = client.get("/api/downloads")
        assert response.status_code == 200
        data = response.json()
        assert len(data["downloads"]) >= 1


class TestAPIDownloadDetails:
    """Tests for download details endpoint"""

    def test_get_download_details_success(self):
        """Test getting details of existing download"""
        client = TestClient(app)
        # Create a download first
        create_response = client.post(
            "/api/download",
            json={"url": "https://example.com/details.mp4"}
        )
        task_id = create_response.json()["task_id"]

        # Get details
        response = client.get(f"/api/download/{task_id}")
        assert response.status_code == 200
        data = response.json()
        assert data["id"] == task_id
        assert "url" in data
        assert "status" in data

    def test_get_download_details_not_found(self):
        """Test getting details of non-existent download"""
        client = TestClient(app)
        response = client.get("/api/download/nonexistent-id")
        assert response.status_code == 404


class TestAPIDownloadPause:
    """Tests for pause download endpoint"""

    def test_pause_download_success(self):
        """Test pausing a download"""
        client = TestClient(app)
        # Create a download first
        create_response = client.post(
            "/api/download",
            json={"url": "https://example.com/pause.mp4"}
        )
        task_id = create_response.json()["task_id"]

        # Pause it
        response = client.post(f"/api/download/{task_id}/pause")
        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "paused"

    def test_pause_download_not_found(self):
        """Test pausing non-existent download"""
        client = TestClient(app)
        response = client.post("/api/download/nonexistent-id/pause")
        assert response.status_code == 404


class TestAPIDownloadResume:
    """Tests for resume download endpoint"""

    def test_resume_download_success(self):
        """Test resuming a paused download"""
        client = TestClient(app)
        # Create and pause a download
        create_response = client.post(
            "/api/download",
            json={"url": "https://example.com/resume.mp4"}
        )
        task_id = create_response.json()["task_id"]

        # Pause first
        client.post(f"/api/download/{task_id}/pause")

        # Resume
        response = client.post(f"/api/download/{task_id}/resume")
        assert response.status_code == 200
        data = response.json()
        assert "status" in data
        assert data["status"] in ["resumed", "already running or completed"]


class TestAPIDownloadCancel:
    """Tests for cancel download endpoint"""

    def test_cancel_download_success(self):
        """Test canceling a download"""
        client = TestClient(app)
        # Create a download first
        create_response = client.post(
            "/api/download",
            json={"url": "https://example.com/cancel.mp4"}
        )
        task_id = create_response.json()["task_id"]

        # Cancel it (DELETE marks as deleted)
        response = client.delete(f"/api/download/{task_id}")
        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "deleted"

    def test_cancel_download_not_found(self):
        """Test canceling non-existent download"""
        client = TestClient(app)
        response = client.delete("/api/download/nonexistent-id")
        assert response.status_code == 404


class TestAPIAnimeSearch:
    """Tests for anime search endpoint"""

    def test_anime_search_missing_query(self):
        """Test anime search without query parameter"""
        client = TestClient(app)
        response = client.get("/api/anime/search")
        # Now returns 422 for validation error
        assert response.status_code == 422  # Bad request

    def test_anime_search_with_query(self):
        """Test anime search with query parameter"""
        client = TestClient(app)
        response = client.get("/api/anime/search?q=naruto")
        # This will likely fail without actual network/anime sites
        # but we're testing the endpoint exists
        assert response.status_code in [200, 500, 503]  # Various possibilities

    def test_anime_search_with_language(self):
        """Test anime search with language parameter"""
        client = TestClient(app)
        response = client.get("/api/anime/search?q=one+piece&lang=vostfr")
        # Similar to above, testing endpoint exists
        assert response.status_code in [200, 500, 503]

    def test_anime_search_with_metadata(self):
        """Test anime search with metadata flag"""
        client = TestClient(app)
        response = client.get("/api/anime/search?q=bleach&include_metadata=true")
        # Testing endpoint accepts the parameter
        assert response.status_code in [200, 500, 503]


class TestAPIAnimeMetadata:
    """Tests for anime metadata endpoint"""

    def test_anime_metadata_missing_url(self):
        """Test metadata endpoint without URL"""
        client = TestClient(app)
        response = client.get("/api/anime/metadata")
        # Returns 422 for validation error
        assert response.status_code == 422

    def test_anime_metadata_with_url(self):
        """Test metadata endpoint with URL"""
        client = TestClient(app)
        response = client.get("/api/anime/metadata?url=https://anime-sama.si/test/")
        # Will likely fail without real anime site
        assert response.status_code in [200, 404, 500, 503]


class TestAPIAnimeEpisodes:
    """Tests for anime episodes endpoint"""

    def test_anime_episodes_missing_url(self):
        """Test episodes endpoint without URL"""
        client = TestClient(app)
        response = client.get("/api/anime/episodes")
        # Returns 422 for validation error
        assert response.status_code == 422

    def test_anime_episodes_with_url(self):
        """Test episodes endpoint with URL"""
        client = TestClient(app)
        response = client.get(
            "/api/anime/episodes?url=https://anime-sama.si/test/&lang=vostfr"
        )
        # Will likely fail without real anime site
        assert response.status_code in [200, 404, 500, 503]


class TestAPIFavorites:
    """Tests for favorites endpoints"""

    def test_list_favorites_empty(self):
        """Test listing favorites when empty"""
        client = TestClient(app)
        response = client.get("/api/favorites")
        assert response.status_code == 200
        data = response.json()
        assert "favorites" in data
        assert "total" in data

    def test_add_favorite_success(self):
        """Test adding a favorite"""
        client = TestClient(app)
        response = client.post(
            "/api/favorites",
            json={
                "anime_id": "test-anime-123",
                "title": "Test Anime",
                "url": "https://example.com/anime",
                "provider": "anime-sama",
                "metadata": {"genres": ["Action"]},
                "poster_url": "https://example.com/poster.jpg"
            }
        )
        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "added"
        assert "favorite" in data

    def test_add_favorite_missing_fields(self):
        """Test adding favorite with missing required fields"""
        client = TestClient(app)
        response = client.post(
            "/api/favorites",
            json={"title": "Test Anime"}  # Missing anime_id, url, provider
        )
        assert response.status_code == 400

    def test_remove_favorite_success(self):
        """Test removing a favorite"""
        client = TestClient(app)
        # Add first
        client.post(
            "/api/favorites",
            json={
                "anime_id": "test-remove-123",
                "title": "Remove Me",
                "url": "https://example.com",
                "provider": "anime-sama"
            }
        )

        # Remove
        response = client.delete("/api/favorites/test-remove-123")
        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "removed"

    def test_remove_favorite_not_found(self):
        """Test removing non-existent favorite"""
        client = TestClient(app)
        response = client.delete("/api/favorites/nonexistent-id")
        assert response.status_code == 404

    def test_get_favorite_success(self):
        """Test getting a specific favorite"""
        client = TestClient(app)
        # Add first
        client.post(
            "/api/favorites",
            json={
                "anime_id": "test-get-123",
                "title": "Get Me",
                "url": "https://example.com",
                "provider": "anime-sama"
            }
        )

        # Get
        response = client.get("/api/favorites/test-get-123")
        assert response.status_code == 200
        data = response.json()
        assert "favorite" in data
        assert data["favorite"]["id"] == "test-get-123"

    def test_get_favorite_not_found(self):
        """Test getting non-existent favorite"""
        client = TestClient(app)
        response = client.get("/api/favorites/nonexistent-id")
        assert response.status_code == 404

    def test_get_favorites_stats(self):
        """Test getting favorites statistics"""
        client = TestClient(app)
        response = client.get("/api/favorites/stats")
        assert response.status_code == 200
        data = response.json()
        assert "total" in data
        assert "by_provider" in data
        assert "by_genre" in data

    def test_toggle_favorite_add(self):
        """Test toggling favorite to add"""
        client = TestClient(app)
        # Make sure it doesn't exist first
        try:
            client.delete("/api/favorites/test-toggle-add")
        except:
            pass

        response = client.post(
            "/api/favorites/toggle",
            json={
                "anime_id": "test-toggle-add",
                "title": "Toggle Add",
                "url": "https://example.com",
                "provider": "anime-sama"
            }
        )
        assert response.status_code == 200
        data = response.json()
        assert data["action"] == "added"

    def test_toggle_favorite_remove(self):
        """Test toggling favorite to remove"""
        client = TestClient(app)
        # Make sure it doesn't exist first
        try:
            client.delete("/api/favorites/test-toggle-remove")
        except:
            pass

        # Add first
        client.post(
            "/api/favorites/toggle",
            json={
                "anime_id": "test-toggle-remove",
                "title": "Toggle Remove",
                "url": "https://example.com",
                "provider": "anime-sama"
            }
        )

        # Toggle to remove
        response = client.post(
            "/api/favorites/toggle",
            json={
                "anime_id": "test-toggle-remove",
                "title": "Toggle Remove",
                "url": "https://example.com",
                "provider": "anime-sama"
            }
        )
        assert response.status_code == 200
        data = response.json()
        assert data["action"] == "removed"


class TestAPIFavoritesSorting:
    """Tests for favorites sorting and filtering"""

    def test_favorites_sort_by_title(self):
        """Test sorting favorites by title"""
        client = TestClient(app)
        # Add multiple favorites
        for title in ["Z Anime", "A Anime", "M Anime"]:
            client.post(
                "/api/favorites",
                json={
                    "anime_id": f"sort-{title}",
                    "title": title,
                    "url": f"https://example.com/{title}",
                    "provider": "anime-sama"
                }
            )

        response = client.get("/api/favorites?sort_by=title&order=asc")
        assert response.status_code == 200
        data = response.json()
        if len(data["favorites"]) > 0:
            # Check if sorted
            titles = [f["title"] for f in data["favorites"][:3]]
            assert titles == sorted(titles)

    def test_favorites_filter_by_provider(self):
        """Test filtering favorites by provider"""
        client = TestClient(app)
        # Add favorites with different providers
        client.post(
            "/api/favorites",
            json={
                "anime_id": "filter-anime-sama",
                "title": "Anime Sama",
                "url": "https://example.com",
                "provider": "anime-sama"
            }
        )
        client.post(
            "/api/favorites",
            json={
                "anime_id": "filter-neko-sama",
                "title": "Neko Sama",
                "url": "https://example.com",
                "provider": "neko-sama"
            }
        )

        response = client.get("/api/favorites?filter_provider=anime-sama")
        assert response.status_code == 200
        data = response.json()
        # Should only have anime-sama
        for fav in data["favorites"]:
            assert fav["provider"] == "anime-sama"


class TestAPIWebInterface:
    """Tests for web interface endpoint"""

    def test_web_interface(self):
        """Test web interface endpoint"""
        client = TestClient(app)
        response = client.get("/web")
        assert response.status_code == 200
        # Should return HTML
        assert "text/html" in response.headers["content-type"]


class TestAPIVideoStreaming:
    """Tests for video streaming endpoints"""

    def test_video_player_by_id_not_found(self):
        """Test video player with non-existent task ID"""
        client = TestClient(app)
        response = client.get("/player/nonexistent-id")
        # Should return 404 or similar
        assert response.status_code in [404, 403]

    def test_video_stream_by_filename_not_found(self):
        """Test video stream with non-existent filename"""
        client = TestClient(app)
        response = client.get("/stream/nonexistent_file.mp4")
        # Should return 404
        assert response.status_code == 404


class TestAPIErrorHandling:
    """Tests for API error handling"""

    def test_invalid_endpoint(self):
        """Test accessing invalid endpoint"""
        client = TestClient(app)
        response = client.get("/api/invalid_endpoint")
        assert response.status_code == 404

    def test_invalid_method(self):
        """Test using invalid HTTP method"""
        client = TestClient(app)
        response = client.post("/api/downloads")  # Should be GET
        assert response.status_code == 405  # Method Not Allowed

    def test_malformed_json(self):
        """Test sending malformed JSON"""
        client = TestClient(app)
        response = client.post(
            "/api/download",
            data="invalid json",
            headers={"Content-Type": "application/json"}
        )
        assert response.status_code == 422


class TestAPICorsHeaders:
    """Tests for CORS headers"""

    def test_cors_headers_present(self):
        """Test that CORS headers are present"""
        client = TestClient(app)
        response = client.get("/")
        # Check for CORS headers
        # Note: Actual CORS configuration may vary
        # This is just to ensure the endpoint responds
        assert response.status_code == 200


# Mock-based tests for endpoints requiring external resources
class TestAPIWithMocks:
    """Tests with mocked external dependencies"""

    def test_download_with_mocked_downloader(self):
        """Test download creation with mocked downloader"""
        client = TestClient(app)
        with patch('app.download_manager.get_downloader') as mock_get_downloader:
            mock_downloader = AsyncMock()
            mock_downloader.get_download_link.return_value = (
                "https://example.com/direct",
                "video.mp4"
            )
            mock_downloader.can_handle.return_value = True
            mock_get_downloader.return_value = mock_downloader

            response = client.post(
                "/api/download",
                json={"url": "https://doodstream.com/test"}
            )
            # Should succeed
            assert response.status_code == 200
