"""
Integration tests for feed route endpoints

Tests the /feed, /feed.rss, /feed.atom, /feed.json, and /feed.xml endpoints
including content negotiation.
"""

import pytest
from starpunk import create_app
from starpunk.notes import create_note


@pytest.fixture
def app(tmp_path):
    """Create and configure a test app instance"""
    test_data_dir = tmp_path / "data"
    test_data_dir.mkdir(parents=True, exist_ok=True)

    test_config = {
        "TESTING": True,
        "DATABASE_PATH": test_data_dir / "starpunk.db",
        "DATA_PATH": test_data_dir,
        "NOTES_PATH": test_data_dir / "notes",
        "SESSION_SECRET": "test-secret-key",
        "ADMIN_ME": "https://test.example.com",
        "SITE_URL": "https://example.com",
        "SITE_NAME": "Test Site",
        "SITE_DESCRIPTION": "Test Description",
        "AUTHOR_NAME": "Test Author",
        "DEV_MODE": False,
        "FEED_CACHE_SECONDS": 0,  # Disable caching for tests
        "FEED_MAX_ITEMS": 50,
    }

    app = create_app(config=test_config)

    # Create test notes
    with app.app_context():
        create_note(content='Test content 1', published=True, custom_slug='test-note-1')
        create_note(content='Test content 2', published=True, custom_slug='test-note-2')

    yield app


@pytest.fixture
def client(app):
    """Test client for making requests"""
    return app.test_client()


@pytest.fixture(autouse=True)
def clear_feed_cache():
    """Clear feed cache before each test"""
    from starpunk.routes import public
    public._feed_cache["notes"] = None
    public._feed_cache["timestamp"] = None
    yield
    # Clear again after test
    public._feed_cache["notes"] = None
    public._feed_cache["timestamp"] = None


class TestExplicitEndpoints:
    """Tests for explicit format endpoints"""

    def test_feed_rss_endpoint(self, client):
        """GET /feed.rss returns RSS feed"""
        response = client.get('/feed.rss')
        assert response.status_code == 200
        assert response.headers['Content-Type'] == 'application/rss+xml; charset=utf-8'
        # Check for XML declaration (quotes may be single or double)
        assert b'<?xml version=' in response.data
        assert b'encoding=' in response.data
        # Check for RSS element (version attribute may be at any position)
        assert b'<rss' in response.data
        assert b'version="2.0"' in response.data

    def test_feed_atom_endpoint(self, client):
        """GET /feed.atom returns ATOM feed"""
        response = client.get('/feed.atom')
        assert response.status_code == 200
        assert response.headers['Content-Type'] == 'application/atom+xml; charset=utf-8'
        # Check for XML declaration (don't assert quote style)
        assert b'<?xml version=' in response.data
        assert b'encoding=' in response.data
        assert b'<feed xmlns="http://www.w3.org/2005/Atom"' in response.data

    def test_feed_json_endpoint(self, client):
        """GET /feed.json returns JSON Feed"""
        response = client.get('/feed.json')
        assert response.status_code == 200
        # Check Content-Type starts with expected type (don't require charset)
        assert response.headers['Content-Type'].startswith('application/feed+json')
        # JSON Feed is streamed, so we need to collect all chunks
        data = b''.join(response.response)
        assert b'"version": "https://jsonfeed.org/version/1.1"' in data
        assert b'"title":' in data

    def test_feed_xml_legacy_endpoint(self, client):
        """GET /feed.xml returns RSS feed (backward compatibility)"""
        response = client.get('/feed.xml')
        assert response.status_code == 200
        assert response.headers['Content-Type'] == 'application/rss+xml; charset=utf-8'
        # Check for XML declaration (quotes may be single or double)
        assert b'<?xml version=' in response.data
        assert b'encoding=' in response.data
        # Check for RSS element (version attribute may be at any position)
        assert b'<rss' in response.data
        assert b'version="2.0"' in response.data


