"""
Tests for admin routes (dashboard, note management)

Tests cover:
- Authentication requirement for all admin routes
- Dashboard rendering with note list
- Create note flow
- Edit note flow
- Delete note flow
- Logout functionality
"""

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


@pytest.fixture
def app(tmp_path):
    """Create test application"""
    # Create test-specific data directory
    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": "http://localhost:5000",
        "DEV_MODE": False,
    }
    app = create_app(config=test_config)
    yield app


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


@pytest.fixture
def authenticated_client(app, client):
    """Client with authenticated session"""
    with app.test_request_context():
        # Create a session for the test user
        session_token = create_session("https://test.example.com")

    # Set session cookie
    client.set_cookie("starpunk_session", session_token)
    return client


@pytest.fixture
def sample_notes(app):
    """Create sample notes"""
    with app.app_context():
        notes = []
        for i in range(3):
            note = create_note(
                content=f"# Admin Test Note {i}\n\nContent {i}.",
                published=(i != 1),  # Note 1 is draft
            )
            notes.append(note)
        return notes


class TestAuthenticationRequirement:
    """Test that all admin routes require authentication"""

    def test_dashboard_requires_auth(self, client):
        """Test /admin requires authentication"""
        response = client.get("/admin/", follow_redirects=False)
        assert response.status_code == 302
        assert "/auth/login" in response.location

    def test_new_note_form_requires_auth(self, client):
        """Test /admin/new requires authentication"""
        response = client.get("/admin/new", follow_redirects=False)
        assert response.status_code == 302

    def test_edit_note_form_requires_auth(self, client, sample_notes):
        """Test /admin/edit/<id> requires authentication"""
        with client.application.app_context():
            from starpunk.notes import list_notes

            notes = list_notes()
            note_id = notes[0].id

        response = client.get(f"/admin/edit/{note_id}", follow_redirects=False)
        assert response.status_code == 302

    def test_create_note_submit_requires_auth(self, client):
        """Test POST /admin/new requires authentication"""
        response = client.post(
            "/admin/new",
            data={"content": "Test content", "published": "on"},
            follow_redirects=False,
        )
        assert response.status_code == 302

    def test_update_note_submit_requires_auth(self, client, sample_notes):
        """Test POST /admin/edit/<id> requires authentication"""
        with client.application.app_context():
            from starpunk.notes import list_notes

            notes = list_notes()
            note_id = notes[0].id

        response = client.post(
            f"/admin/edit/{note_id}",
            data={"content": "Updated content", "published": "on"},
            follow_redirects=False,
        )
        assert response.status_code == 302

    def test_delete_note_requires_auth(self, client, sample_notes):
        """Test POST /admin/delete/<id> requires authentication"""
        with client.application.app_context():
            from starpunk.notes import list_notes

            notes = list_notes()
            note_id = notes[0].id

        response = client.post(
            f"/admin/delete/{note_id}", data={"confirm": "yes"}, follow_redirects=False
        )
        assert response.status_code == 302


class TestDashboard:
    """Test admin dashboard"""

    def test_dashboard_renders(self, authenticated_client):
        """Test dashboard renders for authenticated user"""
        response = authenticated_client.get("/admin/")
        assert response.status_code == 200
        assert b"Dashboard" in response.data or b"Admin" in response.data

    def test_dashboard_shows_all_notes(self, authenticated_client, sample_notes):
        """Test dashboard shows both published and draft notes"""
        response = authenticated_client.get("/admin/")
        assert response.status_code == 200

        # All notes should appear
        assert b"Admin Test Note 0" in response.data
        assert b"Admin Test Note 1" in response.data
        assert b"Admin Test Note 2" in response.data

    def test_dashboard_shows_note_status(self, authenticated_client, sample_notes):
        """Test dashboard shows published/draft status"""
        response = authenticated_client.get("/admin/")
        assert response.status_code == 200

        # Should indicate status
        assert (
            b"published" in response.data.lower() or b"draft" in response.data.lower()
        )

    def test_dashboard_has_new_note_button(self, authenticated_client):
        """Test dashboard has new note button"""
        response = authenticated_client.get("/admin/")
        assert response.status_code == 200
        assert b"/admin/new" in response.data or b"New Note" in response.data

    def test_dashboard_has_edit_links(self, authenticated_client, sample_notes):
        """Test dashboard has edit links for notes"""
        response = authenticated_client.get("/admin/")
        assert response.status_code == 200
        assert b"/admin/edit/" in response.data or b"Edit" in response.data

    def test_dashboard_has_delete_buttons(self, authenticated_client, sample_notes):
        """Test dashboard has delete buttons"""
        response = authenticated_client.get("/admin/")
        assert response.status_code == 200
        assert b"delete" in response.data.lower()

    def test_dashboard_has_logout_link(self, authenticated_client):
        """Test dashboard has logout link"""
        response = authenticated_client.get("/admin/")
        assert response.status_code == 200
        assert b"logout" in response.data.lower()

    def test_dashboard_shows_user_identity(self, authenticated_client):
        """Test dashboard shows logged in user identity"""
        response = authenticated_client.get("/admin/")
        assert response.status_code == 200
        assert b"test.example.com" in response.data


