"""
pytest configuration and shared fixtures.
"""

import asyncio
import pytest
import pytest_asyncio
from typing import AsyncGenerator, Generator
from unittest.mock import AsyncMock, MagicMock

from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker
from fastapi.testclient import TestClient

from src.tempering_machine.shared.database import Base, get_db
from src.tempering_machine.shared.config import settings
from src.tempering_machine.services.web.main import app
from src.tempering_machine.services.hardware.hardware_manager import hardware_manager
from src.tempering_machine.services.safety.safety_monitor import safety_monitor
from src.tempering_machine.services.recipe.recipe_controller import recipe_controller


# Test database URL
TEST_DATABASE_URL = "sqlite+aiosqlite:///:memory:"


@pytest.fixture(scope="session")
def event_loop():
    """Create event loop for the test session."""
    loop = asyncio.get_event_loop_policy().new_event_loop()
    yield loop
    loop.close()


@pytest_asyncio.fixture
async def test_db_engine():
    """Create test database engine."""
    engine = create_async_engine(
        TEST_DATABASE_URL,
        echo=False,
        future=True
    )
    
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)
    
    yield engine
    
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.drop_all)
    
    await engine.dispose()


@pytest_asyncio.fixture
async def test_db_session(test_db_engine) -> AsyncGenerator[AsyncSession, None]:
    """Create test database session."""
    async_session = sessionmaker(
        test_db_engine, class_=AsyncSession, expire_on_commit=False
    )
    
    async with async_session() as session:
        yield session


@pytest.fixture
def test_client(test_db_session):
    """Create test client with database override."""
    def override_get_db():
        return test_db_session
    
    app.dependency_overrides[get_db] = override_get_db
    
    with TestClient(app) as client:
        yield client
    
    app.dependency_overrides.clear()


@pytest.fixture
def mock_hardware_manager():
    """Mock hardware manager for testing."""
    mock = MagicMock()
    mock.initialize = AsyncMock(return_value=True)
    mock.shutdown = AsyncMock(return_value=True)
    mock.get_hardware_status = MagicMock()
    mock.get_all_temperatures = AsyncMock(return_value={})
    mock.get_temperature = AsyncMock(return_value=None)
    mock.is_safe_to_operate = AsyncMock(return_value=(True, []))
    mock.emergency_stop = AsyncMock(return_value=True)
    mock.set_motor_state = AsyncMock(return_value=True)
    mock.set_heater_state = AsyncMock(return_value=True)
    
    return mock


@pytest.fixture
def mock_safety_monitor():
    """Mock safety monitor for testing."""
    mock = MagicMock()
    mock.initialize = AsyncMock(return_value=True)
    mock.shutdown = AsyncMock(return_value=True)
    mock.get_active_alarms = MagicMock(return_value=[])
    mock.get_alarm_summary = MagicMock(return_value={
        "total_active_alarms": 0,
        "active_by_priority": {"critical": 0, "high": 0, "medium": 0, "low": 0},
        "emergency_stop_active": False,
        "last_safety_check": "2025-01-08T12:00:00",
    })
    mock.acknowledge_alarm = AsyncMock(return_value=True)
    
    return mock


@pytest.fixture
def mock_recipe_controller():
    """Mock recipe controller for testing."""
    mock = MagicMock()
    mock.start_recipe = AsyncMock(return_value="test-session-id")
    mock.stop_recipe = AsyncMock(return_value=True)
    mock.pause_recipe = AsyncMock(return_value=True)
    mock.resume_recipe = AsyncMock(return_value=True)
    mock.emergency_stop = AsyncMock(return_value=True)
    mock.get_process_status = AsyncMock(return_value=None)
    mock.get_active_sessions = AsyncMock(return_value=[])
    
    return mock


@pytest.fixture
def sample_recipe_data():
    """Sample recipe data for testing."""
    return {
        "name": "Test Recipe",
        "description": "A test recipe for unit testing",
        "heating_goal": 46.0,
        "cooling_goal": 27.0,
        "pouring_goal": 30.0,
        "tank_temp": 45.0,
        "fountain_temp": 32.0,
        "mixer_enabled": True,
        "fountain_enabled": True,
        "mold_heater_enabled": False,
        "vibration_enabled": False,
        "vib_heater_enabled": False,
        "pedal_control_enabled": True,
        "pedal_on_time": 2.0,
        "pedal_off_time": 3.0,
    }


@pytest.fixture
def sample_temperature_reading():
    """Sample temperature reading for testing."""
    from datetime import datetime
    from src.tempering_machine.services.hardware.hardware_manager import TemperatureReading
    
    return TemperatureReading(
        value=25.5,
        timestamp=datetime.now(),
        sensor_name="tank_bottom",
        units="°C",
        is_valid=True,
        error_message=None
    )


@pytest.fixture
def sample_hardware_status():
    """Sample hardware status for testing."""
    from datetime import datetime
    from src.tempering_machine.services.hardware.hardware_manager import (
        HardwareStatus, TemperatureReading, MotorStatus, MotorState, SafetyStatus, ComponentStatus
    )
    
    return HardwareStatus(
        temperatures={
            "tank_bottom": TemperatureReading(
                value=25.5,
                timestamp=datetime.now(),
                sensor_name="tank_bottom",
                units="°C",
                is_valid=True
            ),
            "fountain": TemperatureReading(
                value=30.2,
                timestamp=datetime.now(),
                sensor_name="fountain",
                units="°C",
                is_valid=True
            )
        },
        motors={
            "mixer_motor": MotorStatus(
                name="mixer_motor",
                state=MotorState.STOPPED,
                is_enabled=False
            )
        },
        safety=SafetyStatus(
            emergency_stop_active=False,
            cover_sensor_closed=True,
            temperature_alarms=[],
            current_alarms=[],
            last_safety_check=datetime.now()
        ),
        communication_health=95.0,
        system_status=ComponentStatus.ONLINE,
        last_update=datetime.now()
    )


@pytest.fixture(autouse=True)
async def setup_test_environment(monkeypatch):
    """Set up test environment with mocked services."""
    # Override settings for testing
    monkeypatch.setattr(settings, "environment", "testing")
    monkeypatch.setattr(settings, "debug", True)
    monkeypatch.setattr(settings.database, "url", TEST_DATABASE_URL)
    
    # Prevent actual hardware initialization during tests
    monkeypatch.setattr(hardware_manager, "initialize", AsyncMock(return_value=True))
    monkeypatch.setattr(safety_monitor, "initialize", AsyncMock(return_value=True))


# Pytest markers
pytest_plugins = ["pytest_asyncio"]