"""
Tests for Micropub endpoint

Tests the /micropub endpoint for creating posts via IndieWeb clients.
Covers both form-encoded and JSON requests, authentication, and error handling.

Note: After Phase 4 (ADR-030), StarPunk no longer issues tokens. Tests mock
external token verification responses.
"""

import pytest
from unittest.mock import patch
from starpunk.notes import get_note


# Mock token verification responses

@pytest.fixture
def mock_valid_token():
    """Mock response from external token verification (valid token)"""
    def verify_token(token):
        if token == "valid_token":
            return {
                "me": "https://user.example",
                "client_id": "https://client.example",
                "scope": "create"
            }
        return None
    return verify_token


@pytest.fixture
def mock_invalid_token():
    """Mock response from external token verification (invalid token)"""
    def verify_token(token):
        return None
    return verify_token


@pytest.fixture
def mock_read_only_token():
    """Mock response for token without create scope"""
    def verify_token(token):
        if token == "read_only_token":
            return {
                "me": "https://user.example",
                "client_id": "https://client.example",
                "scope": "read"
            }
        return None
    return verify_token


# Authentication Tests


def test_micropub_no_token(client):
    """Test Micropub endpoint rejects requests without token"""
    response = client.post('/micropub', data={
        'h': 'entry',
        'content': 'Test post'
    })

    assert response.status_code == 401
    data = response.get_json()
    assert data['error'] == 'unauthorized'
    assert 'No access token' in data['error_description']


def test_micropub_invalid_token(client, mock_invalid_token):
    """Test Micropub endpoint rejects invalid tokens"""
    with patch('starpunk.routes.micropub.verify_external_token', mock_invalid_token):
        response = client.post(
            '/micropub',
            data={'h': 'entry', 'content': 'Test post'},
            headers={'Authorization': 'Bearer invalid_token'}
        )

        assert response.status_code == 401
        data = response.get_json()
        assert data['error'] == 'unauthorized'
        assert 'Invalid or expired' in data['error_description']


def test_micropub_insufficient_scope(client, mock_read_only_token):
    """Test Micropub endpoint rejects token without create scope"""
    with patch('starpunk.routes.micropub.verify_external_token', mock_read_only_token):
        response = client.post(
            '/micropub',
            data={'h': 'entry', 'content': 'Test post'},
            headers={'Authorization': 'Bearer read_only_token'}
        )

        assert response.status_code == 403
        data = response.get_json()
        assert data['error'] == 'insufficient_scope'


# Create Post Tests


def test_micropub_create_note_form(client, app, mock_valid_token):
    """Test creating a note via form-encoded request"""
    with patch('starpunk.routes.micropub.verify_external_token', mock_valid_token):
        response = client.post(
            '/micropub',
            data={
                'h': 'entry',
                'content': 'This is a test note from Micropub',
            },
            headers={'Authorization': 'Bearer valid_token'}
        )

        assert response.status_code == 201
        assert 'Location' in response.headers

        # Verify note was created
        location = response.headers['Location']
        slug = location.split('/')[-1]
        with app.app_context():
            note = get_note(slug)
            assert note is not None
            assert note.content == 'This is a test note from Micropub'
            assert note.published is True


def test_micropub_create_note_json(client, app, mock_valid_token):
    """Test creating a note via JSON request"""
    with patch('starpunk.routes.micropub.verify_external_token', mock_valid_token):
        response = client.post(
            '/micropub',
            json={
                'type': ['h-entry'],
                'properties': {
                    'content': ['JSON test note']
                }
            },
            headers={'Authorization': 'Bearer valid_token'}
        )

        assert response.status_code == 201
        assert 'Location' in response.headers

        location = response.headers['Location']
        slug = location.split('/')[-1]
        with app.app_context():
            note = get_note(slug)
            assert note.content == 'JSON test note'


def test_micropub_create_with_name(client, app, mock_valid_token):
    """Test creating a note with a title (name property)"""
    with patch('starpunk.routes.micropub.verify_external_token', mock_valid_token):
        response = client.post(
            '/micropub',
            json={
                'type': ['h-entry'],
                'properties': {
                    'name': ['My Test Title'],
                    'content': ['Content goes here']
                }
            },
            headers={'Authorization': 'Bearer valid_token'}
        )

        # Verify note was created successfully
        assert response.status_code == 201
        assert 'Location' in response.headers


