"""
Tests for JSON Feed generation module

Tests cover:
- JSON Feed generation with various note counts
- RFC 3339 date formatting
- Feed structure and required fields
- Entry ordering (newest first)
- JSON validity
"""

import pytest
from datetime import datetime, timezone
import json
import time

from starpunk import create_app
from starpunk.feeds.json_feed import generate_json_feed, generate_json_feed_streaming
from starpunk.notes import create_note, list_notes
from tests.helpers.feed_ordering import assert_feed_newest_first


@pytest.fixture
def app(tmp_path):
    """Create test application"""
    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 Blog",
        "SITE_DESCRIPTION": "A test blog",
        "DEV_MODE": False,
    }
    app = create_app(config=test_config)
    yield app


@pytest.fixture
def sample_notes(app):
    """Create sample published notes"""
    with app.app_context():
        notes = []
        for i in range(5):
            note = create_note(
                content=f"# Test Note {i}\n\nThis is test content for note {i}.",
                published=True,
            )
            notes.append(note)
            time.sleep(0.01)  # Ensure distinct timestamps
        return list_notes(published_only=True, limit=10)


class TestGenerateJsonFeed:
    """Test generate_json_feed() function"""

    def test_generate_json_feed_basic(self, app, sample_notes):
        """Test basic JSON Feed generation with notes"""
        with app.app_context():
            feed_json = generate_json_feed(
                site_url="https://example.com",
                site_name="Test Blog",
                site_description="A test blog",
                notes=sample_notes,
            )

            # Should return JSON string
            assert isinstance(feed_json, str)

            # Parse JSON to verify structure
            feed = json.loads(feed_json)

            # Check required fields
            assert feed["version"] == "https://jsonfeed.org/version/1.1"
            assert feed["title"] == "Test Blog"
            assert "items" in feed
            assert isinstance(feed["items"], list)

            # Check items (should have 5 items)
            assert len(feed["items"]) == 5

    def test_generate_json_feed_empty(self, app):
        """Test JSON Feed generation with no notes"""
        with app.app_context():
            feed_json = generate_json_feed(
                site_url="https://example.com",
                site_name="Test Blog",
                site_description="A test blog",
                notes=[],
            )

            # Should still generate valid JSON
            feed = json.loads(feed_json)
            assert feed["items"] == []

    def test_generate_json_feed_respects_limit(self, app, sample_notes):
        """Test JSON Feed respects item limit"""
        with app.app_context():
            feed_json = generate_json_feed(
                site_url="https://example.com",
                site_name="Test Blog",
                site_description="A test blog",
                notes=sample_notes,
                limit=3,
            )

            feed = json.loads(feed_json)

            # Should only have 3 items (respecting limit)
            assert len(feed["items"]) == 3

    def test_generate_json_feed_newest_first(self, app):
        """Test JSON Feed displays notes in newest-first order"""
        with app.app_context():
            # Create notes with distinct timestamps
            for i in range(3):
                create_note(
                    content=f"# Note {i}\n\nContent {i}.",
                    published=True,
                )
                time.sleep(0.01)

            # Get notes from database (should be DESC = newest first)
            notes = list_notes(published_only=True, limit=10)

            # Generate feed
            feed_json = generate_json_feed(
                site_url="https://example.com",
                site_name="Test Blog",
                site_description="A test blog",
                notes=notes,
            )

            # Use shared helper to verify ordering
            assert_feed_newest_first(feed_json, format_type='json', expected_count=3)

            # Also verify manually with JSON parsing
            feed = json.loads(feed_json)
            items = feed["items"]

            # First item should be newest (Note 2)
            # Last item should be oldest (Note 0)
            assert "Note 2" in items[0]["title"]
            assert "Note 0" in items[-1]["title"]

    def test_generate_json_feed_requires_site_url(self):
        """Test JSON Feed generation requires site_url"""
        with pytest.raises(ValueError, match="site_url is required"):
            generate_json_feed(
                site_url="",
                site_name="Test Blog",
                site_description="A test blog",
                notes=[],
            )

    def test_generate_json_feed_requires_site_name(self):
        """Test JSON Feed generation requires site_name"""
        with pytest.raises(ValueError, match="site_name is required"):
            generate_json_feed(
                site_url="https://example.com",
                site_name="",
                site_description="A test blog",
                notes=[],
            )

    def test_generate_json_feed_item_structure(self, app, sample_notes):
        """Test individual JSON Feed item has all required fields"""
        with app.app_context():
            feed_json = generate_json_feed(
                site_url="https://example.com",
                site_name="Test Blog",
                site_description="A test blog",
                notes=sample_notes[:1],
            )

            feed = json.loads(feed_json)
            item = feed["items"][0]

            # Check required item fields
            assert "id" in item
            assert "url" in item
            assert "title" in item
            assert "date_published" in item

            # Check either content_html or content_text is present
            assert "content_html" in item or "content_text" in item

    def test_generate_json_feed_html_content(self, app):
        """Test JSON Feed includes HTML content"""
        with app.app_context():
            note = create_note(
                content="# Test\n\nThis is **bold** and *italic*.",
                published=True,
            )

            feed_json = generate_json_feed(
                site_url="https://example.com",
                site_name="Test Blog",
                site_description="A test blog",
                notes=[note],
            )

            feed = json.loads(feed_json)
            item = feed["items"][0]

            # Should have content_html
            assert "content_html" in item
            content = item["content_html"]

            # Should contain HTML tags
            assert "<strong>" in content or "<em>" in content

    def test_generate_json_feed_starpunk_extension(self, app, sample_notes):
        """Test JSON Feed includes StarPunk custom extension"""
        with app.app_context():
            feed_json = generate_json_feed(
                site_url="https://example.com",
                site_name="Test Blog",
                site_description="A test blog",
                notes=sample_notes[:1],
            )

            feed = json.loads(feed_json)
            item = feed["items"][0]

            # Should have _starpunk extension
            assert "_starpunk" in item
            assert "permalink_path" in item["_starpunk"]
            assert "word_count" in item["_starpunk"]

    def test_generate_json_feed_date_format(self, app, sample_notes):
        """Test JSON Feed uses RFC 3339 date format"""
        with app.app_context():
            feed_json = generate_json_feed(
                site_url="https://example.com",
                site_name="Test Blog",
                site_description="A test blog",
                notes=sample_notes[:1],
            )

            feed = json.loads(feed_json)
            item = feed["items"][0]

            # date_published should be in RFC 3339 format
            date_str = item["date_published"]

            # Should end with 'Z' for UTC or have timezone offset
            assert date_str.endswith("Z") or "+" in date_str or "-" in date_str[-6:]

            # Should be parseable as ISO 8601
            parsed = datetime.fromisoformat(date_str.replace("Z", "+00:00"))
            assert parsed.tzinfo is not None


