"""Tests for the TUI Interviews pane — helper functions and data parsing."""

MOCK_INTERVIEWS = [
    {
        "id": "aaa-111",
        "role_title": "Software Engineer",
        "interview_type": "behavioral",
        "scheduled_at": "2026-04-10T14:00:00Z",
        "status": "scheduled",
        "has_feedback": False,
        "company_slug": "google",
    },
    {
        "id": "bbb-222",
        "role_title": "Product Manager",
        "interview_type": "product_sense",
        "scheduled_at": "2026-04-01T10:00:00Z",
        "status": "completed",
        "has_feedback": True,
        "company_slug": None,
    },
]

MOCK_FEEDBACK = {
    "overall_score": 4,
    "summary": "Strong performance overall.",
    "strengths": ["Clear communication", "Good STAR format"],
    "improvements": ["Quantify impact more"],
    "question_scores": [
        {"question": "Tell me about a challenge", "score": 4, "notes": "Good depth"}
    ],
}


# ---------------------------------------------------------------------------
# _status_label
# ---------------------------------------------------------------------------


class TestStatusLabel:
    def test_scheduled(self):
        from skilark_cli.tui.screens.interviews import _status_label

        assert "cyan" in _status_label("scheduled")

    def test_in_progress(self):
        from skilark_cli.tui.screens.interviews import _status_label

        assert "yellow" in _status_label("in_progress")

    def test_completed(self):
        from skilark_cli.tui.screens.interviews import _status_label

        assert "green" in _status_label("completed")

    def test_cancelled(self):
        from skilark_cli.tui.screens.interviews import _status_label

        assert "dim" in _status_label("cancelled")

    def test_error(self):
        from skilark_cli.tui.screens.interviews import _status_label

        assert "red" in _status_label("error")

    def test_unknown_returns_raw(self):
        from skilark_cli.tui.screens.interviews import _status_label

        assert _status_label("foo") == "foo"


# ---------------------------------------------------------------------------
# _render_feedback
# ---------------------------------------------------------------------------


class TestRenderFeedback:
    def test_includes_score(self):
        from skilark_cli.tui.screens.interviews import _render_feedback

        text = _render_feedback(MOCK_FEEDBACK)
        assert "4/5" in text

    def test_includes_stars(self):
        from skilark_cli.tui.screens.interviews import _render_feedback

        text = _render_feedback(MOCK_FEEDBACK)
        # 4 filled stars out of 5
        assert "★★★★" in text

    def test_includes_summary(self):
        from skilark_cli.tui.screens.interviews import _render_feedback

        text = _render_feedback(MOCK_FEEDBACK)
        assert "Strong performance overall." in text

    def test_includes_strengths(self):
        from skilark_cli.tui.screens.interviews import _render_feedback

        text = _render_feedback(MOCK_FEEDBACK)
        assert "Clear communication" in text
        assert "Good STAR format" in text

    def test_includes_improvements(self):
        from skilark_cli.tui.screens.interviews import _render_feedback

        text = _render_feedback(MOCK_FEEDBACK)
        assert "Quantify impact" in text

    def test_includes_question_scores(self):
        from skilark_cli.tui.screens.interviews import _render_feedback

        text = _render_feedback(MOCK_FEEDBACK)
        assert "Tell me about a challenge" in text
        assert "Good depth" in text

    def test_empty_feedback_with_score_only(self):
        from skilark_cli.tui.screens.interviews import _render_feedback

        text = _render_feedback({"overall_score": 3, "summary": "OK"})
        assert "3/5" in text
        assert "OK" in text

    def test_missing_optional_sections_no_error(self):
        from skilark_cli.tui.screens.interviews import _render_feedback

        # Should not raise even with minimal input
        text = _render_feedback({"overall_score": 5})
        assert "5/5" in text

    def test_zero_score_renders(self):
        from skilark_cli.tui.screens.interviews import _render_feedback

        text = _render_feedback({"overall_score": 0, "summary": "No show"})
        assert "0/5" in text


# ---------------------------------------------------------------------------
# InterviewsPane — instantiation and initial state
# ---------------------------------------------------------------------------


