"""
Additional tests to achieve >95% coverage.
"""

import pytest
import json
import base64
from unittest.mock import Mock, patch, MagicMock
from datetime import datetime, date
import pandas as pd


pytestmark = pytest.mark.unit


@pytest.mark.asyncio
class TestCallToolBranches:
    """Test all branches in call_tool function."""
    
    @patch('cpap_mcp.server.CPAPReader')
    async def test_all_tool_branches(self, mock_cpap_reader_class):
        """Test each tool name branch in call_tool."""
        from cpap_mcp.server import call_tool, upload_file
        
        # Setup mock
        mock_reader = Mock()
        mock_device = Mock()
        mock_device.model_dump.return_value = {
            "serial_number": "TEST123",
            "model_name": "Test Device",
        }
        mock_reader.get_devices.return_value = [mock_device]
        mock_reader.get_sessions.return_value = []
        mock_reader.get_validation_errors.return_value = []
        mock_cpap_reader_class.return_value = mock_reader
        
        # Upload a file first
        content_b64 = base64.b64encode(b"test data").decode('utf-8')
        await upload_file({"file_path": "test.edf", "content": content_b64})
        
        # Test each tool branch
        tools_to_test = [
            ("list_uploaded_files", {}),
            ("get_devices", {}),
            ("get_sessions", {}),
            ("get_validation_errors", {}),
            ("export_all_data", {}),
        ]
        
        for tool_name, args in tools_to_test:
            result = await call_tool(tool_name, args)
            assert len(result) > 0
            assert result[0].type == "text"


@pytest.mark.asyncio
class TestDeviceSerializationBranches:
    """Test device model_dump branches (lines 408-440)."""
    
    @patch('cpap_mcp.server.CPAPReader')
    async def test_device_with_all_fields(self, mock_cpap_reader_class):
        """Test device serialization with all optional fields."""
        from cpap_mcp.server import get_devices, upload_file
        
        # Create device with all fields
        mock_device = Mock()
        mock_device.model_dump.return_value = {
            "serial_number": "SN12345",
            "model_name": "AirSense 11",
            "model_id": "AS11",
            "firmware_version": "v2.1.0",
            "last_updated": datetime.now().isoformat(),
        }
        
        mock_reader = Mock()
        mock_reader.get_devices.return_value = [mock_device]
        mock_cpap_reader_class.return_value = mock_reader
        
        # Upload file
        content_b64 = base64.b64encode(b"test").decode('utf-8')
        await upload_file({"file_path": "test.edf", "content": content_b64})
        
        result = await get_devices()
        data = json.loads(result[0].text)
        assert data["count"] == 1
        assert data["devices"][0]["serial_number"] == "SN12345"
        assert data["devices"][0]["model_id"] == "AS11"
        assert data["devices"][0]["firmware_version"] == "v2.1.0"


