"""
Tests for the onboarding flow.

Verifies that:
- TOPIC_CHOICES covers all expected categories
- run_onboarding wires up the profile prompts, client, and config store correctly
- run_profile_migration updates existing users
"""

from unittest.mock import MagicMock, patch, call

from skilark_cli.config_store import ConfigStore
from skilark_cli.onboarding import (
    TOPIC_CHOICES,
    ROLE_CHOICES,
    LANGUAGE_CHOICES,
    DIRECTION_CHOICES,
    derive_topics,
    run_onboarding,
    run_profile_migration,
)


def test_topic_choices_cover_key_sources():
    values = {t["value"] for t in TOPIC_CHOICES}
    assert "python" in values
    assert "java" in values
    assert "go" in values
    assert "docker" in values
    assert "kubernetes" in values
    assert "dsa" in values
    assert "sql" in values


def test_role_choices_not_empty():
    assert len(ROLE_CHOICES) >= 5
    values = {c["value"] for c in ROLE_CHOICES}
    assert "swe" in values
    assert "ai_ml" in values
    assert "data_eng" in values


def testderive_topics_python_swe():
    topics = derive_topics("swe", "Python")
    assert "python" in topics
    assert "dsa" in topics
    assert "system-design" in topics


def testderive_topics_go_data_eng():
    topics = derive_topics("data_eng", "Go")
    assert "go" in topics
    assert "sql" in topics
    assert "spark" in topics


def testderive_topics_fallback():
    topics = derive_topics("other", "Other")
    assert topics == ["python"]  # fallback when no topics derived


def test_run_onboarding(tmp_path):
    with patch("skilark_cli.onboarding.inquirer") as mock_inq, \
         patch("skilark_cli.onboarding.SkilarkClient") as mock_client_cls:

        # New flow uses inquirer.select for each question.
        select_prompt = MagicMock()
        # Role → Language → Direction (no transition follow-up, no DSA coding prompt
        # because "platform" role doesn't derive DSA)
        select_prompt.execute.side_effect = ["platform", "Go", "stay_sharp"]
        mock_inq.select.return_value = select_prompt

        mock_client = MagicMock()
        mock_client.create_user.return_value = {
            "id": "12345678-1234-5678-1234-567812345678",
            "topics": ["go", "kubernetes", "docker", "terraform"],
        }
        mock_client_cls.return_value = mock_client

        store = ConfigStore(config_dir=tmp_path)
        run_onboarding(store, api_url="http://localhost:8000")

        config = store.load()
        assert config["user_id"] == "12345678-1234-5678-1234-567812345678"
        assert "go" in config["topics"]
        assert config["api_url"] == "http://localhost:8000"
        assert config["current_role"] == "platform"
        assert config["primary_language"] == "Go"
        assert config["career_direction"] == "stay_sharp"
        mock_client.create_user.assert_called_once()
        # Verify profile fields were passed to API
        create_call = mock_client.create_user.call_args
        assert create_call.kwargs["current_role"] == "platform"
        assert create_call.kwargs["primary_language"] == "Go"
        assert create_call.kwargs["career_direction"] == "stay_sharp"


def test_run_onboarding_swe_python_with_coding(tmp_path):
    """SWE + Python derives DSA, so coding language prompt appears."""
    with patch("skilark_cli.onboarding.inquirer") as mock_inq, \
         patch("skilark_cli.onboarding.SkilarkClient") as mock_client_cls:

        select_prompt = MagicMock()
        # Role → Language → Direction → Coding language (DSA derived for SWE)
        select_prompt.execute.side_effect = ["swe", "Python", "stay_sharp", "python"]
        mock_inq.select.return_value = select_prompt

        mock_client = MagicMock()
        mock_client.create_user.return_value = {
            "id": "12345678-1234-5678-1234-567812345678",
            "topics": ["python", "dsa", "system-design"],
        }
        mock_client_cls.return_value = mock_client

        store = ConfigStore(config_dir=tmp_path)
        run_onboarding(store, api_url="http://localhost:8000")

        config = store.load()
        assert config["coding_language"] == "python"
        # 4 select calls: role, language, direction, coding language
        assert mock_inq.select.call_count == 4


