"""
Tests for ATOM feed generation module

Tests cover:
- ATOM feed generation with various note counts
- RFC 3339 date formatting
- Feed structure and required elements
- Entry ordering (newest first)
- XML escaping
"""

import pytest
from datetime import datetime, timezone
from xml.etree import ElementTree as ET
import time

from starpunk import create_app
from starpunk.feeds.atom import generate_atom, generate_atom_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 TestGenerateAtom:
    """Test generate_atom() function"""

    def test_generate_atom_basic(self, app, sample_notes):
        """Test basic ATOM feed generation with notes"""
        with app.app_context():
            feed_xml = generate_atom(
                site_url="https://example.com",
                site_name="Test Blog",
                site_description="A test blog",
                notes=sample_notes,
            )

            # Should return XML string
            assert isinstance(feed_xml, str)
            assert feed_xml.startswith("<?xml")

            # Parse XML to verify structure
            root = ET.fromstring(feed_xml)

            # Check namespace
            assert root.tag == "{http://www.w3.org/2005/Atom}feed"

            # Find required feed elements (with namespace)
            ns = {'atom': 'http://www.w3.org/2005/Atom'}
            title = root.find('atom:title', ns)
            assert title is not None
            assert title.text == "Test Blog"

            id_elem = root.find('atom:id', ns)
            assert id_elem is not None

            updated = root.find('atom:updated', ns)
            assert updated is not None

            # Check entries (should have 5 entries)
            entries = root.findall('atom:entry', ns)
            assert len(entries) == 5

    def test_generate_atom_empty(self, app):
        """Test ATOM feed generation with no notes"""
        with app.app_context():
            feed_xml = generate_atom(
                site_url="https://example.com",
                site_name="Test Blog",
                site_description="A test blog",
                notes=[],
            )

            # Should still generate valid XML
            assert isinstance(feed_xml, str)
            root = ET.fromstring(feed_xml)

            ns = {'atom': 'http://www.w3.org/2005/Atom'}
            entries = root.findall('atom:entry', ns)
            assert len(entries) == 0

    def test_generate_atom_respects_limit(self, app, sample_notes):
        """Test ATOM feed respects entry limit"""
        with app.app_context():
            feed_xml = generate_atom(
                site_url="https://example.com",
                site_name="Test Blog",
                site_description="A test blog",
                notes=sample_notes,
                limit=3,
            )

            root = ET.fromstring(feed_xml)
            ns = {'atom': 'http://www.w3.org/2005/Atom'}
            entries = root.findall('atom:entry', ns)

            # Should only have 3 entries (respecting limit)
            assert len(entries) == 3

    def test_generate_atom_newest_first(self, app):
        """Test ATOM 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_xml = generate_atom(
                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_xml, format_type='atom', expected_count=3)

            # Also verify manually with XML parsing
            root = ET.fromstring(feed_xml)
            ns = {'atom': 'http://www.w3.org/2005/Atom'}
            entries = root.findall('atom:entry', ns)

            # First entry should be newest (Note 2)
            # Last entry should be oldest (Note 0)
            first_title = entries[0].find('atom:title', ns).text
            last_title = entries[-1].find('atom:title', ns).text

            assert "Note 2" in first_title
            assert "Note 0" in last_title

    def test_generate_atom_requires_site_url(self):
        """Test ATOM feed generation requires site_url"""
        with pytest.raises(ValueError, match="site_url is required"):
            generate_atom(
                site_url="",
                site_name="Test Blog",
                site_description="A test blog",
                notes=[],
            )

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

    def test_generate_atom_entry_structure(self, app, sample_notes):
        """Test individual ATOM entry has all required elements"""
        with app.app_context():
            feed_xml = generate_atom(
                site_url="https://example.com",
                site_name="Test Blog",
                site_description="A test blog",
                notes=sample_notes[:1],
            )

            root = ET.fromstring(feed_xml)
            ns = {'atom': 'http://www.w3.org/2005/Atom'}
            entry = root.find('atom:entry', ns)

            # Check required entry elements
            assert entry.find('atom:id', ns) is not None
            assert entry.find('atom:title', ns) is not None
            assert entry.find('atom:updated', ns) is not None
            assert entry.find('atom:published', ns) is not None
            assert entry.find('atom:content', ns) is not None
            assert entry.find('atom:link', ns) is not None

    def test_generate_atom_html_content(self, app):
        """Test ATOM feed includes HTML content properly escaped"""
        with app.app_context():
            note = create_note(
                content="# Test\n\nThis is **bold** and *italic*.",
                published=True,
            )

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

            root = ET.fromstring(feed_xml)
            ns = {'atom': 'http://www.w3.org/2005/Atom'}
            entry = root.find('atom:entry', ns)
            content = entry.find('atom:content', ns)

            # Should have type="html"
            assert content.get('type') == 'html'

            # Content should contain escaped HTML
            content_text = content.text
            assert "&lt;" in content_text or "<strong>" in content_text

    def test_generate_atom_xml_escaping(self, app):
        """Test ATOM feed escapes special XML characters"""
        with app.app_context():
            note = create_note(
                content="# Test & Special <Characters>\n\nContent with 'quotes' and \"doubles\".",
                published=True,
            )

            feed_xml = generate_atom(
                site_url="https://example.com",
                site_name="Test Blog & More",
                site_description="A test <blog>",
                notes=[note],
            )

            # Should produce valid XML (no parse errors)
            root = ET.fromstring(feed_xml)
            assert root is not None

            # Check title is properly escaped in XML
            ns = {'atom': 'http://www.w3.org/2005/Atom'}
            title = root.find('atom:title', ns)
            assert title.text == "Test Blog & More"


class TestGenerateAtomStreaming:
    """Test generate_atom_streaming() function"""

    def test_generate_atom_streaming_basic(self, app, sample_notes):
        """Test streaming ATOM feed generation"""
        with app.app_context():
            generator = generate_atom_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 XML
            feed_xml = ''.join(chunks)
            root = ET.fromstring(feed_xml)

            ns = {'atom': 'http://www.w3.org/2005/Atom'}
            entries = root.findall('atom:entry', ns)
            assert len(entries) == 5

    def test_generate_atom_streaming_yields_chunks(self, app, sample_notes):
        """Test streaming yields multiple chunks"""
        with app.app_context():
            generator = generate_atom_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 XML declaration + feed + entries + closing)
            assert len(chunks) >= 4