class TestContentNegotiation:
    """Tests for /feed content negotiation endpoint"""

    def test_accept_rss(self, client):
        """Accept: application/rss+xml returns RSS"""
        response = client.get('/feed', headers={'Accept': 'application/rss+xml'})
        assert response.status_code == 200
        assert response.headers['Content-Type'] == 'application/rss+xml; charset=utf-8'
        assert b'<rss' in response.data
        assert b'version="2.0"' in response.data

    def test_accept_atom(self, client):
        """Accept: application/atom+xml returns ATOM"""
        response = client.get('/feed', headers={'Accept': 'application/atom+xml'})
        assert response.status_code == 200
        assert response.headers['Content-Type'] == 'application/atom+xml; charset=utf-8'
        assert b'<feed xmlns="http://www.w3.org/2005/Atom"' in response.data

    def test_accept_json_feed(self, client):
        """Accept: application/feed+json returns JSON Feed"""
        response = client.get('/feed', headers={'Accept': 'application/feed+json'})
        assert response.status_code == 200
        assert response.headers['Content-Type'].startswith('application/feed+json')
        data = b''.join(response.response)
        assert b'"version": "https://jsonfeed.org/version/1.1"' in data

    def test_accept_json_generic(self, client):
        """Accept: application/json returns JSON Feed"""
        response = client.get('/feed', headers={'Accept': 'application/json'})
        assert response.status_code == 200
        assert response.headers['Content-Type'].startswith('application/feed+json')
        data = b''.join(response.response)
        assert b'"version": "https://jsonfeed.org/version/1.1"' in data

    def test_accept_wildcard(self, client):
        """Accept: */* returns RSS (default)"""
        response = client.get('/feed', headers={'Accept': '*/*'})
        assert response.status_code == 200
        assert response.headers['Content-Type'] == 'application/rss+xml; charset=utf-8'
        assert b'<rss' in response.data
        assert b'version="2.0"' in response.data

    def test_no_accept_header(self, client):
        """No Accept header defaults to RSS"""
        response = client.get('/feed')
        assert response.status_code == 200
        assert response.headers['Content-Type'] == 'application/rss+xml; charset=utf-8'
        assert b'<rss' in response.data
        assert b'version="2.0"' in response.data

    def test_quality_factor_atom_wins(self, client):
        """Higher quality factor wins"""
        response = client.get('/feed', headers={
            'Accept': 'application/atom+xml;q=0.9, application/rss+xml;q=0.5'
        })
        assert response.status_code == 200
        assert response.headers['Content-Type'] == 'application/atom+xml; charset=utf-8'

    def test_quality_factor_json_wins(self, client):
        """JSON with highest quality wins"""
        response = client.get('/feed', headers={
            'Accept': 'application/json;q=1.0, application/atom+xml;q=0.8'
        })
        assert response.status_code == 200
        assert response.headers['Content-Type'].startswith('application/feed+json')

    def test_browser_accept_header(self, client):
        """Browser-like Accept header returns RSS"""
        response = client.get('/feed', headers={
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
        })
        assert response.status_code == 200
        assert response.headers['Content-Type'] == 'application/rss+xml; charset=utf-8'

    def test_no_acceptable_format(self, client):
        """No acceptable format returns 406"""
        response = client.get('/feed', headers={'Accept': 'text/html'})
        assert response.status_code == 406
        assert response.headers['Content-Type'] == 'text/plain; charset=utf-8'
        assert 'X-Available-Formats' in response.headers
        assert 'application/rss+xml' in response.headers['X-Available-Formats']
        assert 'application/atom+xml' in response.headers['X-Available-Formats']
        assert 'application/feed+json' in response.headers['X-Available-Formats']
        assert b'Not Acceptable' in response.data


class TestCacheHeaders:
    """Tests for cache control headers"""

    def test_rss_cache_header(self, client):
        """RSS feed includes Cache-Control header"""
        response = client.get('/feed.rss')
        assert 'Cache-Control' in response.headers
        # FEED_CACHE_SECONDS is 0 in test config
        assert 'max-age=0' in response.headers['Cache-Control']

    def test_atom_cache_header(self, client):
        """ATOM feed includes Cache-Control header"""
        response = client.get('/feed.atom')
        assert 'Cache-Control' in response.headers
        assert 'max-age=0' in response.headers['Cache-Control']

    def test_json_cache_header(self, client):
        """JSON Feed includes Cache-Control header"""
        response = client.get('/feed.json')
        assert 'Cache-Control' in response.headers
        assert 'max-age=0' in response.headers['Cache-Control']


class TestFeedContent:
    """Tests for feed content correctness"""

    def test_rss_contains_notes(self, client):
        """RSS feed contains test notes"""
        response = client.get('/feed.rss')
        assert b'test-note-1' in response.data
        assert b'test-note-2' in response.data
        assert b'Test content 1' in response.data
        assert b'Test content 2' in response.data

    def test_atom_contains_notes(self, client):
        """ATOM feed contains test notes"""
        response = client.get('/feed.atom')
        assert b'test-note-1' in response.data
        assert b'test-note-2' in response.data
        assert b'Test content 1' in response.data
        assert b'Test content 2' in response.data

    def test_json_contains_notes(self, client):
        """JSON Feed contains test notes"""
        response = client.get('/feed.json')
        data = b''.join(response.response)
        assert b'test-note-1' in data
        assert b'test-note-2' in data
        assert b'Test content 1' in data
        assert b'Test content 2' in data


class TestBackwardCompatibility:
    """Tests for backward compatibility"""

    def test_feed_xml_same_as_feed_rss(self, client):
        """GET /feed.xml returns same content as /feed.rss"""
        rss_response = client.get('/feed.rss')
        xml_response = client.get('/feed.xml')

        assert rss_response.status_code == xml_response.status_code
        assert rss_response.headers['Content-Type'] == xml_response.headers['Content-Type']
        # Content should be identical
        assert rss_response.data == xml_response.data

    def test_feed_xml_contains_rss(self, client):
        """GET /feed.xml contains RSS XML"""
        response = client.get('/feed.xml')
        # Check for XML declaration (quotes may be single or double)
        assert b'<?xml version=' in response.data
        assert b'encoding=' in response.data
        # Check for RSS element (version attribute may be at any position)
        assert b'<rss' in response.data
        assert b'version="2.0"' in response.data
        assert b'</rss>' in response.data
