"""Tests for the Profile screen: profile info, stats, edit, link, feedback."""

import pytest
from unittest.mock import MagicMock

from textual.widgets import Button, Input, Select, Static

from skilark_cli.tui.app import SkilarkApp


MOCK_PROFILE = {
    "current_role": "swe",
    "primary_language": "Python",
    "career_direction": "stay_sharp",
    "topics": ["python", "dsa", "kubernetes"],
}

MOCK_STATS = {
    "streak": 12,
    "total_completed": 47,
    "week_completed": 8,
    "week_goal": 10,
    "topic_counts": {"python": 18, "kubernetes": 9, "dsa": 6},
    "last_session": {"date": "2026-03-06", "topic": "python", "title": "List Comprehension"},
}


def _make_client():
    client = MagicMock()
    client.get_profile.return_value = MOCK_PROFILE
    client.get_stats.return_value = MOCK_STATS
    client.create_link_code.return_value = {
        "code": "ABC123",
        "expires_at": "2026-03-07T12:10:00Z",
    }
    client.send_feedback.return_value = None
    client.update_profile.return_value = MOCK_PROFILE
    return client


@pytest.fixture
def profile_app():
    client = _make_client()
    app = SkilarkApp(client=client)
    app._active_tab = 3  # Start directly on Profile
    return app


# --- AC1: Profile info displayed ---


class TestProfileInfo:
    async def test_shows_role(self, profile_app):
        """AC1: Profile screen displays the user's role."""
        async with profile_app.run_test() as pilot:
            await pilot.pause(delay=0.5)
            role = profile_app.query_one("#profile-val-role", Static)
            assert "Software Engineer" in str(role.content)

    async def test_shows_language(self, profile_app):
        """AC1: Profile screen displays the user's language."""
        async with profile_app.run_test() as pilot:
            await pilot.pause(delay=0.5)
            lang = profile_app.query_one("#profile-val-language", Static)
            assert "Python" in str(lang.content)

    async def test_shows_topics(self, profile_app):
        """AC1: Profile screen displays the user's topics."""
        async with profile_app.run_test() as pilot:
            await pilot.pause(delay=0.5)
            topics = profile_app.query_one("#profile-val-topics", Static)
            text = str(topics.content).lower()
            assert "python" in text
            assert "dsa" in text

    async def test_shows_direction(self, profile_app):
        """AC1: Profile screen displays the user's direction."""
        async with profile_app.run_test() as pilot:
            await pilot.pause(delay=0.5)
            direction = profile_app.query_one("#profile-val-direction", Static)
            assert "Stay sharp" in str(direction.content)


# --- AC2: Stats displayed ---


class TestProfileStats:
    async def test_shows_streak(self, profile_app):
        """AC2: Profile screen displays streak count."""
        async with profile_app.run_test() as pilot:
            await pilot.pause(delay=0.5)
            stats = profile_app.query_one("#profile-stats", Static)
            assert "12" in str(stats.content)

    async def test_shows_weekly_progress(self, profile_app):
        """AC2: Profile screen shows weekly progress."""
        async with profile_app.run_test() as pilot:
            await pilot.pause(delay=0.5)
            stats = profile_app.query_one("#profile-stats", Static)
            text = str(stats.content)
            assert "8" in text and "10" in text

    async def test_shows_topic_bars(self, profile_app):
        """AC2: Profile screen shows per-topic breakdown."""
        async with profile_app.run_test() as pilot:
            await pilot.pause(delay=0.5)
            stats = profile_app.query_one("#profile-stats", Static)
            text = str(stats.content).lower()
            assert "python" in text
            assert "18" in str(stats.content)


# --- AC3 + AC4: Edit mode (inline) ---