class TestInterviewsPane:
    def test_default_view_is_list(self):
        from skilark_cli.tui.screens.interviews import InterviewsPane

        pane = InterviewsPane(client=None)
        assert pane._view == "list"

    def test_interviews_stored(self):
        from skilark_cli.tui.screens.interviews import InterviewsPane

        pane = InterviewsPane(client=None)
        pane._interviews = MOCK_INTERVIEWS
        assert len(pane._interviews) == 2

    def test_interviews_default_empty(self):
        from skilark_cli.tui.screens.interviews import InterviewsPane

        pane = InterviewsPane(client=None)
        assert pane._interviews == []

    def test_client_stored(self):
        from unittest.mock import MagicMock
        from skilark_cli.tui.screens.interviews import InterviewsPane

        mock_client = MagicMock()
        pane = InterviewsPane(client=mock_client)
        assert pane.client is mock_client

    def test_bindings_include_refresh(self):
        from skilark_cli.tui.screens.interviews import InterviewsPane

        keys = {b.key for b in InterviewsPane.BINDINGS}
        assert "r" in keys

    def test_bindings_include_back(self):
        from skilark_cli.tui.screens.interviews import InterviewsPane

        keys = {b.key for b in InterviewsPane.BINDINGS}
        assert "b" in keys
        assert "escape" in keys

    def test_bindings_include_feedback(self):
        from skilark_cli.tui.screens.interviews import InterviewsPane

        keys = {b.key for b in InterviewsPane.BINDINGS}
        assert "f" in keys

    def test_bindings_include_book(self):
        from skilark_cli.tui.screens.interviews import InterviewsPane

        keys = {b.key for b in InterviewsPane.BINDINGS}
        assert "n" in keys


class TestInterviewBookModal:
    """Unit tests for the booking modal."""

    def test_interview_type_options(self):
        from skilark_cli.tui.screens.interview_book_modal import INTERVIEW_TYPE_OPTIONS

        types = [v for _, v in INTERVIEW_TYPE_OPTIONS]
        assert "behavioral" in types
        assert "technical_coding" in types
        assert "system_design" in types
        assert len(types) == 9

    def test_interview_types_includes_quick_chat(self):
        from skilark_cli.tui.screens.interview_book_modal import INTERVIEW_TYPE_OPTIONS

        types = [t[1] for t in INTERVIEW_TYPE_OPTIONS]
        assert "quick_chat" in types

    def test_date_options_14_days(self):
        from skilark_cli.tui.screens.interview_book_modal import _date_options

        options = _date_options()
        assert len(options) == 14

    def test_date_options_starts_with_today(self):
        from skilark_cli.tui.screens.interview_book_modal import _date_options

        options = _date_options()
        assert "Today" in options[0][0]

    def test_date_options_tomorrow(self):
        from skilark_cli.tui.screens.interview_book_modal import _date_options

        options = _date_options()
        assert "Tomorrow" in options[1][0]

    def test_date_options_values_are_iso(self):
        from datetime import date
        from skilark_cli.tui.screens.interview_book_modal import _date_options

        options = _date_options()
        for _, val in options:
            date.fromisoformat(val)  # raises if invalid

    def test_default_date_is_today_or_tomorrow(self):
        from datetime import datetime, timedelta
        from skilark_cli.tui.screens.interview_book_modal import _default_date

        now = datetime.now().astimezone()
        today = now.date().isoformat()
        tomorrow = (now.date() + timedelta(days=1)).isoformat()
        # Falls back to tomorrow when no time slots remain today
        assert _default_date() in (today, tomorrow)

    def test_default_time_slot_format(self):
        from skilark_cli.tui.screens.interview_book_modal import _default_time_slot

        slot = _default_time_slot()
        assert len(slot) == 5
        assert slot[2] == ":"

    def test_default_time_slot_is_on_5min_boundary(self):
        from skilark_cli.tui.screens.interview_book_modal import _default_time_slot

        slot = _default_time_slot()
        minutes = int(slot[3:])
        assert minutes % 5 == 0, f"Expected 5-min boundary, got :{minutes:02d}"

    def test_local_tz_name_is_non_empty(self):
        from skilark_cli.tui.screens.interview_book_modal import _LOCAL_TZ_NAME

        assert _LOCAL_TZ_NAME  # e.g. "EDT", "PST", "UTC"
        assert len(_LOCAL_TZ_NAME) >= 2

    def test_modal_can_instantiate(self):
        from skilark_cli.tui.screens.interview_book_modal import InterviewBookModal

        modal = InterviewBookModal(client=None)
        assert modal is not None

    def test_render_feedback_quick_chat_no_score(self):
        from skilark_cli.tui.screens.interviews import _render_feedback

        fb = {"summary": "Great conversation!", "highlights": ["Clear communicator", "Genuine enthusiasm"]}
        result = _render_feedback(fb)
        assert "Great conversation!" in result
        assert "Clear communicator" in result
        assert "/5" not in result  # No score line when overall_score is absent

    def test_render_feedback_quick_chat_with_highlights(self):
        from skilark_cli.tui.screens.interviews import _render_feedback

        fb = {"summary": "Good chat.", "highlights": ["Friendly", "Prepared"]}
        result = _render_feedback(fb)
        assert "Highlights" in result
        assert "Friendly" in result

    def test_render_feedback_standard_still_works(self):
        from skilark_cli.tui.screens.interviews import _render_feedback

        fb = {"overall_score": 4, "summary": "Good", "strengths": ["A"], "improvements": ["B"]}
        result = _render_feedback(fb)
        assert "4/5" in result
        assert "A" in result