class TestGenerateJsonFeedStreaming:
    """Test generate_json_feed_streaming() function"""

    def test_generate_json_feed_streaming_basic(self, app, sample_notes):
        """Test streaming JSON Feed generation"""
        with app.app_context():
            generator = generate_json_feed_streaming(
                site_url="https://example.com",
                site_name="Test Blog",
                site_description="A test blog",
                notes=sample_notes,
            )

            # Collect all chunks
            chunks = list(generator)
            assert len(chunks) > 0

            # Join and verify valid JSON
            feed_json = ''.join(chunks)
            feed = json.loads(feed_json)

            assert len(feed["items"]) == 5

    def test_generate_json_feed_streaming_yields_chunks(self, app, sample_notes):
        """Test streaming yields multiple chunks"""
        with app.app_context():
            generator = generate_json_feed_streaming(
                site_url="https://example.com",
                site_name="Test Blog",
                site_description="A test blog",
                notes=sample_notes,
                limit=3,
            )

            chunks = list(generator)

            # Should have multiple chunks (at least opening + items + closing)
            assert len(chunks) >= 3

    def test_generate_json_feed_streaming_valid_json(self, app, sample_notes):
        """Test streaming produces valid JSON"""
        with app.app_context():
            generator = generate_json_feed_streaming(
                site_url="https://example.com",
                site_name="Test Blog",
                site_description="A test blog",
                notes=sample_notes,
            )

            feed_json = ''.join(generator)

            # Should be valid JSON
            feed = json.loads(feed_json)
            assert feed["version"] == "https://jsonfeed.org/version/1.1"