def test_run_onboarding_no_dsa_no_coding_prompt(tmp_path):
    """When role doesn't derive DSA, coding language prompt is never shown."""
    with patch("skilark_cli.onboarding.inquirer") as mock_inq, \
         patch("skilark_cli.onboarding.SkilarkClient") as mock_client_cls:

        select_prompt = MagicMock()
        # platform + Go + level_up → no DSA
        select_prompt.execute.side_effect = ["platform", "Go", "level_up"]
        mock_inq.select.return_value = select_prompt

        mock_client = MagicMock()
        mock_client.create_user.return_value = {
            "id": "12345678-1234-5678-1234-567812345678",
            "topics": ["go", "kubernetes", "docker", "terraform"],
        }
        mock_client_cls.return_value = mock_client

        store = ConfigStore(config_dir=tmp_path)
        run_onboarding(store, api_url="http://localhost:8000")

        # Only 3 select calls: role, language, direction (no coding)
        assert mock_inq.select.call_count == 3


def test_run_onboarding_transition_asks_target_role(tmp_path):
    """When direction is 'transition', a follow-up target role question appears."""
    with patch("skilark_cli.onboarding.inquirer") as mock_inq, \
         patch("skilark_cli.onboarding.SkilarkClient") as mock_client_cls:

        select_prompt = MagicMock()
        # Role → Language → Direction(transition) → TargetRole
        # platform + Go + transition → no DSA, but target_role prompt
        select_prompt.execute.side_effect = ["platform", "Go", "transition", "data_eng"]
        mock_inq.select.return_value = select_prompt

        mock_client = MagicMock()
        mock_client.create_user.return_value = {
            "id": "12345678-1234-5678-1234-567812345678",
            "topics": ["go", "kubernetes", "docker", "terraform"],
        }
        mock_client_cls.return_value = mock_client

        store = ConfigStore(config_dir=tmp_path)
        run_onboarding(store, api_url="http://localhost:8000")

        # 4 select calls: role, language, direction, target_role (no coding since no DSA)
        assert mock_inq.select.call_count == 4
        create_call = mock_client.create_user.call_args
        assert create_call.kwargs["target_role"] == "data_eng"


def test_run_profile_migration(tmp_path):
    """Existing user gets prompted for profile and API is updated."""
    # Pre-create a config file to simulate existing user
    store = ConfigStore(config_dir=tmp_path)
    store.save(user_id="existing-uuid", topics=["python"], api_url="http://localhost:8000")

    with patch("skilark_cli.onboarding.inquirer") as mock_inq, \
         patch("skilark_cli.onboarding.SkilarkClient") as mock_client_cls:

        select_prompt = MagicMock()
        select_prompt.execute.side_effect = ["swe", "Python", "stay_sharp"]
        mock_inq.select.return_value = select_prompt

        mock_client = MagicMock()
        mock_client_cls.return_value = mock_client

        run_profile_migration(store, api_url="http://localhost:8000")

        config = store.load()
        assert config["current_role"] == "swe"
        assert config["primary_language"] == "Python"
        assert config["career_direction"] == "stay_sharp"
        # Topics should be re-derived from profile
        assert "python" in config["topics"]
        assert "dsa" in config["topics"]
        mock_client.update_profile.assert_called_once_with(
            current_role="swe",
            primary_language="Python",
            career_direction="stay_sharp",
        )
        # Topics should be synced to API
        mock_client.update_topics.assert_called_once()


def test_config_store_has_profile(tmp_path):
    store = ConfigStore(config_dir=tmp_path)
    assert not store.has_profile()

    store.save(user_id="test-id", topics=["python"], api_url="http://localhost:8000")
    assert not store.has_profile()

    store.update_profile(current_role="swe")
    assert store.has_profile()
