"""Tests for mediaController.py."""
import unittest
from unittest.mock import patch, MagicMock, PropertyMock, call


class TestMediaControllerRoutes(unittest.TestCase):
    """Test the media controller Flask routes."""

    def setUp(self):
        from mediaController import app
        self.app = app
        self.client = app.test_client()

    def test_index_route(self):
        response = self.client.get('/')
        self.assertIn(response.status_code, [200, 404])

    def test_execute_unknown_action_returns_400(self):
        """Bug fix: unknown actions should return 400, not crash."""
        response = self.client.post('/execute', data={'id': 'nonexistent_action'})
        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.data.decode(), 'unknown action')

    def test_execute_missing_id_returns_400(self):
        """Missing id parameter should return 400."""
        response = self.client.post('/execute', data={})
        self.assertEqual(response.status_code, 400)

    def test_execute_next_returns_ok(self):
        """Test that 'next' action returns ok and calls the method."""
        mock_fn = MagicMock()
        import mediaController
        original = mediaController.methods['next']
        mediaController.methods['next'] = (mock_fn, 'test_val')
        try:
            response = self.client.post('/execute', data={'id': 'next'})
            self.assertEqual(response.status_code, 200)
            self.assertEqual(response.data.decode(), 'ok')
            mock_fn.assert_called_once_with('test_val')
        finally:
            mediaController.methods['next'] = original

    def test_execute_prev_returns_ok(self):
        """Test that 'prev' action returns ok."""
        mock_fn = MagicMock()
        import mediaController
        original = mediaController.methods['prev']
        mediaController.methods['prev'] = (mock_fn, 'test_val')
        try:
            response = self.client.post('/execute', data={'id': 'prev'})
            self.assertEqual(response.status_code, 200)
            mock_fn.assert_called_once_with('test_val')
        finally:
            mediaController.methods['prev'] = original

    def test_execute_pause_returns_ok(self):
        """Test that 'pause' action returns ok."""
        mock_fn = MagicMock()
        import mediaController
        original = mediaController.methods['pause']
        mediaController.methods['pause'] = (mock_fn, 'test_val')
        try:
            response = self.client.post('/execute', data={'id': 'pause'})
            self.assertEqual(response.status_code, 200)
            mock_fn.assert_called_once_with('test_val')
        finally:
            mediaController.methods['pause'] = original

    def test_execute_runslides_returns_ok(self):
        """Test that 'runslides' action calls with True."""
        mock_fn = MagicMock()
        import mediaController
        original = mediaController.methods['runslides']
        mediaController.methods['runslides'] = (mock_fn, True)
        try:
            response = self.client.post('/execute', data={'id': 'runslides'})
            self.assertEqual(response.status_code, 200)
            mock_fn.assert_called_once_with(True)
        finally:
            mediaController.methods['runslides'] = original

    def test_execute_stopslides_returns_ok(self):
        """Test that 'stopslides' action calls with False."""
        mock_fn = MagicMock()
        import mediaController
        original = mediaController.methods['stopslides']
        mediaController.methods['stopslides'] = (mock_fn, False)
        try:
            response = self.client.post('/execute', data={'id': 'stopslides'})
            self.assertEqual(response.status_code, 200)
            mock_fn.assert_called_once_with(False)
        finally:
            mediaController.methods['stopslides'] = original


class TestOnPress(unittest.TestCase):
    """Test keyboard listener callback."""

    def test_escape_returns_false(self):
        """ESC key should return False to stop listener."""
        from mediaController import on_press
        import pynput
        result = on_press(pynput.keyboard.Key.esc)
        self.assertFalse(result)

    @patch('mediaController.slides')
    def test_key_8_triggers_run(self, mock_slides):
        """Key '8' should start slides."""
        from mediaController import on_press
        mock_key = MagicMock()
        mock_key.char = '8'
        on_press(mock_key)
        mock_slides.assert_called_once_with(True)

    @patch('mediaController.slides')
    def test_key_9_triggers_stop(self, mock_slides):
        """Key '9' should stop slides."""
        from mediaController import on_press
        mock_key = MagicMock()
        mock_key.char = '9'
        on_press(mock_key)
        mock_slides.assert_called_once_with(False)

    @patch('mediaController.slides')
    def test_other_key_no_action(self, mock_slides):
        """Other keys should not trigger slides."""
        from mediaController import on_press
        mock_key = MagicMock()
        mock_key.char = 'a'
        on_press(mock_key)
        mock_slides.assert_not_called()

    @patch('mediaController.slides')
    def test_special_key_handled_via_attribute_error(self, mock_slides):
        """Special keys (no .char) should be handled without crash."""
        from mediaController import on_press
        mock_key = MagicMock(spec=[])
        mock_key.name = 'ctrl'
        type(mock_key).char = PropertyMock(side_effect=AttributeError)
        on_press(mock_key)
        mock_slides.assert_not_called()


class TestSlides(unittest.TestCase):
    """Test the slides function."""

    @patch('mediaController.subprocess.run')
    def test_slides_run_true(self, mock_run):
        from mediaController import slides
        slides(True)
        mock_run.assert_called_once()
        call_args = mock_run.call_args
        self.assertIn('start', call_args[0][0])

    @patch('mediaController.subprocess.run')
    def test_slides_run_false(self, mock_run):
        from mediaController import slides
        slides(False)
        mock_run.assert_called_once()
        call_args = mock_run.call_args
        self.assertIn('stop', call_args[0][0])


if __name__ == '__main__':
    unittest.main()