# ---------------------------------------------------------------------------
# OTP client methods
# ---------------------------------------------------------------------------


class TestOTPClient:
    def test_send_otp(self):
        import httpx
        from skilark_cli.client import SkilarkClient

        def handler(request: httpx.Request) -> httpx.Response:
            assert request.url.path == "/v1/interviews/otp/send"
            return httpx.Response(202, json={"status": "otp_sent", "otp_id": "otp_abc"})

        client = SkilarkClient(
            base_url="http://test",
            user_id="user-1",
            transport=httpx.MockTransport(handler),
        )
        result = client.send_otp("+12125551234")
        assert result["otp_id"] == "otp_abc"
        assert result["status"] == "otp_sent"

    def test_verify_otp_and_book(self):
        import httpx
        from skilark_cli.client import SkilarkClient

        def handler(request: httpx.Request) -> httpx.Response:
            assert request.url.path == "/v1/interviews/otp/verify"
            return httpx.Response(201, json={"id": "iv-001", "status": "scheduled"})

        client = SkilarkClient(
            base_url="http://test",
            user_id="user-1",
            transport=httpx.MockTransport(handler),
        )
        result = client.verify_otp_and_book(
            otp_id="otp_abc",
            code="123456",
            role_title="General",
            interview_type="quick_chat",
            scheduled_at="2026-04-05T14:00:00Z",
        )
        assert result["id"] == "iv-001"
        assert result["status"] == "scheduled"

    def test_send_otp_sends_phone_number_in_body(self):
        import json
        import httpx
        from skilark_cli.client import SkilarkClient

        captured: dict = {}

        def handler(request: httpx.Request) -> httpx.Response:
            captured["body"] = json.loads(request.content)
            return httpx.Response(202, json={"status": "otp_sent", "otp_id": "otp_xyz"})

        client = SkilarkClient(
            base_url="http://test",
            transport=httpx.MockTransport(handler),
        )
        client.send_otp("+16505551234")
        assert captured["body"]["phone_number"] == "+16505551234"

    def test_verify_otp_sends_all_fields(self):
        import json
        import httpx
        from skilark_cli.client import SkilarkClient

        captured: dict = {}

        def handler(request: httpx.Request) -> httpx.Response:
            captured["body"] = json.loads(request.content)
            return httpx.Response(201, json={"id": "iv-002", "status": "scheduled"})

        client = SkilarkClient(
            base_url="http://test",
            transport=httpx.MockTransport(handler),
        )
        client.verify_otp_and_book(
            otp_id="otp_abc",
            code="654321",
            role_title="SWE",
            interview_type="quick_chat",
            scheduled_at="2026-04-10T10:00:00Z",
            invite_code="INV001",
            company_slug="stripe",
        )
        body = captured["body"]
        assert body["otp_id"] == "otp_abc"
        assert body["code"] == "654321"
        assert body["role_title"] == "SWE"
        assert body["invite_code"] == "INV001"
        assert body["company_slug"] == "stripe"

    def test_verify_otp_defaults_optional_fields(self):
        import json
        import httpx
        from skilark_cli.client import SkilarkClient

        captured: dict = {}

        def handler(request: httpx.Request) -> httpx.Response:
            captured["body"] = json.loads(request.content)
            return httpx.Response(201, json={"id": "iv-003"})

        client = SkilarkClient(
            base_url="http://test",
            transport=httpx.MockTransport(handler),
        )
        client.verify_otp_and_book(
            otp_id="otp_abc",
            code="000000",
            role_title="General",
            interview_type="quick_chat",
            scheduled_at="2026-04-10T10:00:00Z",
        )
        body = captured["body"]
        # Defaults should be empty strings, not missing
        assert body["invite_code"] == ""
        assert body["company_slug"] == ""