@pytest.mark.asyncio  
class TestSessionDetailsEdgeCases:
    """Test session details with various data configurations."""
    
    @patch('cpap_mcp.server.CPAPReader')
    async def test_session_with_empty_spo2(self, mock_cpap_reader_class):
        """Test session with no SpO2 data (line 741, 750)."""
        from cpap_mcp.server import get_session_spo2_data, upload_file
        
        # Setup mock session with no SpO2 data
        mock_session = Mock()
        mock_session.session_id = "test_session"
        mock_session.get_spo2_data.return_value = None  # No SpO2 data
        
        mock_reader = Mock()
        mock_reader.get_sessions.return_value = [mock_session]
        mock_cpap_reader_class.return_value = mock_reader
        
        # Upload file
        content_b64 = base64.b64encode(b"test").decode('utf-8')
        await upload_file({"file_path": "test.edf", "content": content_b64})
        
        result = await get_session_spo2_data({"session_id": "test_session", "sample_limit": 100})
        assert "No SpO2 data available" in result[0].text
    
    @patch('cpap_mcp.server.CPAPReader')
    async def test_session_with_empty_dataframe(self, mock_cpap_reader_class):
        """Test session with empty DataFrame (line 750)."""
        from cpap_mcp.server import get_session_spo2_data, upload_file
        
        # Setup mock session with empty DataFrame
        mock_session = Mock()
        mock_session.session_id = "test_session"
        mock_session.get_spo2_data.return_value = pd.DataFrame()  # Empty DataFrame
        
        mock_reader = Mock()
        mock_reader.get_sessions.return_value = [mock_session]
        mock_cpap_reader_class.return_value = mock_reader
        
        # Upload file
        content_b64 = base64.b64encode(b"test").decode('utf-8')
        await upload_file({"file_path": "test.edf", "content": content_b64})
        
        result = await get_session_spo2_data({"session_id": "test_session", "sample_limit": 100})
        assert "No SpO2 data available" in result[0].text
    
    @patch('cpap_mcp.server.CPAPReader')
    async def test_session_with_large_spo2_data(self, mock_cpap_reader_class):
        """Test SpO2 data sampling (lines 752-762)."""
        from cpap_mcp.server import get_session_spo2_data, upload_file
        
        # Create large SpO2 DataFrame
        large_df = pd.DataFrame({
            'timestamp': pd.date_range('2024-01-01', periods=10000, freq='1s'),
            'spo2': [95 + i % 5 for i in range(10000)],
        })
        
        mock_session = Mock()
        mock_session.session_id = "test_session"
        mock_session.get_spo2_data.return_value = large_df
        
        mock_reader = Mock()
        mock_reader.get_sessions.return_value = [mock_session]
        mock_cpap_reader_class.return_value = mock_reader
        
        # Upload file
        content_b64 = base64.b64encode(b"test").decode('utf-8')
        await upload_file({"file_path": "test.edf", "content": content_b64})
        
        result = await get_session_spo2_data({"session_id": "test_session", "sample_limit": 100})
        data = json.loads(result[0].text)
        
        assert data["total_samples"] <= 100  # Should be limited
        assert data["sample_limit_applied"] == 100


@pytest.mark.asyncio
class TestGetDeviceSettings:
    """Test get_device_settings branches (lines 796, 610, 615-618)."""
    
    @patch('cpap_mcp.server.CPAPReader')
    async def test_get_device_settings_with_session_id(self, mock_cpap_reader_class):
        """Test get_device_settings with specific session_id (line 796)."""
        from cpap_mcp.server import get_device_settings, upload_file
        
        # Setup mock session
        mock_settings = Mock()
        mock_settings.model_dump.return_value = {
            "mode": "APAP",
            "min_pressure": 4.0,
            "max_pressure": 20.0,
        }
        
        mock_session = Mock()
        mock_session.session_id = "specific_session"
        mock_session.start_time = datetime(2024, 1, 1)
        mock_session.settings = mock_settings
        
        mock_reader = Mock()
        mock_reader.get_sessions.return_value = [mock_session]
        mock_cpap_reader_class.return_value = mock_reader
        
        # Upload file
        content_b64 = base64.b64encode(b"test").decode('utf-8')
        await upload_file({"file_path": "test.edf", "content": content_b64})
        
        result = await get_device_settings({"session_id": "specific_session"})
        data = json.loads(result[0].text)
        assert data["session_id"] == "specific_session"
        assert data["settings"]["mode"] == "APAP"
    
    @patch('cpap_mcp.server.CPAPReader')
    async def test_get_device_settings_most_recent(self, mock_cpap_reader_class):
        """Test get_device_settings without session_id - gets most recent."""
        from cpap_mcp.server import get_device_settings, upload_file
        
        # Setup multiple sessions
        mock_settings1 = Mock()
        mock_settings1.model_dump.return_value = {"mode": "CPAP"}
        mock_settings2 = Mock()
        mock_settings2.model_dump.return_value = {"mode": "APAP"}
        
        mock_session1 = Mock()
        mock_session1.session_id = "old_session"
        mock_session1.start_time = datetime(2024, 1, 1)
        mock_session1.settings = mock_settings1
        
        mock_session2 = Mock()
        mock_session2.session_id = "recent_session"
        mock_session2.start_time = datetime(2024, 1, 2)
        mock_session2.settings = mock_settings2
        
        mock_reader = Mock()
        mock_reader.get_sessions.return_value = [mock_session1, mock_session2]
        mock_cpap_reader_class.return_value = mock_reader
        
        # Upload file
        content_b64 = base64.b64encode(b"test").decode('utf-8')
        await upload_file({"file_path": "test.edf", "content": content_b64})
        
        result = await get_device_settings({})
        data = json.loads(result[0].text)
        assert data["session_id"] == "recent_session"  # Most recent
        assert data["settings"]["mode"] == "APAP"
    
    @patch('cpap_mcp.server.CPAPReader')
    async def test_get_device_settings_invalid_session(self, mock_cpap_reader_class):
        """Test get_device_settings with invalid session_id (line 796)."""
        from cpap_mcp.server import get_device_settings, upload_file
        
        mock_session = Mock()
        mock_session.session_id = "valid_session"
        mock_session.start_time = datetime(2024, 1, 1)
        
        mock_reader = Mock()
        mock_reader.get_sessions.return_value = [mock_session]
        mock_cpap_reader_class.return_value = mock_reader
        
        # Upload file
        content_b64 = base64.b64encode(b"test").decode('utf-8')
        await upload_file({"file_path": "test.edf", "content": content_b64})
        
        with pytest.raises(ValueError, match="Session not found"):
            await get_device_settings({"session_id": "invalid_session"})


