from unittest.mock import patch, AsyncMock, MagicMock
import pytest
from fastapi.testclient import TestClient
from fastapi import FastAPI
from models.presentation_layout import PresentationLayoutModel
from models.presentation_structure_model import PresentationStructureModel
from api.v1.ppt.endpoints.presentation import PRESENTATION_ROUTER

class MockAiohttpResponse:
    def __init__(self, status=200, json_data=None):
        self.status = status
        self._json_data = json_data or {"path": "/tmp/exports/test.pdf"}

    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc, tb):
        pass

    async def json(self):
        return self._json_data

    async def text(self):
        return str(self._json_data)

class MockAiohttpSession:
    def __init__(self, *args, **kwargs):
        pass

    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc, tb):
        pass

    def post(self, *args, **kwargs):
        return MockAiohttpResponse()

    def get(self, *args, **kwargs):
        pptx_model_data = {
            "slides": [],
            "title": "Test",
            "notes": [],
            "layout": {},
            "structure": {},
        }
        return MockAiohttpResponse(json_data=pptx_model_data)

@pytest.fixture
def app():
    app = FastAPI()
    app.include_router(PRESENTATION_ROUTER, prefix="/api/v1/ppt")
    return app

@pytest.fixture
def client(app):
    return TestClient(app)

@pytest.fixture
def mock_get_layout():
    async def _mock_get_layout_by_name(layout_name: str):
        mock_slide = MagicMock()
        mock_slide.name = "Mock Slide"
        mock_slide.json_schema = {"title": "Mock Slide Title"}
        mock_slide.description = "Mock slide description"
        mock_layout = MagicMock(spec=PresentationLayoutModel)
        mock_layout.name = layout_name
        mock_layout.ordered = True
        mock_layout.slides = [mock_slide]
        mock_layout.model_dump = lambda: {}
        mock_layout.to_presentation_structure = lambda: PresentationStructureModel(
            slides=[index for index in range(len(mock_layout.slides))]
        )
        def to_string():
            message = f"## Presentation Layout\n\n"
            for index, slide in enumerate(mock_layout.slides):
                message += f"### Slide Layout: {index}: \n"
                message += f"- Name: {slide.name or slide.json_schema.get('title')} \n"
                message += f"- Description: {slide.description} \n\n"
            return message
        mock_layout.to_string = to_string
        return mock_layout
    return _mock_get_layout_by_name

async def mock_generate_ppt_outline(*args, **kwargs):
    yield '{"title": "Test", "slides": [{"title": "Slide 1", "body": "Body 1"}], "notes": []}'

@pytest.fixture(autouse=True)
def patch_presentation_api(monkeypatch, mock_get_layout):
    # Patch all dependencies used in the API
    patches = [
        patch('api.v1.ppt.endpoints.presentation.get_layout_by_name', new=AsyncMock(side_effect=mock_get_layout)),
        patch('api.v1.ppt.endpoints.presentation.TEMP_FILE_SERVICE.create_temp_dir', return_value='/tmp/mockdir'),
        patch('api.v1.ppt.endpoints.presentation.DocumentsLoader'),
        patch('api.v1.ppt.endpoints.presentation.generate_document_summary', new_callable=AsyncMock, return_value="mock_summary"),
        patch('api.v1.ppt.endpoints.presentation.generate_ppt_outline', side_effect=mock_generate_ppt_outline),
        patch('api.v1.ppt.endpoints.presentation.get_sql_session'),
        patch('api.v1.ppt.endpoints.presentation.get_slide_content_from_type_and_outline', new_callable=AsyncMock, return_value={"mock": "slide_content"}),
        patch('api.v1.ppt.endpoints.presentation.process_slide_and_fetch_assets', new_callable=AsyncMock),
        patch('api.v1.ppt.endpoints.presentation.get_exports_directory', return_value='/tmp/exports'),
        patch('api.v1.ppt.endpoints.presentation.PptxPresentationCreator'),
        patch('api.v1.ppt.endpoints.presentation.aiohttp.ClientSession', return_value=MockAiohttpSession()),
    ]
    mocks = [p.start() for p in patches]

    # Setup DocumentsLoader mock
    docs_loader = mocks[2]
    docs_loader.return_value.load_documents = AsyncMock()
    docs_loader.return_value.documents = []

    # Setup PptxPresentationCreator mock for pptx test
    pptx_creator = mocks[9]
    pptx_creator.return_value.create_ppt = AsyncMock()
    pptx_creator.return_value.save = MagicMock()

    yield

    for p in patches:
        p.stop()

class TestPresentationGenerationAPI:
    def test_generate_presentation_export_as_pdf(self, client):
        response = client.post(
            "/api/v1/ppt/presentation/generate",
            json={
                "prompt": "Create a presentation about artificial intelligence and machine learning",
                "n_slides": 5,
                "language": "English",
                "export_as": "pdf",
                "layout": "general"
            }
        )
        assert response.status_code == 200
        assert "presentation_id" in response.json()
        assert "pdf" in response.json()["path"]

    def test_generate_presentation_export_as_pptx(self, client):
        response = client.post(
            "/api/v1/ppt/presentation/generate",
            json={
                "prompt": "Create a presentation about artificial intelligence and machine learning",
                "n_slides": 5,
                "language": "English",
                "export_as": "pptx",
                "layout": "general"
            }
        )
        assert response.status_code == 200
        assert "presentation_id" in response.json()
        assert "pptx" in response.json()["path"]

    def test_generate_presentation_with_no_prompt(self, client):
        response = client.post(
            "/api/v1/ppt/presentation/generate",
            json={
                "n_slides": 5,
                "language": "English",
                "export_as": "pdf",
                "layout": "general"
            }
        )
        assert response.status_code == 422


    def test_generate_presentation_with_n_slides_less_than_one(self, client):
        response = client.post(
            "/api/v1/ppt/presentation/generate",
            json={
                "prompt": "Create a presentation about artificial intelligence and machine learning",
                "n_slides": 0,
                "language": "English",
                "export_as": "pdf",
                "layout": "general"
            }
        )
        assert response.status_code == 422

    def test_generate_presentation_with_invalid_export_type(self, client):
        response = client.post(
            "/api/v1/ppt/presentation/generate",
            json={
                "prompt": "Create a presentation about artificial intelligence and machine learning",
                "n_slides": 5,
                "language": "English",
                "export_as": "invalid_type",
                "layout": "general"
            }
        )
        assert response.status_code == 422