# ---------------------------------------------------------------------------
# _render_detail_lines
# ---------------------------------------------------------------------------


class TestRenderDetailLines:
    def test_shows_meeting_link(self):
        from skilark_cli.tui.screens.interviews import _render_detail_lines

        iv = {
            "role_title": "Engineer",
            "interview_type": "behavioral",
            "company_slug": "google",
            "status": "scheduled",
            "scheduled_at": "2026-04-05T14:00:00Z",
            "meeting_link": "https://zoom.us/j/123",
            "channel": "zoom",
            "duration_minutes": 45,
        }
        text = "\n".join(_render_detail_lines(iv, None))
        assert "zoom.us" in text
        assert "45 min" in text

    def test_shows_phone_for_phone_channel(self):
        from skilark_cli.tui.screens.interviews import _render_detail_lines

        iv = {
            "role_title": "General",
            "interview_type": "quick_chat",
            "company_slug": "",
            "status": "scheduled",
            "scheduled_at": "2026-04-05T14:00:00Z",
            "phone_number": "+1***1234",
            "channel": "phone",
            "duration_minutes": 10,
        }
        text = "\n".join(_render_detail_lines(iv, None))
        assert "+1***1234" in text

    def test_phone_channel_does_not_show_meeting_link(self):
        from skilark_cli.tui.screens.interviews import _render_detail_lines

        iv = {
            "role_title": "General",
            "interview_type": "quick_chat",
            "company_slug": "",
            "status": "scheduled",
            "scheduled_at": "2026-04-05T14:00:00Z",
            "phone_number": "+16509550203",
            "meeting_link": "https://zoom.us/j/999",
            "channel": "phone",
            "duration_minutes": 10,
        }
        text = "\n".join(_render_detail_lines(iv, None))
        # Phone takes precedence; zoom link should be suppressed
        assert "+16509550203" in text
        assert "zoom.us" not in text

    def test_shows_calendar_links_when_token_present(self):
        from skilark_cli.tui.screens.interviews import _render_detail_lines

        iv = {
            "role_title": "Engineer",
            "interview_type": "behavioral",
            "company_slug": "",
            "status": "scheduled",
            "scheduled_at": "2026-04-05T14:00:00Z",
            "meeting_link": "https://zoom.us/j/123",
            "channel": "zoom",
            "duration_minutes": 45,
            "calendar_token": "test_token",
        }
        text = "\n".join(_render_detail_lines(iv, None))
        assert "calendar" in text.lower()
        assert "test_token" in text

    def test_no_calendar_links_when_completed(self):
        from skilark_cli.tui.screens.interviews import _render_detail_lines

        iv = {
            "role_title": "Engineer",
            "interview_type": "behavioral",
            "company_slug": "",
            "status": "completed",
            "scheduled_at": "2026-04-05T14:00:00Z",
            "channel": "zoom",
            "duration_minutes": 45,
            "calendar_token": "test_token",
        }
        text = "\n".join(_render_detail_lines(iv, None))
        # Calendar links only show for scheduled interviews
        assert "Google Calendar" not in text
        assert "Download .ics" not in text

    def test_no_meeting_link_when_absent(self):
        from skilark_cli.tui.screens.interviews import _render_detail_lines

        iv = {
            "role_title": "Engineer",
            "interview_type": "behavioral",
            "company_slug": "stripe",
            "status": "scheduled",
            "scheduled_at": "2026-04-05T14:00:00Z",
            "channel": "zoom",
            "duration_minutes": 30,
        }
        text = "\n".join(_render_detail_lines(iv, None))
        assert "Join:" not in text
        assert "Phone:" not in text

    def test_channel_and_duration_always_shown(self):
        from skilark_cli.tui.screens.interviews import _render_detail_lines

        iv = {
            "role_title": "PM",
            "interview_type": "product_sense",
            "company_slug": "",
            "status": "scheduled",
            "scheduled_at": "2026-04-05T14:00:00Z",
            "channel": "zoom",
            "duration_minutes": 60,
        }
        text = "\n".join(_render_detail_lines(iv, None))
        assert "Channel:" in text
        assert "zoom" in text
        assert "Duration:" in text
        assert "60 min" in text

    def test_feedback_rendered_when_provided(self):
        from skilark_cli.tui.screens.interviews import _render_detail_lines

        iv = {
            "role_title": "Engineer",
            "interview_type": "behavioral",
            "company_slug": "",
            "status": "completed",
            "scheduled_at": "2026-04-01T14:00:00Z",
            "channel": "zoom",
            "duration_minutes": 45,
            "has_feedback": True,
        }
        fb = {"overall_score": 4, "summary": "Well done.", "strengths": ["Clear"], "improvements": []}
        text = "\n".join(_render_detail_lines(iv, fb))
        assert "AI Feedback" in text
        assert "Well done." in text

    def test_renders_transcript_turns(self):
        from skilark_cli.tui.screens.interviews import _render_detail_lines

        iv = {
            "role_title": "SWE",
            "interview_type": "system_design",
            "company_slug": "google",
            "status": "completed",
            "scheduled_at": "2026-04-01T14:00:00Z",
            "channel": "zoom",
            "duration_minutes": 45,
            "transcript": [
                {"speaker": "bot", "text": "Tell me about yourself"},
                {"speaker": "user", "text": "I have 5 years of experience"},
            ],
        }
        text = "\n".join(_render_detail_lines(iv, None))
        assert "Transcript" in text
        assert "Tell me about yourself" in text
        assert "5 years of experience" in text

    def test_transcript_caps_at_20_turns(self):
        from skilark_cli.tui.screens.interviews import _render_detail_lines

        iv = {
            "role_title": "SWE",
            "interview_type": "behavioral",
            "company_slug": "",
            "status": "completed",
            "scheduled_at": "2026-04-01T14:00:00Z",
            "channel": "zoom",
            "duration_minutes": 45,
            "transcript": [
                {"speaker": "bot", "text": f"Turn {i}"} for i in range(25)
            ],
        }
        text = "\n".join(_render_detail_lines(iv, None))
        assert "Turn 0" in text
        assert "Turn 19" in text
        assert "Turn 20" not in text
        assert "5 more turns" in text