@pytest.mark.asyncio
class TestAnalyzeTherapyEffectiveness:
    """Test therapy analysis branches (lines 930, 972-974, 1000-1001, 1010)."""
    
    @patch('cpap_mcp.server.CPAPReader')
    async def test_analyze_with_mild_ahi(self, mock_cpap_reader_class):
        """Test therapy analysis with mild AHI (line 930)."""
        from cpap_mcp.server import analyze_therapy_effectiveness, upload_file
        
        # Setup mock sessions with mild AHI
        mock_session = Mock()
        mock_session.session_id = "test_session"
        mock_session.start_time = datetime(2024, 1, 1)
        mock_session.duration_hours = 8.0
        mock_session.ahi = 3.5  # Mild AHI (< 5)
        mock_session.average_pressure = 10.0
        mock_session.leak_rate = 5.0
        
        mock_reader = Mock()
        mock_reader.get_sessions.return_value = [mock_session]
        # Mock get_summary_statistics to return proper dictionary
        # Sessions need to be dicts with summary structure
        mock_reader.get_summary_statistics.return_value = {
            "sessions": [{
                "summary": {
                    "duration_hours": 8.0,
                    "ahi": 3.5,
                    "leak_95th": 5.0,
                }
            }],
            "total_sessions": 1,
            "total_hours": 8.0,
            "average_hours_per_session": 8.0,
            "average_ahi": 3.5,
            "average_leak_95th": 5.0,
        }
        mock_cpap_reader_class.return_value = mock_reader
        
        # Upload file
        content_b64 = base64.b64encode(b"test").decode('utf-8')
        await upload_file({"file_path": "test.edf", "content": content_b64})
        
        result = await analyze_therapy_effectiveness({})
        data = json.loads(result[0].text)
        
        # Check nested structure
        assert data["therapy_effectiveness"]["average_ahi"] == 3.5
        assert "excellent" in data["therapy_effectiveness"]["ahi_control"].lower()
    
    @patch('cpap_mcp.server.CPAPReader')
    async def test_analyze_with_moderate_ahi(self, mock_cpap_reader_class):
        """Test therapy analysis with moderate AHI (lines 972-974)."""
        from cpap_mcp.server import analyze_therapy_effectiveness, upload_file
        
        # Setup mock sessions with moderate AHI
        mock_session = Mock()
        mock_session.session_id = "test_session"
        mock_session.start_time = datetime(2024, 1, 1)
        mock_session.duration_hours = 7.0
        mock_session.ahi = 12.0  # Moderate AHI (5-15)
        mock_session.average_pressure = 12.0
        mock_session.leak_rate = 10.0
        
        mock_reader = Mock()
        mock_reader.get_sessions.return_value = [mock_session]
        # Mock get_summary_statistics to return proper dictionary
        mock_reader.get_summary_statistics.return_value = {
            "sessions": [{
                "summary": {
                    "duration_hours": 7.0,
                    "ahi": 12.0,
                    "leak_95th": 10.0,
                }
            }],
            "total_sessions": 1,
            "total_hours": 7.0,
            "average_hours_per_session": 7.0,
            "average_ahi": 12.0,
            "average_leak_95th": 10.0,
        }
        mock_cpap_reader_class.return_value = mock_reader
        
        # Upload file
        content_b64 = base64.b64encode(b"test").decode('utf-8')
        await upload_file({"file_path": "test.edf", "content": content_b64})
        
        result = await analyze_therapy_effectiveness({})
        data = json.loads(result[0].text)
        
        assert data["therapy_effectiveness"]["average_ahi"] == 12.0
        assert "good" in data["therapy_effectiveness"]["ahi_control"].lower()
    
    @patch('cpap_mcp.server.CPAPReader')
    async def test_analyze_with_high_ahi(self, mock_cpap_reader_class):
        """Test therapy analysis with high AHI (lines 1000-1001)."""
        from cpap_mcp.server import analyze_therapy_effectiveness, upload_file
        
        # Setup mock sessions with high AHI
        mock_session = Mock()
        mock_session.session_id = "test_session"
        mock_session.start_time = datetime(2024, 1, 1)
        mock_session.duration_hours = 6.0
        mock_session.ahi = 20.0  # High AHI (> 15)
        mock_session.average_pressure = 8.0
        mock_session.leak_rate = 25.0
        
        mock_reader = Mock()
        mock_reader.get_sessions.return_value = [mock_session]
        # Mock get_summary_statistics to return proper dictionary
        mock_reader.get_summary_statistics.return_value = {
            "sessions": [{
                "summary": {
                    "duration_hours": 6.0,
                    "ahi": 20.0,
                    "leak_95th": 25.0,
                }
            }],
            "total_sessions": 1,
            "total_hours": 6.0,
            "average_hours_per_session": 6.0,
            "average_ahi": 20.0,
            "average_leak_95th": 25.0,
        }
        mock_cpap_reader_class.return_value = mock_reader
        
        # Upload file
        content_b64 = base64.b64encode(b"test").decode('utf-8')
        await upload_file({"file_path": "test.edf", "content": content_b64})
        
        result = await analyze_therapy_effectiveness({})
        data = json.loads(result[0].text)
        
        assert data["therapy_effectiveness"]["average_ahi"] == 20.0
        assert "needs_improvement" in data["therapy_effectiveness"]["ahi_control"].lower()
    
    @patch('cpap_mcp.server.CPAPReader')
    async def test_analyze_with_device_filter(self, mock_cpap_reader_class):
        """Test therapy analysis with device_id filter (line 1010)."""
        from cpap_mcp.server import analyze_therapy_effectiveness, upload_file
        
        # Setup mock sessions
        mock_session1 = Mock()
        mock_session1.session_id = "session1"
        mock_session1.device_id = "device1"
        mock_session1.start_time = datetime(2024, 1, 1)
        mock_session1.duration_hours = 8.0
        mock_session1.ahi = 3.0
        mock_session1.average_pressure = 10.0
        mock_session1.leak_rate = 5.0
        
        mock_session2 = Mock()
        mock_session2.session_id = "session2"
        mock_session2.device_id = "device2"
        mock_session2.start_time = datetime(2024, 1, 2)
        mock_session2.duration_hours = 7.0
        mock_session2.ahi = 15.0
        mock_session2.average_pressure = 12.0
        mock_session2.leak_rate = 10.0
        
        mock_reader = Mock()
        # When called with device_id, only return matching sessions
        def get_sessions_side_effect(device_id=None, **kwargs):
            if device_id == "device1":
                return [mock_session1]
            return [mock_session1, mock_session2]
        
        mock_reader.get_sessions.side_effect = get_sessions_side_effect
        # Mock get_summary_statistics to return proper dictionary
        mock_reader.get_summary_statistics.return_value = {
            "sessions": [{
                "summary": {
                    "duration_hours": 8.0,
                    "ahi": 3.0,
                    "leak_95th": 5.0,
                }
            }],
            "total_sessions": 1,
            "total_hours": 8.0,
            "average_hours_per_session": 8.0,
            "average_ahi": 3.0,
            "average_leak_95th": 5.0,
        }
        mock_cpap_reader_class.return_value = mock_reader
        
        # Upload file
        content_b64 = base64.b64encode(b"test").decode('utf-8')
        await upload_file({"file_path": "test.edf", "content": content_b64})
        
        result = await analyze_therapy_effectiveness({"device_id": "device1"})
        data = json.loads(result[0].text)
        
        # Should only analyze device1's session
        assert data["total_sessions"] == 1
        assert data["therapy_effectiveness"]["average_ahi"] == 3.0


