"""Tests for Flask routes and API endpoints."""

import json
import pytest
from unittest.mock import patch, MagicMock


@pytest.fixture(scope='session')
def app():
    """Create application for testing."""
    import app as app_module
    from routes import register_blueprints
    from utils.database import init_db

    app_module.app.config['TESTING'] = True

    # Initialize database for settings tests
    init_db()

    # Register blueprints only if not already registered (normally done in main())
    # Check if any blueprint is already registered to avoid re-registration
    if 'pager' not in app_module.app.blueprints:
        register_blueprints(app_module.app)

    return app_module.app


@pytest.fixture
def client(app):
    """Create test client."""
    return app.test_client()


class TestHealthEndpoint:
    """Tests for health check endpoint."""

    def test_health_check(self, client):
        """Test health endpoint returns expected data."""
        response = client.get('/health')
        assert response.status_code == 200

        data = json.loads(response.data)
        assert data['status'] == 'healthy'
        assert 'version' in data
        assert 'uptime_seconds' in data
        assert 'processes' in data
        assert 'data' in data

    def test_health_process_status(self, client):
        """Test health endpoint reports process status."""
        response = client.get('/health')
        data = json.loads(response.data)

        processes = data['processes']
        assert 'pager' in processes
        assert 'sensor' in processes
        assert 'adsb' in processes
        assert 'wifi' in processes
        assert 'bluetooth' in processes


class TestDevicesEndpoint:
    """Tests for devices endpoint."""

    def test_get_devices(self, client):
        """Test getting device list."""
        response = client.get('/devices')
        assert response.status_code == 200

        data = json.loads(response.data)
        assert isinstance(data, list)

    @patch('app.SDRFactory.detect_devices')
    def test_devices_returns_list(self, mock_detect, client):
        """Test devices endpoint returns list format."""
        mock_device = MagicMock()
        mock_device.to_dict.return_value = {
            'index': 0,
            'name': 'Test RTL-SDR',
            'sdr_type': 'rtlsdr'
        }
        mock_detect.return_value = [mock_device]

        response = client.get('/devices')
        data = json.loads(response.data)

        assert len(data) == 1
        assert data[0]['name'] == 'Test RTL-SDR'


class TestDependenciesEndpoint:
    """Tests for dependencies endpoint."""

    def test_get_dependencies(self, client):
        """Test getting dependency status."""
        response = client.get('/dependencies')
        assert response.status_code == 200

        data = json.loads(response.data)
        assert data['status'] == 'success'
        assert 'os' in data
        assert 'pkg_manager' in data
        assert 'modes' in data


class TestSettingsEndpoints:
    """Tests for settings API endpoints."""

    def test_get_settings(self, client):
        """Test getting all settings."""
        response = client.get('/settings')
        assert response.status_code == 200

        data = json.loads(response.data)
        assert data['status'] == 'success'
        assert 'settings' in data

    def test_save_settings(self, client):
        """Test saving settings."""
        response = client.post(
            '/settings',
            data=json.dumps({'test_key': 'test_value'}),
            content_type='application/json'
        )
        assert response.status_code == 200

        data = json.loads(response.data)
        assert data['status'] == 'success'
        assert 'test_key' in data['saved']

    def test_save_empty_settings(self, client):
        """Test saving empty settings returns error."""
        response = client.post(
            '/settings',
            data=json.dumps({}),
            content_type='application/json'
        )
        assert response.status_code == 400

    def test_get_single_setting(self, client):
        """Test getting a single setting."""
        # First save a setting
        client.post(
            '/settings',
            data=json.dumps({'my_setting': 'my_value'}),
            content_type='application/json'
        )

        # Then retrieve it
        response = client.get('/settings/my_setting')
        assert response.status_code == 200

        data = json.loads(response.data)
        assert data['status'] == 'success'
        assert data['value'] == 'my_value'

    def test_get_nonexistent_setting(self, client):
        """Test getting a setting that doesn't exist."""
        response = client.get('/settings/nonexistent_key_xyz')
        assert response.status_code == 404

    def test_update_setting(self, client):
        """Test updating a setting via PUT."""
        response = client.put(
            '/settings/update_test',
            data=json.dumps({'value': 'updated_value'}),
            content_type='application/json'
        )
        assert response.status_code == 200

        data = json.loads(response.data)
        assert data['status'] == 'success'
        assert data['value'] == 'updated_value'

    def test_delete_setting(self, client):
        """Test deleting a setting."""
        # First create a setting
        client.post(
            '/settings',
            data=json.dumps({'delete_me': 'value'}),
            content_type='application/json'
        )

        # Then delete it
        response = client.delete('/settings/delete_me')
        assert response.status_code == 200

        data = json.loads(response.data)
        assert data['status'] == 'success'
        assert data['deleted'] is True