# ---------------------------------------------------------------------------
# _load_detail error resilience
# ---------------------------------------------------------------------------


class TestLoadDetailErrorResilience:
    """Verify _load_detail never leaves the view stuck on 'Loading...'."""

    def _run_worker(self, pane, interview_id):
        """Invoke the _load_detail worker body directly (skips @work decorator)."""
        worker_fn = getattr(
            type(pane)._load_detail, "__wrapped__", type(pane)._load_detail
        )
        worker_fn(pane, interview_id)

    def test_post_api_exception_calls_render_error(self):
        """If code AFTER the API call raises, _render_detail_error must fire."""
        from unittest.mock import MagicMock, PropertyMock, patch

        from skilark_cli.tui.screens.interviews import InterviewsPane

        pane = InterviewsPane.__new__(InterviewsPane)
        pane.client = MagicMock()
        pane.client.get_interview.return_value = {
            "id": "iv-1",
            "role_title": "SWE",
            "status": "completed",
        }
        # Force an error AFTER the API call by making _interviews a non-iterable.
        pane._interviews = None  # next(...) will raise TypeError

        rendered = {}

        def fake_call_from_thread(fn, *args, **kwargs):
            rendered["fn"] = fn.__name__
            rendered["args"] = args

        mock_app = MagicMock()
        mock_app.call_from_thread = fake_call_from_thread

        with patch.object(type(pane), "app", new_callable=PropertyMock, return_value=mock_app):
            self._run_worker(pane, "iv-1")

        # With proper error handling, the error path should fire.
        # Without it, the worker dies silently and rendered stays empty.
        assert rendered.get("fn") == "_render_detail_error", (
            f"Expected _render_detail_error to be called, got {rendered.get('fn')!r}. "
            "Worker died silently — 'Loading...' would hang forever."
        )

    def test_api_error_calls_render_error(self):
        """If get_interview raises, _render_detail_error must fire."""
        from unittest.mock import MagicMock, PropertyMock, patch

        from skilark_cli.tui.screens.interviews import InterviewsPane

        pane = InterviewsPane.__new__(InterviewsPane)
        pane.client = MagicMock()
        pane.client.get_interview.side_effect = ConnectionError("timeout")
        pane._interviews = []

        rendered = {}

        def fake_call_from_thread(fn, *args, **kwargs):
            rendered["fn"] = fn.__name__
            rendered["args"] = args

        mock_app = MagicMock()
        mock_app.call_from_thread = fake_call_from_thread

        with patch.object(type(pane), "app", new_callable=PropertyMock, return_value=mock_app):
            self._run_worker(pane, "iv-1")

        assert rendered["fn"] == "_render_detail_error", (
            f"Expected _render_detail_error, got {rendered.get('fn')}"
        )