class TestCreateNote:
    """Test note creation flow"""

    def test_new_note_form_renders(self, authenticated_client):
        """Test new note form renders"""
        response = authenticated_client.get("/admin/new")
        assert response.status_code == 200
        assert b"<form" in response.data
        assert b"<textarea" in response.data

    def test_new_note_form_has_content_field(self, authenticated_client):
        """Test form has content textarea"""
        response = authenticated_client.get("/admin/new")
        assert response.status_code == 200
        assert b'name="content"' in response.data or b'id="content"' in response.data

    def test_new_note_form_has_published_checkbox(self, authenticated_client):
        """Test form has published checkbox"""
        response = authenticated_client.get("/admin/new")
        assert response.status_code == 200
        assert b'type="checkbox"' in response.data
        assert b'name="published"' in response.data or b"published" in response.data

    def test_create_note_success(self, authenticated_client):
        """Test creating a note successfully"""
        response = authenticated_client.post(
            "/admin/new",
            data={"content": "# New Test Note\n\nThis is a test.", "published": "on"},
            follow_redirects=True,
        )

        assert response.status_code == 200
        assert (
            b"created" in response.data.lower() or b"success" in response.data.lower()
        )

        # Verify note was created
        with authenticated_client.application.app_context():
            from starpunk.notes import list_notes

            notes = list_notes()
            assert any("New Test Note" in n.content for n in notes)

    def test_create_draft_note(self, authenticated_client):
        """Test creating a draft note (published unchecked)"""
        response = authenticated_client.post(
            "/admin/new",
            data={
                "content": "# Draft Note\n\nThis is a draft."
                # published checkbox not checked
            },
            follow_redirects=True,
        )

        assert response.status_code == 200

        # Verify draft was created
        with authenticated_client.application.app_context():
            from starpunk.notes import list_notes

            # Get all notes and filter for drafts
            all_notes = list_notes()
            drafts = [n for n in all_notes if not n.published]
            assert any("Draft Note" in n.content for n in drafts)

    def test_create_note_with_empty_content_fails(self, authenticated_client):
        """Test creating note with empty content fails"""
        response = authenticated_client.post(
            "/admin/new", data={"content": "", "published": "on"}, follow_redirects=True
        )

        # Should show error
        assert b"error" in response.data.lower() or b"required" in response.data.lower()

    def test_create_note_redirects_to_dashboard(self, authenticated_client):
        """Test successful create redirects to dashboard"""
        response = authenticated_client.post(
            "/admin/new",
            data={"content": "# Test\n\nContent.", "published": "on"},
            follow_redirects=False,
        )

        assert response.status_code == 302
        assert "/admin/" in response.location