@pytest.mark.asyncio
class TestProposeComfortImprovements:
    """Test comfort improvement proposals (lines 658, 700, 706, 710-711)."""
    
    @patch('cpap_mcp.server.CPAPReader')
    async def test_propose_comfort_with_high_leak(self, mock_cpap_reader_class):
        """Test comfort proposals with high leak rate (line 658)."""
        from cpap_mcp.server import propose_comfort_improvements, upload_file
        from cpap_py.models import DeviceSettings, CPAPMode
        
        # Setup mock session with high leak
        mock_session = Mock()
        mock_session.session_id = "test_session"
        mock_session.start_time = datetime(2024, 1, 1)
        mock_session.leak_rate = 30.0  # High leak
        mock_session.ahi = 5.0
        mock_session.duration_hours = 7.0
        mock_session.average_pressure = 10.0
        # Add real DeviceSettings object
        mock_session.settings = DeviceSettings(
            mode=CPAPMode.APAP,
            epr_enabled=False,
            epr_level=0,
            ramp_enabled=False,
            ramp_time=0,
        )
        
        mock_reader = Mock()
        mock_reader.get_sessions.return_value = [mock_session]
        # Mock get_devices to return a list of device mocks
        mock_device = Mock()
        mock_device.serial_number = "TEST123"
        mock_reader.get_devices.return_value = [mock_device]
        mock_cpap_reader_class.return_value = mock_reader
        
        # Upload file
        content_b64 = base64.b64encode(b"test").decode('utf-8')
        await upload_file({"file_path": "test.edf", "content": content_b64})
        
        result = await propose_comfort_improvements({})
        data = json.loads(result[0].text)
        
        # Should have comfort improvement proposals (EPR and ramp)
        assert "proposed_changes" in data
        assert len(data["proposed_changes"]) > 0
        # Check that changes are for comfort (EPR or ramp related)
        change_names = [c["setting"] for c in data["proposed_changes"]]
        assert any("epr" in name.lower() or "ramp" in name.lower() for name in change_names)
    
    @patch('cpap_mcp.server.CPAPReader')
    async def test_propose_comfort_with_short_duration(self, mock_cpap_reader_class):
        """Test comfort proposals with short usage (line 700)."""
        from cpap_mcp.server import propose_comfort_improvements, upload_file
        from cpap_py.models import DeviceSettings, CPAPMode
        
        # Setup mock session with short duration
        mock_session = Mock()
        mock_session.session_id = "test_session"
        mock_session.start_time = datetime(2024, 1, 1)
        mock_session.leak_rate = 10.0
        mock_session.ahi = 5.0
        mock_session.duration_hours = 4.0  # Short duration (< 6 hours)
        mock_session.average_pressure = 10.0
        # Add real DeviceSettings object
        mock_session.settings = DeviceSettings(
            mode=CPAPMode.APAP,
            epr_enabled=False,
            epr_level=0,
            ramp_enabled=False,
            ramp_time=0,
        )
        
        mock_reader = Mock()
        mock_reader.get_sessions.return_value = [mock_session]
        # Mock get_devices to return a list of device mocks
        mock_device = Mock()
        mock_device.serial_number = "TEST123"
        mock_reader.get_devices.return_value = [mock_device]
        mock_cpap_reader_class.return_value = mock_reader
        
        # Upload file
        content_b64 = base64.b64encode(b"test").decode('utf-8')
        await upload_file({"file_path": "test.edf", "content": content_b64})
        
        result = await propose_comfort_improvements({})
        data = json.loads(result[0].text)
        
        # Should have comfort improvement proposals (EPR and ramp)
        assert "proposed_changes" in data
        assert len(data["proposed_changes"]) > 0
        # Check for comfort-related changes
        change_names = [c["setting"] for c in data["proposed_changes"]]
        assert any("epr" in name.lower() or "ramp" in name.lower() for name in change_names)
