"""Unit tests for Event Logger."""

import pytest
import json
import tempfile
import shutil
from datetime import datetime, timedelta
from pathlib import Path
from unittest.mock import MagicMock, patch
from std_msgs.msg import String

# Import the module under test
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from saltybot_event_logger.event_logger_node import Event, EventLogger


class TestEvent:
    """Test Event dataclass."""

    def test_event_creation(self):
        """Test creating an event."""
        now = datetime.now().isoformat()
        event = Event(
            timestamp=now,
            event_type='voice_command',
            source='/saltybot/voice_command',
            data={'command': 'spin'}
        )

        assert event.timestamp == now
        assert event.event_type == 'voice_command'
        assert event.source == '/saltybot/voice_command'
        assert event.data['command'] == 'spin'

    def test_event_to_dict(self):
        """Test converting event to dict."""
        from dataclasses import asdict
        event = Event(
            timestamp='2026-03-05T12:00:00',
            event_type='trick',
            source='/saltybot/trick_state',
            data={'trick': 'dance'}
        )

        event_dict = asdict(event)
        assert event_dict['event_type'] == 'trick'
        assert 'timestamp' in event_dict


class TestEventLogger:
    """Test EventLogger node."""

    @pytest.fixture
    def temp_data_dir(self):
        """Create temporary data directory."""
        temp_dir = tempfile.mkdtemp()
        yield Path(temp_dir)
        shutil.rmtree(temp_dir)

    @pytest.fixture
    def logger(self, temp_data_dir):
        """Create EventLogger instance with mocked ROS."""
        with patch('saltybot_event_logger.event_logger_node.Node.__init__', return_value=None):
            with patch('saltybot_event_logger.event_logger_node.rclpy'):
                logger = EventLogger()
                logger.data_dir = temp_data_dir
                logger.get_logger = MagicMock()
                logger.create_subscription = MagicMock()
                logger.create_publisher = MagicMock()
                logger.create_timer = MagicMock()
                logger.live_feed_pub = MagicMock()
                logger.stats_pub = MagicMock()
                return logger

    def test_event_types_mapping(self, logger):
        """Test event type mapping."""
        assert logger.EVENT_TYPES['voice_command'] == 'voice_command'
        assert logger.EVENT_TYPES['trick_state'] == 'trick'
        assert logger.EVENT_TYPES['first_encounter'] == 'encounter'
        assert logger.EVENT_TYPES['emergency_stop'] == 'e-stop'

    def test_log_event(self, logger, temp_data_dir):
        """Test logging an event to file."""
        event = Event(
            timestamp='2026-03-05T12:00:00',
            event_type='voice_command',
            source='/saltybot/voice_command',
            data={'command': 'spin'}
        )

        logger._log_event(event)

        # Check file was created
        today = datetime.now().strftime('%Y-%m-%d')
        log_file = temp_data_dir / f'{today}.jsonl'
        assert log_file.exists()

        # Check content
        with open(log_file, 'r') as f:
            line = f.readline()
            logged_event = json.loads(line)

        assert logged_event['event_type'] == 'voice_command'
        assert logged_event['data']['command'] == 'spin'

    def test_log_rotation_compress(self, logger, temp_data_dir):
        """Test log compression for old files."""
        # Create old log file
        old_date = (datetime.now() - timedelta(days=8)).strftime('%Y-%m-%d')
        old_file = temp_data_dir / f'{old_date}.jsonl'
        old_file.write_text('{"test": "data"}\n')

        logger._rotate_logs()

        # Check file was compressed
        gz_file = temp_data_dir / f'{old_date}.jsonl.gz'
        assert gz_file.exists()
        assert not old_file.exists()

    def test_log_rotation_delete(self, logger, temp_data_dir):
        """Test deleting very old files."""
        # Create very old log file
        old_date = (datetime.now() - timedelta(days=91)).strftime('%Y-%m-%d')
        old_file = temp_data_dir / f'{old_date}.jsonl'
        old_file.write_text('{"test": "data"}\n')

        logger._rotate_logs()

        # Check file was deleted
        assert not old_file.exists()

    def test_query_events_all(self, logger, temp_data_dir):
        """Test querying all events."""
        # Create log entries
        today = datetime.now().strftime('%Y-%m-%d')
        log_file = temp_data_dir / f'{today}.jsonl'

        events_data = [
            Event('2026-03-05T10:00:00', 'voice_command', '/saltybot/voice_command', {'cmd': 'spin'}),
            Event('2026-03-05T11:00:00', 'trick', '/saltybot/trick_state', {'trick': 'dance'}),
        ]

        with open(log_file, 'w') as f:
            for event in events_data:
                from dataclasses import asdict
                f.write(json.dumps(asdict(event)) + '\n')

        # Query all
        results = logger.query_events()
        assert len(results) >= 2

    def test_query_events_by_type(self, logger, temp_data_dir):
        """Test querying events by type."""
        today = datetime.now().strftime('%Y-%m-%d')
        log_file = temp_data_dir / f'{today}.jsonl'

        events_data = [
            Event('2026-03-05T10:00:00', 'voice_command', '/saltybot/voice_command', {'cmd': 'spin'}),
            Event('2026-03-05T11:00:00', 'trick', '/saltybot/trick_state', {'trick': 'dance'}),
            Event('2026-03-05T12:00:00', 'error', '/saltybot/error', {'msg': 'test'}),
        ]

        with open(log_file, 'w') as f:
            for event in events_data:
                from dataclasses import asdict
                f.write(json.dumps(asdict(event)) + '\n')

        # Query only voice commands
        results = logger.query_events(event_types=['voice_command'])
        assert all(e.event_type == 'voice_command' for e in results)

    def test_query_events_by_time_range(self, logger, temp_data_dir):
        """Test querying events by time range."""
        today = datetime.now().strftime('%Y-%m-%d')
        log_file = temp_data_dir / f'{today}.jsonl'

        events_data = [
            Event('2026-03-05T10:00:00', 'voice_command', '/saltybot/voice_command', {'cmd': 'spin'}),
            Event('2026-03-05T14:00:00', 'trick', '/saltybot/trick_state', {'trick': 'dance'}),
            Event('2026-03-05T18:00:00', 'error', '/saltybot/error', {'msg': 'test'}),
        ]

        with open(log_file, 'w') as f:
            for event in events_data:
                from dataclasses import asdict
                f.write(json.dumps(asdict(event)) + '\n')

        # Query only morning events
        results = logger.query_events(
            start_time='2026-03-05T09:00:00',
            end_time='2026-03-05T13:00:00'
        )
        assert len(results) >= 1
        assert results[0].event_type == 'voice_command'

    def test_export_csv(self, logger, temp_data_dir):
        """Test CSV export."""
        today = datetime.now().strftime('%Y-%m-%d')
        log_file = temp_data_dir / f'{today}.jsonl'

        events_data = [
            Event('2026-03-05T10:00:00', 'voice_command', '/saltybot/voice_command', {'cmd': 'spin'}),
            Event('2026-03-05T11:00:00', 'trick', '/saltybot/trick_state', {'trick': 'dance'}),
        ]

        with open(log_file, 'w') as f:
            for event in events_data:
                from dataclasses import asdict
                f.write(json.dumps(asdict(event)) + '\n')

        # Export to CSV
        csv_path = logger.export_csv()
        assert csv_path
        assert Path(csv_path).exists()
        assert Path(csv_path).suffix == '.csv'

    def test_daily_stats(self, logger):
        """Test daily statistics calculation."""
        # Add events
        logger.daily_events = [
            Event('2026-03-05T10:00:00', 'voice_command', '/saltybot/voice_command', {}),
            Event('2026-03-05T11:00:00', 'trick', '/saltybot/trick_state', {}),
        ]
        logger.daily_stats = {'voice_command': 1, 'trick': 1, 'encounters': 5, 'errors': 0}

        stats = logger.get_daily_stats()

        assert stats['total_events'] == 2
        assert stats['encounters'] == 5
        assert stats['errors'] == 0
        assert 'by_type' in stats

    def test_on_event_json_data(self, logger):
        """Test processing event with JSON data."""
        msg = String()
        msg.data = json.dumps({'command': 'spin', 'duration': 2})

        logger._on_event(msg, 'voice_command', '/saltybot/voice_command')

        assert len(logger.daily_events) > 0
        event = logger.daily_events[0]
        assert event.data['command'] == 'spin'

    def test_on_event_string_data(self, logger):
        """Test processing event with string data."""
        msg = String()
        msg.data = 'simple string message'

        logger._on_event(msg, 'error', '/saltybot/error')

        assert len(logger.daily_events) > 0
        event = logger.daily_events[0]
        assert 'message' in event.data


if __name__ == '__main__':
    pytest.main([__file__, '-v'])