class TestCorrelationEndpoints:
    """Tests for correlation API endpoints."""

    def test_get_correlations(self, client):
        """Test getting device correlations."""
        response = client.get('/correlation')
        assert response.status_code == 200

        data = json.loads(response.data)
        assert data['status'] == 'success'
        assert 'correlations' in data
        assert 'wifi_count' in data
        assert 'bt_count' in data

    def test_correlations_with_confidence_filter(self, client):
        """Test correlation endpoint respects confidence filter."""
        response = client.get('/correlation?min_confidence=0.8')
        assert response.status_code == 200

        data = json.loads(response.data)
        assert data['status'] == 'success'


class TestListeningPostEndpoints:
    """Tests for listening post endpoints."""

    def test_tools_check(self, client):
        """Test listening post tools availability check."""
        response = client.get('/listening/tools')
        assert response.status_code == 200

        data = json.loads(response.data)
        assert 'rtl_fm' in data
        assert 'available' in data

    def test_scanner_status(self, client):
        """Test scanner status endpoint."""
        response = client.get('/listening/scanner/status')
        assert response.status_code == 200

        data = json.loads(response.data)
        assert 'running' in data
        assert 'paused' in data
        assert 'current_freq' in data

    def test_presets(self, client):
        """Test scanner presets endpoint."""
        response = client.get('/listening/presets')
        assert response.status_code == 200

        data = json.loads(response.data)
        assert 'presets' in data
        assert len(data['presets']) > 0

        # Check preset structure
        preset = data['presets'][0]
        assert 'name' in preset
        assert 'start' in preset
        assert 'end' in preset
        assert 'mod' in preset

    def test_scanner_stop_when_not_running(self, client):
        """Test stopping scanner when not running."""
        response = client.post('/listening/scanner/stop')
        assert response.status_code == 200

        data = json.loads(response.data)
        assert data['status'] == 'stopped'

    def test_activity_log(self, client):
        """Test getting activity log."""
        response = client.get('/listening/scanner/log')
        assert response.status_code == 200

        data = json.loads(response.data)
        assert 'log' in data
        assert 'total' in data

    def test_scanner_skip_when_not_running(self, client):
        """Test skip signal when scanner not running returns error."""
        response = client.post('/listening/scanner/skip')
        assert response.status_code == 400

        data = json.loads(response.data)
        assert data['status'] == 'error'


class TestAudioEndpoints:
    """Tests for audio demodulation endpoints."""

    def test_audio_status(self, client):
        """Test audio status endpoint."""
        response = client.get('/listening/audio/status')
        assert response.status_code == 200

        data = json.loads(response.data)
        assert 'running' in data
        assert 'frequency' in data
        assert 'modulation' in data

    def test_audio_stop_when_not_running(self, client):
        """Test stopping audio when not running."""
        response = client.post('/listening/audio/stop')
        assert response.status_code == 200

        data = json.loads(response.data)
        assert data['status'] == 'stopped'

    def test_audio_start_missing_frequency(self, client):
        """Test starting audio without frequency returns error."""
        response = client.post(
            '/listening/audio/start',
            data=json.dumps({}),
            content_type='application/json'
        )
        assert response.status_code == 400

        data = json.loads(response.data)
        assert data['status'] == 'error'
        assert 'frequency' in data['message'].lower()

    def test_audio_start_invalid_modulation(self, client):
        """Test starting audio with invalid modulation returns error."""
        response = client.post(
            '/listening/audio/start',
            data=json.dumps({
                'frequency': 98.1,
                'modulation': 'invalid_mode'
            }),
            content_type='application/json'
        )
        assert response.status_code == 400

        data = json.loads(response.data)
        assert data['status'] == 'error'
        assert 'modulation' in data['message'].lower()

    def test_audio_stream_when_not_running(self, client):
        """Test audio stream when not running returns error."""
        response = client.get('/listening/audio/stream')
        assert response.status_code == 400

        data = json.loads(response.data)
        assert data['status'] == 'error'


class TestExportEndpoints:
    """Tests for data export endpoints."""

    def test_export_aircraft_json(self, client):
        """Test exporting aircraft data as JSON."""
        response = client.get('/export/aircraft?format=json')
        assert response.status_code == 200
        assert response.content_type == 'application/json'

    def test_export_aircraft_csv(self, client):
        """Test exporting aircraft data as CSV."""
        response = client.get('/export/aircraft?format=csv')
        assert response.status_code == 200
        assert 'text/csv' in response.content_type

    def test_export_wifi_json(self, client):
        """Test exporting WiFi data as JSON."""
        response = client.get('/export/wifi?format=json')
        assert response.status_code == 200
        assert response.content_type == 'application/json'

    def test_export_wifi_csv(self, client):
        """Test exporting WiFi data as CSV."""
        response = client.get('/export/wifi?format=csv')
        assert response.status_code == 200
        assert 'text/csv' in response.content_type

    def test_export_bluetooth_json(self, client):
        """Test exporting Bluetooth data as JSON."""
        response = client.get('/export/bluetooth?format=json')
        assert response.status_code == 200
        assert response.content_type == 'application/json'

    def test_export_bluetooth_csv(self, client):
        """Test exporting Bluetooth data as CSV."""
        response = client.get('/export/bluetooth?format=csv')
        assert response.status_code == 200
        assert 'text/csv' in response.content_type
