"""
Tests for the `skilark profile` command.

Covers: first-run guard, no-profile migration, profile display,
all edit actions (role, language, direction), status/link delegation,
back action, and API error handling.
"""

from unittest.mock import MagicMock, patch

from skilark_cli.commands.profile import run, _label_for
from skilark_cli.config_store import ConfigStore
from skilark_cli.onboarding import ROLE_CHOICES, DIRECTION_CHOICES


def _setup_store(tmp_path, *, with_profile=True):
    """Create a ConfigStore with optional profile fields."""
    store = ConfigStore(config_dir=tmp_path)
    store.save(user_id="test-uuid", topics=["python", "dsa"], api_url="http://localhost:8000")
    if with_profile:
        store.update_profile(
            current_role="swe",
            primary_language="Python",
            career_direction="stay_sharp",
        )
    return store


def test_first_run_prints_hint(tmp_path, capsys):
    """First-run (no config) prints setup hint and returns."""
    with patch("skilark_cli.commands.profile.ConfigStore") as mock_cls:
        mock_store = MagicMock()
        mock_store.is_first_run.return_value = True
        mock_cls.return_value = mock_store

        run()

        # Should not crash; prints a hint message.
        mock_store.load.assert_not_called()


def test_no_profile_triggers_migration(tmp_path):
    """User without profile gets routed to migration flow."""
    store = _setup_store(tmp_path, with_profile=False)

    with patch("skilark_cli.commands.profile.ConfigStore", return_value=store), \
         patch("skilark_cli.commands.profile.run_profile_migration") as mock_migrate:
        run()
        mock_migrate.assert_called_once_with(store, api_url="http://localhost:8000")


def test_back_action_returns(tmp_path):
    """Choosing 'Back' returns without any API calls."""
    store = _setup_store(tmp_path)

    with patch("skilark_cli.commands.profile.ConfigStore", return_value=store), \
         patch("skilark_cli.commands.profile.inquirer") as mock_inq, \
         patch("skilark_cli.commands.profile.SkilarkClient") as mock_client_cls:

        select_prompt = MagicMock()
        select_prompt.execute.return_value = "back"
        mock_inq.select.return_value = select_prompt

        run()

        mock_client_cls.assert_not_called()


def test_status_delegates(tmp_path):
    """'My status' action delegates to status.run()."""
    store = _setup_store(tmp_path)

    with patch("skilark_cli.commands.profile.ConfigStore", return_value=store), \
         patch("skilark_cli.commands.profile.inquirer") as mock_inq, \
         patch("skilark_cli.commands.status.run") as mock_status_run:

        select_prompt = MagicMock()
        select_prompt.execute.return_value = "status"
        mock_inq.select.return_value = select_prompt

        run()

        mock_status_run.assert_called_once()


def test_link_delegates(tmp_path):
    """'Link accounts' action delegates to link.dispatch()."""
    store = _setup_store(tmp_path)

    with patch("skilark_cli.commands.profile.ConfigStore", return_value=store), \
         patch("skilark_cli.commands.profile.inquirer") as mock_inq, \
         patch("skilark_cli.commands.link.dispatch") as mock_link:

        select_prompt = MagicMock()
        select_prompt.execute.return_value = "link"
        mock_inq.select.return_value = select_prompt

        run()

        mock_link.assert_called_once_with([])


def test_change_role(tmp_path):
    """Role edit updates API first, then local config + topics."""
    store = _setup_store(tmp_path)

    with patch("skilark_cli.commands.profile.ConfigStore", return_value=store), \
         patch("skilark_cli.commands.profile.inquirer") as mock_inq, \
         patch("skilark_cli.commands.profile.SkilarkClient") as mock_client_cls:

        select_prompt = MagicMock()
        # First select: action menu → "role", second select: role picker → "data_eng"
        select_prompt.execute.side_effect = ["role", "data_eng"]
        mock_inq.select.return_value = select_prompt

        mock_client = MagicMock()
        mock_client_cls.return_value = mock_client

        run()

        mock_client.update_profile.assert_called_once_with(current_role="data_eng")
        mock_client.update_topics.assert_called_once()
        config = store.load()
        assert config["current_role"] == "data_eng"
        # Topics should be re-derived (data_eng → sql, spark, python + Python → python)
        assert "sql" in config["topics"]


def test_change_language(tmp_path):
    """Language edit updates API + local config + topics."""
    store = _setup_store(tmp_path)

    with patch("skilark_cli.commands.profile.ConfigStore", return_value=store), \
         patch("skilark_cli.commands.profile.inquirer") as mock_inq, \
         patch("skilark_cli.commands.profile.SkilarkClient") as mock_client_cls:

        select_prompt = MagicMock()
        select_prompt.execute.side_effect = ["language", "Go"]
        mock_inq.select.return_value = select_prompt

        mock_client = MagicMock()
        mock_client_cls.return_value = mock_client

        run()

        mock_client.update_profile.assert_called_once_with(primary_language="Go")
        mock_client.update_topics.assert_called_once()
        config = store.load()
        assert config["primary_language"] == "Go"
        assert "go" in config["topics"]


def test_change_direction(tmp_path):
    """Direction edit updates API + local config but does NOT re-derive topics."""
    store = _setup_store(tmp_path)

    with patch("skilark_cli.commands.profile.ConfigStore", return_value=store), \
         patch("skilark_cli.commands.profile.inquirer") as mock_inq, \
         patch("skilark_cli.commands.profile.SkilarkClient") as mock_client_cls:

        select_prompt = MagicMock()
        select_prompt.execute.side_effect = ["direction", "level_up"]
        mock_inq.select.return_value = select_prompt

        mock_client = MagicMock()
        mock_client_cls.return_value = mock_client

        run()

        mock_client.update_profile.assert_called_once_with(career_direction="level_up")
        mock_client.update_topics.assert_not_called()
        config = store.load()
        assert config["career_direction"] == "level_up"


def test_api_error_does_not_update_local_config(tmp_path):
    """When API call fails, local config should NOT be updated."""
    store = _setup_store(tmp_path)
    original_config = store.load()

    with patch("skilark_cli.commands.profile.ConfigStore", return_value=store), \
         patch("skilark_cli.commands.profile.inquirer") as mock_inq, \
         patch("skilark_cli.commands.profile.SkilarkClient") as mock_client_cls:

        select_prompt = MagicMock()
        select_prompt.execute.side_effect = ["role", "platform"]
        mock_inq.select.return_value = select_prompt

        mock_client = MagicMock()
        mock_client.update_profile.side_effect = Exception("API unreachable")
        mock_client_cls.return_value = mock_client

        run()  # Should not raise

        # Local config should be unchanged.
        config = store.load()
        assert config["current_role"] == original_config["current_role"]
        assert config["topics"] == original_config["topics"]


def test_label_for_known_value():
    assert _label_for(ROLE_CHOICES, "swe") == "Software Engineer"


def test_label_for_unknown_value():
    assert _label_for(ROLE_CHOICES, "unknown") == "unknown"