def test_micropub_create_with_categories(client, app, mock_valid_token):
    """Test creating a note with tags (category property)"""
    with patch('starpunk.routes.micropub.verify_external_token', mock_valid_token):
        response = client.post(
            '/micropub',
            json={
                'type': ['h-entry'],
                'properties': {
                    'content': ['Tagged post'],
                    'category': ['test', 'micropub', 'indieweb']
                }
            },
            headers={'Authorization': 'Bearer valid_token'}
        )

        # Verify note was created successfully
        assert response.status_code == 201
        assert 'Location' in response.headers


def test_micropub_create_with_custom_slug_form(client, app, mock_valid_token):
    """Test creating a note with custom slug via form-encoded request"""
    with patch('starpunk.routes.micropub.verify_external_token', mock_valid_token):
        response = client.post(
            '/micropub',
            data={
                'h': 'entry',
                'content': 'This is a test for custom slugs',
                'mp-slug': 'my-custom-slug'
            },
            headers={'Authorization': 'Bearer valid_token'}
        )

        assert response.status_code == 201
        assert 'Location' in response.headers

        # Verify the custom slug was used
        location = response.headers['Location']
        assert location.endswith('/notes/my-custom-slug')

        # Verify note exists with the custom slug
        with app.app_context():
            note = get_note('my-custom-slug')
            assert note is not None
            assert note.slug == 'my-custom-slug'
            assert note.content == 'This is a test for custom slugs'


def test_micropub_create_with_custom_slug_json(client, app, mock_valid_token):
    """Test creating a note with custom slug via JSON request"""
    with patch('starpunk.routes.micropub.verify_external_token', mock_valid_token):
        response = client.post(
            '/micropub',
            json={
                'type': ['h-entry'],
                'properties': {
                    'content': ['JSON test with custom slug']
                },
                'mp-slug': 'json-custom-slug'
            },
            headers={'Authorization': 'Bearer valid_token'}
        )

        assert response.status_code == 201
        assert 'Location' in response.headers

        # Verify the custom slug was used
        location = response.headers['Location']
        assert location.endswith('/notes/json-custom-slug')

        # Verify note exists with the custom slug
        with app.app_context():
            note = get_note('json-custom-slug')
            assert note is not None
            assert note.slug == 'json-custom-slug'
            assert note.content == 'JSON test with custom slug'


# Query Tests


def test_micropub_query_config(client, mock_valid_token):
    """Test q=config endpoint"""
    with patch('starpunk.routes.micropub.verify_external_token', mock_valid_token):
        response = client.get(
            '/micropub?q=config',
            headers={'Authorization': 'Bearer valid_token'}
        )

        assert response.status_code == 200
        data = response.get_json()
        # Config endpoint returns server capabilities
        # Check that it's a valid config response
        assert isinstance(data, dict)


def test_micropub_query_source(client, mock_valid_token):
    """Test q=source endpoint"""
    with patch('starpunk.routes.micropub.verify_external_token', mock_valid_token):
        # First create a note
        create_response = client.post(
            '/micropub',
            json={
                'type': ['h-entry'],
                'properties': {
                    'content': ['Source test']
                }
            },
            headers={'Authorization': 'Bearer valid_token'}
        )
        location = create_response.headers['Location']

        # Query for source
        response = client.get(
            f'/micropub?q=source&url={location}',
            headers={'Authorization': 'Bearer valid_token'}
        )

        assert response.status_code == 200
        data = response.get_json()
        assert 'properties' in data
        assert data['properties']['content'][0] == 'Source test'


# Error Handling Tests


def test_micropub_missing_content(client, mock_valid_token):
    """Test creating note without content fails"""
    with patch('starpunk.routes.micropub.verify_external_token', mock_valid_token):
        response = client.post(
            '/micropub',
            json={
                'type': ['h-entry'],
                'properties': {}
            },
            headers={'Authorization': 'Bearer valid_token'}
        )

        assert response.status_code == 400
        data = response.get_json()
        assert data['error'] == 'invalid_request'


def test_micropub_unsupported_action(client, mock_valid_token):
    """Test unsupported actions (update, delete) return error"""
    with patch('starpunk.routes.micropub.verify_external_token', mock_valid_token):
        # Test update
        response = client.post(
            '/micropub',
            json={
                'action': 'update',
                'url': 'https://example.com/note/123'
            },
            headers={'Authorization': 'Bearer valid_token'}
        )

        assert response.status_code == 400
        data = response.get_json()
        assert 'not supported' in data['error_description']

        # Test delete
        response = client.post(
            '/micropub',
            json={
                'action': 'delete',
                'url': 'https://example.com/note/123'
            },
            headers={'Authorization': 'Bearer valid_token'}
        )

        assert response.status_code == 400
        data = response.get_json()
        assert 'not supported' in data['error_description']