class TestProfileEdit:
    async def test_edit_mode_shows_inline_widgets(self, profile_app):
        """AC3: Pressing 'e' shows Select/Input widgets inline, hides display values."""
        async with profile_app.run_test() as pilot:
            await pilot.pause(delay=0.5)
            await pilot.press("e")
            await pilot.pause()
            # Edit widgets visible
            assert profile_app.query_one("#profile-edit-role", Select).display is True
            assert profile_app.query_one("#profile-edit-language", Input).display is True
            assert profile_app.query_one("#profile-edit-direction", Select).display is True
            assert profile_app.query_one("#profile-edit-save", Button).display is True
            # Display values hidden
            assert profile_app.query_one("#profile-val-role", Static).display is False
            assert profile_app.query_one("#profile-val-language", Static).display is False
            assert profile_app.query_one("#profile-val-direction", Static).display is False

    async def test_edit_prefills_current_values(self, profile_app):
        """AC3: Edit widgets are pre-filled with current profile values."""
        async with profile_app.run_test() as pilot:
            await pilot.pause(delay=0.5)
            await pilot.press("e")
            await pilot.pause()
            assert profile_app.query_one("#profile-edit-role", Select).value == "swe"
            assert profile_app.query_one("#profile-edit-language", Input).value == "Python"
            assert profile_app.query_one("#profile-edit-direction", Select).value == "stay_sharp"

    async def test_edit_save_via_button(self, profile_app):
        """AC4: Clicking Save button saves the profile and closes edit."""
        async with profile_app.run_test(size=(120, 40)) as pilot:
            await pilot.pause(delay=0.5)
            await pilot.press("e")
            await pilot.pause()
            save_btn = profile_app.query_one("#profile-edit-save", Button)
            save_btn.focus()
            await pilot.pause()
            await pilot.press("enter")
            await pilot.pause(delay=0.5)
            # Edit widgets hidden, display values back
            assert profile_app.query_one("#profile-edit-role", Select).display is False
            assert profile_app.query_one("#profile-val-role", Static).display is True
            profile_app.client.update_profile.assert_called_once()

    async def test_edit_cancel(self, profile_app):
        """AC5: Pressing Escape in edit mode cancels and restores display."""
        async with profile_app.run_test() as pilot:
            await pilot.pause(delay=0.5)
            await pilot.press("e")
            await pilot.pause()
            await pilot.press("escape")
            await pilot.pause()
            assert profile_app.query_one("#profile-edit-role", Select).display is False
            assert profile_app.query_one("#profile-val-role", Static).display is True

    async def test_edit_refocus_after_cancel(self, profile_app):
        """After cancel, pane has focus so 'e' works again."""
        async with profile_app.run_test() as pilot:
            await pilot.pause(delay=0.5)
            await pilot.press("e")
            await pilot.pause()
            await pilot.press("escape")
            await pilot.pause()
            await pilot.press("e")
            await pilot.pause()
            assert profile_app.query_one("#profile-edit-role", Select).display is True

    async def test_edit_stays_open_on_select_change(self, profile_app):
        """Edit mode stays open when changing a Select — no auto-close."""
        async with profile_app.run_test() as pilot:
            await pilot.pause(delay=0.5)
            await pilot.press("e")
            await pilot.pause()
            assert profile_app.query_one("#profile-edit-role", Select).display is True
            assert profile_app.client.update_profile.call_count == 0


# --- AC6: Link code ---


class TestProfileLink:
    async def test_link_generates_code(self, profile_app):
        """AC6: Pressing 'l' generates and displays a link code."""
        async with profile_app.run_test() as pilot:
            await pilot.pause(delay=0.5)
            await pilot.press("l")
            await pilot.pause(delay=0.5)
            profile_app.client.create_link_code.assert_called_once()
            link = profile_app.query_one("#profile-link-code", Static)
            assert "ABC123" in str(link.content)

    async def test_link_error(self, profile_app):
        """AC9: API error during link code generation shows inline error."""
        profile_app.client.create_link_code.side_effect = Exception("Server error")
        async with profile_app.run_test() as pilot:
            await pilot.pause(delay=0.5)
            await pilot.press("l")
            await pilot.pause(delay=0.5)
            link = profile_app.query_one("#profile-link-code", Static)
            text = str(link.content).lower()
            assert "failed" in text or "error" in text


# --- AC7: Feedback ---


class TestProfileFeedback:
    async def test_feedback_shows_input(self, profile_app):
        """AC7: Pressing 'f' shows the feedback input."""
        async with profile_app.run_test() as pilot:
            await pilot.pause(delay=0.5)
            await pilot.press("f")
            await pilot.pause()
            feedback = profile_app.query_one("#profile-feedback-input", Input)
            assert feedback.display is True
            assert feedback.has_focus

    async def test_feedback_submit(self, profile_app):
        """AC7: Typing feedback and pressing Enter submits it."""
        async with profile_app.run_test() as pilot:
            await pilot.pause(delay=0.5)
            await pilot.press("f")
            await pilot.pause()
            for ch in "great app":
                await pilot.press(ch)
            await pilot.press("enter")
            await pilot.pause(delay=0.5)
            profile_app.client.send_feedback.assert_called_once_with("great app")


# --- AC8: Fetch error ---


class TestProfileErrors:
    async def test_fetch_error(self):
        """AC8: API error during fetch shows error message."""
        import httpx

        client = MagicMock()
        client.get_profile.side_effect = httpx.ConnectError("Connection refused")
        app = SkilarkApp(client=client)
        app._active_tab = 3
        async with app.run_test() as pilot:
            await pilot.pause(delay=0.5)
            error = app.query_one("#profile-error", Static)
            assert error.display is True