class TestEditNote:
    """Test note editing flow"""

    def test_edit_note_form_renders(self, authenticated_client, sample_notes):
        """Test edit form renders"""
        with authenticated_client.application.app_context():
            from starpunk.notes import list_notes

            notes = list_notes()
            note_id = notes[0].id

        response = authenticated_client.get(f"/admin/edit/{note_id}")
        assert response.status_code == 200
        assert b"<form" in response.data
        assert b"<textarea" in response.data

    def test_edit_form_has_existing_content(self, authenticated_client, sample_notes):
        """Test edit form is pre-filled with existing content"""
        with authenticated_client.application.app_context():
            from starpunk.notes import list_notes

            notes = list_notes()
            note_id = notes[0].id

        response = authenticated_client.get(f"/admin/edit/{note_id}")
        assert response.status_code == 200
        assert b"Admin Test Note" in response.data

    def test_edit_form_has_delete_button(self, authenticated_client, sample_notes):
        """Test edit form has delete button"""
        with authenticated_client.application.app_context():
            from starpunk.notes import list_notes

            notes = list_notes()
            note_id = notes[0].id

        response = authenticated_client.get(f"/admin/edit/{note_id}")
        assert response.status_code == 200
        assert b"delete" in response.data.lower()

    def test_update_note_success(self, authenticated_client, sample_notes):
        """Test updating a note successfully"""
        with authenticated_client.application.app_context():
            from starpunk.notes import list_notes

            notes = list_notes()
            note_id = notes[0].id

        response = authenticated_client.post(
            f"/admin/edit/{note_id}",
            data={"content": "# Updated Note\n\nUpdated content.", "published": "on"},
            follow_redirects=True,
        )

        assert response.status_code == 200
        assert (
            b"updated" in response.data.lower() or b"success" in response.data.lower()
        )

        # Verify note was updated
        with authenticated_client.application.app_context():
            from starpunk.notes import get_note

            note = get_note(id=note_id)
            assert "Updated Note" in note.content

    def test_update_note_change_published_status(
        self, authenticated_client, sample_notes
    ):
        """Test changing published status"""
        with authenticated_client.application.app_context():
            from starpunk.notes import list_notes

            notes = list_notes(published_only=True)
            note_id = notes[0].id

        # Unpublish the note
        response = authenticated_client.post(
            f"/admin/edit/{note_id}",
            data={
                "content": "# Test\n\nContent."
                # published not checked
            },
            follow_redirects=True,
        )

        assert response.status_code == 200

        # Verify status changed
        with authenticated_client.application.app_context():
            from starpunk.notes import get_note

            note = get_note(id=note_id)
            assert not note.published

    def test_edit_nonexistent_note_404(self, authenticated_client):
        """Test editing nonexistent note returns 404"""
        response = authenticated_client.get("/admin/edit/99999")
        assert response.status_code == 404

    def test_update_nonexistent_note_404(self, authenticated_client):
        """Test updating nonexistent note returns 404"""
        response = authenticated_client.post(
            "/admin/edit/99999", data={"content": "Test", "published": "on"}
        )
        assert response.status_code == 404


class TestDeleteNote:
    """Test note deletion"""

    def test_delete_note_with_confirmation(self, authenticated_client, sample_notes):
        """Test deleting note with confirmation"""
        with authenticated_client.application.app_context():
            from starpunk.notes import list_notes

            notes = list_notes()
            note_id = notes[0].id

        response = authenticated_client.post(
            f"/admin/delete/{note_id}", data={"confirm": "yes"}, follow_redirects=True
        )

        assert response.status_code == 200
        assert (
            b"deleted" in response.data.lower() or b"success" in response.data.lower()
        )

        # Verify note was deleted
        with authenticated_client.application.app_context():
            from starpunk.notes import get_note

            note = get_note(id=note_id)
            assert note is None or note.deleted_at is not None

    def test_delete_without_confirmation_cancels(
        self, authenticated_client, sample_notes
    ):
        """Test deleting without confirmation cancels operation"""
        with authenticated_client.application.app_context():
            from starpunk.notes import list_notes

            notes = list_notes()
            note_id = notes[0].id

        response = authenticated_client.post(
            f"/admin/delete/{note_id}", data={"confirm": "no"}, follow_redirects=True
        )

        assert response.status_code == 200
        assert (
            b"cancelled" in response.data.lower() or b"cancel" in response.data.lower()
        )

        # Verify note still exists
        with authenticated_client.application.app_context():
            from starpunk.notes import get_note

            note = get_note(id=note_id)
            assert note is not None
            assert note.deleted_at is None

    def test_delete_nonexistent_note_shows_error(self, authenticated_client):
        """Test deleting nonexistent note returns 404"""
        response = authenticated_client.post(
            "/admin/delete/99999", data={"confirm": "yes"}
        )
        assert response.status_code == 404

    def test_delete_redirects_to_dashboard(self, authenticated_client, sample_notes):
        """Test delete redirects to dashboard"""
        with authenticated_client.application.app_context():
            from starpunk.notes import list_notes

            notes = list_notes()
            note_id = notes[0].id

        response = authenticated_client.post(
            f"/admin/delete/{note_id}", data={"confirm": "yes"}, follow_redirects=False
        )

        assert response.status_code == 302
        assert "/admin/" in response.location
