"""
Tests for the `skilark interview` command and SkilarkClient interview methods.

Client tests use httpx.MockTransport; command tests use MagicMock client.
"""

import json

import httpx
import pytest
from unittest.mock import MagicMock, patch

from skilark_cli.client import SkilarkClient
from skilark_cli.commands.interview import (
    dispatch,
    run_cancel,
    run_list,
    run_schedule,
    USAGE,
)


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _make_client(handler, user_id: str | None = "user-1") -> SkilarkClient:
    """Return a SkilarkClient backed by a MockTransport."""
    return SkilarkClient(
        base_url="https://test",
        user_id=user_id,
        transport=httpx.MockTransport(handler),
    )


def _make_interview(
    id: str = "iv-abc-123",
    role_title: str = "Backend Engineer",
    interview_type: str = "system_design",
    scheduled_at: str = "2026-04-01T14:00:00Z",
    status: str = "scheduled",
) -> dict:
    """Return a minimal interview dict."""
    return {
        "id": id,
        "role_title": role_title,
        "interview_type": interview_type,
        "scheduled_at": scheduled_at,
        "status": status,
    }


# ---------------------------------------------------------------------------
# SkilarkClient — HTTP transport tests
# ---------------------------------------------------------------------------


class TestCreateInterview:
    def test_hits_correct_path(self):
        def handler(request):
            assert request.method == "POST"
            assert request.url.path == "/v1/interviews"
            body = json.loads(request.content)
            assert body["role_title"] == "SRE"
            assert body["interview_type"] == "behavioral"
            assert body["invite_code"] == "ABC123"
            return httpx.Response(201, json=_make_interview())

        client = _make_client(handler)
        client.create_interview(
            role_title="SRE",
            interview_type="behavioral",
            scheduled_at="2026-04-01T14:00:00Z",
            invite_code="ABC123",
        )

    def test_sends_user_header(self):
        def handler(request):
            assert request.headers["x-skilark-user"] == "user-1"
            return httpx.Response(201, json=_make_interview())

        client = _make_client(handler, user_id="user-1")
        client.create_interview(
            role_title="SRE",
            interview_type="behavioral",
            scheduled_at="2026-04-01T14:00:00Z",
            invite_code="ABC123",
        )

    def test_raises_on_403(self):
        def handler(request):
            return httpx.Response(403, json={"error": "invalid invite"})

        client = _make_client(handler)
        with pytest.raises(httpx.HTTPStatusError):
            client.create_interview(
                role_title="SRE",
                interview_type="behavioral",
                scheduled_at="2026-04-01T14:00:00Z",
                invite_code="BAD",
            )


class TestListInterviews:
    def test_hits_correct_path(self):
        def handler(request):
            assert request.method == "GET"
            assert request.url.path == "/v1/interviews"
            return httpx.Response(200, json={"interviews": []})

        client = _make_client(handler)
        client.list_interviews()

    def test_returns_parsed_json(self):
        interviews = [_make_interview()]

        def handler(request):
            return httpx.Response(200, json={"interviews": interviews})

        client = _make_client(handler)
        result = client.list_interviews()
        assert len(result["interviews"]) == 1
        assert result["interviews"][0]["id"] == "iv-abc-123"


class TestCancelInterview:
    def test_hits_correct_path(self):
        def handler(request):
            assert request.method == "POST"
            assert request.url.path == "/v1/interviews/iv-abc-123/cancel"
            return httpx.Response(200, json=_make_interview(status="cancelled"))

        client = _make_client(handler)
        client.cancel_interview("iv-abc-123")

    def test_returns_cancelled_interview(self):
        def handler(request):
            return httpx.Response(200, json=_make_interview(status="cancelled"))

        client = _make_client(handler)
        result = client.cancel_interview("iv-abc-123")
        assert result["status"] == "cancelled"


class TestGetInterview:
    def test_hits_correct_path(self):
        def handler(request):
            assert request.method == "GET"
            assert request.url.path == "/v1/interviews/iv-abc-123"
            return httpx.Response(
                200,
                json={**_make_interview(), "transcript": []},
            )

        client = _make_client(handler)
        client.get_interview("iv-abc-123")

    def test_returns_parsed_json_with_transcript(self):
        transcript = [{"speaker": "bot", "text": "Tell me about yourself."}]

        def handler(request):
            return httpx.Response(
                200,
                json={**_make_interview(), "transcript": transcript},
            )

        client = _make_client(handler)
        result = client.get_interview("iv-abc-123")
        assert result["id"] == "iv-abc-123"
        assert result["transcript"] == transcript

    def test_sends_user_header(self):
        def handler(request):
            assert request.headers["x-skilark-user"] == "user-1"
            return httpx.Response(200, json={**_make_interview(), "transcript": []})

        client = _make_client(handler, user_id="user-1")
        client.get_interview("iv-abc-123")

    def test_raises_on_404(self):
        def handler(request):
            return httpx.Response(404, json={"error": "not found"})

        client = _make_client(handler)
        with pytest.raises(httpx.HTTPStatusError):
            client.get_interview("does-not-exist")


class TestGetInterviewFeedback:
    def test_hits_correct_path(self):
        def handler(request):
            assert request.method == "GET"
            assert request.url.path == "/v1/interviews/iv-abc-123/feedback"
            return httpx.Response(
                200,
                json={
                    "overall_score": 4,
                    "summary": "Good job",
                    "strengths": ["clear"],
                    "improvements": ["depth"],
                },
            )

        client = _make_client(handler)
        client.get_interview_feedback("iv-abc-123")

    def test_returns_feedback_dict(self):
        def handler(request):
            return httpx.Response(
                200,
                json={
                    "overall_score": 4,
                    "summary": "Good job",
                    "strengths": ["clear"],
                    "improvements": ["depth"],
                },
            )

        client = _make_client(handler)
        result = client.get_interview_feedback("iv-abc-123")
        assert result is not None
        assert result["overall_score"] == 4
        assert result["summary"] == "Good job"

    def test_returns_none_on_404(self):
        def handler(request):
            return httpx.Response(404, json={"error": "not found"})

        client = _make_client(handler)
        result = client.get_interview_feedback("iv-abc-123")
        assert result is None

    def test_raises_on_non_404_error(self):
        def handler(request):
            return httpx.Response(500, json={"error": "server error"})

        client = _make_client(handler)
        with pytest.raises(httpx.HTTPStatusError):
            client.get_interview_feedback("iv-abc-123")

    def test_sends_user_header(self):
        def handler(request):
            assert request.headers["x-skilark-user"] == "user-1"
            return httpx.Response(
                200,
                json={"overall_score": 3, "summary": "OK", "strengths": [], "improvements": []},
            )

        client = _make_client(handler, user_id="user-1")
        client.get_interview_feedback("iv-abc-123")


# ---------------------------------------------------------------------------
# Command tests — run_list
# ---------------------------------------------------------------------------


class TestRunList:
    def test_shows_table(self):
        mock_client = MagicMock()
        mock_client.list_interviews.return_value = {
            "interviews": [_make_interview(), _make_interview(id="iv-def-456", role_title="Frontend Engineer")]
        }

        mock_console = MagicMock()
        with patch("skilark_cli.commands.interview.console", mock_console):
            run_list(mock_client)

        mock_client.list_interviews.assert_called_once()
        # Table is printed (Table object passed to console.print)
        assert mock_console.print.called

    def test_empty_shows_message(self):
        mock_client = MagicMock()
        mock_client.list_interviews.return_value = {"interviews": []}

        mock_console = MagicMock()
        with patch("skilark_cli.commands.interview.console", mock_console):
            run_list(mock_client)

        all_output = " ".join(str(c) for c in mock_console.print.call_args_list)
        assert "no interviews" in all_output.lower()


# ---------------------------------------------------------------------------
# Command tests — run_schedule
# ---------------------------------------------------------------------------


class TestRunSchedule:
    def test_success(self):
        mock_client = MagicMock()
        mock_client.create_interview.return_value = _make_interview()

        mock_console = MagicMock()
        with patch("skilark_cli.commands.interview.console", mock_console):
            run_schedule(
                mock_client,
                role_title="Backend Engineer",
                interview_type="system_design",
                scheduled_at="2026-04-01T14:00:00Z",
                invite_code="ABC123",
            )

        mock_client.create_interview.assert_called_once()
        all_output = " ".join(str(c) for c in mock_console.print.call_args_list)
        assert "scheduled" in all_output.lower()

    def test_invalid_invite_shows_error(self):
        mock_client = MagicMock()
        resp = MagicMock()
        resp.status_code = 403
        exc = httpx.HTTPStatusError("forbidden", request=MagicMock(), response=resp)
        mock_client.create_interview.side_effect = exc

        mock_console = MagicMock()
        with patch("skilark_cli.commands.interview.console", mock_console):
            run_schedule(
                mock_client,
                role_title="SRE",
                interview_type="behavioral",
                scheduled_at="2026-04-01T14:00:00Z",
                invite_code="EXPIRED",
            )

        all_output = " ".join(str(c) for c in mock_console.print.call_args_list)
        assert "invite code" in all_output.lower()


# ---------------------------------------------------------------------------
# Command tests — run_cancel
# ---------------------------------------------------------------------------


class TestRunCancel:
    def test_success(self):
        mock_client = MagicMock()
        mock_client.cancel_interview.return_value = _make_interview(status="cancelled")

        mock_console = MagicMock()
        with patch("skilark_cli.commands.interview.console", mock_console):
            run_cancel(mock_client, "iv-abc-123")

        mock_client.cancel_interview.assert_called_once_with("iv-abc-123")
        all_output = " ".join(str(c) for c in mock_console.print.call_args_list)
        assert "cancelled" in all_output.lower()


# ---------------------------------------------------------------------------
# dispatch() tests
# ---------------------------------------------------------------------------


class TestDispatch:
    def test_no_args_shows_usage(self):
        mock_store = MagicMock()
        mock_store.is_first_run.return_value = False
        mock_store.load.return_value = {"api_url": "https://test", "user_id": "u1"}

        mock_console = MagicMock()
        with (
            patch("skilark_cli.commands.interview.ConfigStore", return_value=mock_store),
            patch("skilark_cli.commands.interview.SkilarkClient"),
            patch("skilark_cli.commands.interview.console", mock_console),
        ):
            dispatch([])

        all_output = " ".join(str(c) for c in mock_console.print.call_args_list)
        assert "schedule" in all_output.lower()

    def test_first_run_shows_login_hint(self):
        mock_store = MagicMock()
        mock_store.is_first_run.return_value = True

        mock_console = MagicMock()
        with (
            patch("skilark_cli.commands.interview.ConfigStore", return_value=mock_store),
            patch("skilark_cli.commands.interview.console", mock_console),
        ):
            dispatch([])

        all_output = " ".join(str(c) for c in mock_console.print.call_args_list)
        assert "login" in all_output.lower()

    def test_unknown_subcommand_shows_error(self):
        mock_store = MagicMock()
        mock_store.is_first_run.return_value = False
        mock_store.load.return_value = {"api_url": "https://test", "user_id": "u1"}

        mock_console = MagicMock()
        with (
            patch("skilark_cli.commands.interview.ConfigStore", return_value=mock_store),
            patch("skilark_cli.commands.interview.SkilarkClient"),
            patch("skilark_cli.commands.interview.console", mock_console),
        ):
            dispatch(["bogus"])

        all_output = " ".join(str(c) for c in mock_console.print.call_args_list)
        assert "unknown subcommand" in all_output.lower()

    def test_schedule_missing_flags_shows_error(self):
        mock_store = MagicMock()
        mock_store.is_first_run.return_value = False
        mock_store.load.return_value = {"api_url": "https://test", "user_id": "u1"}

        mock_console = MagicMock()
        with (
            patch("skilark_cli.commands.interview.ConfigStore", return_value=mock_store),
            patch("skilark_cli.commands.interview.SkilarkClient"),
            patch("skilark_cli.commands.interview.console", mock_console),
        ):
            dispatch(["schedule", "--role", "SRE"])

        all_output = " ".join(str(c) for c in mock_console.print.call_args_list)
        assert "missing required" in all_output.lower()

    def test_schedule_invalid_type_shows_error(self):
        mock_store = MagicMock()
        mock_store.is_first_run.return_value = False
        mock_store.load.return_value = {"api_url": "https://test", "user_id": "u1"}

        mock_console = MagicMock()
        with (
            patch("skilark_cli.commands.interview.ConfigStore", return_value=mock_store),
            patch("skilark_cli.commands.interview.SkilarkClient"),
            patch("skilark_cli.commands.interview.console", mock_console),
        ):
            dispatch([
                "schedule",
                "--role", "SRE",
                "--type", "invalid_type",
                "--at", "2026-04-01T14:00:00Z",
                "--invite", "ABC123",
            ])

        all_output = " ".join(str(c) for c in mock_console.print.call_args_list)
        assert "invalid type" in all_output.lower()

    def test_cancel_no_id_shows_error(self):
        mock_store = MagicMock()
        mock_store.is_first_run.return_value = False
        mock_store.load.return_value = {"api_url": "https://test", "user_id": "u1"}

        mock_console = MagicMock()
        with (
            patch("skilark_cli.commands.interview.ConfigStore", return_value=mock_store),
            patch("skilark_cli.commands.interview.SkilarkClient"),
            patch("skilark_cli.commands.interview.console", mock_console),
        ):
            dispatch(["cancel"])

        all_output = " ".join(str(c) for c in mock_console.print.call_args_list)
        assert "interview-id" in all_output.lower()


# ---------------------------------------------------------------------------
# main.py routing
# ---------------------------------------------------------------------------


class TestMainInterviewRouting:
    def test_interview_routes_to_dispatch(self):
        import sys
        from skilark_cli.main import main

        with patch("skilark_cli.commands.interview.dispatch") as mock_dispatch:
            with patch.object(sys, "argv", ["skilark", "interview"]):
                main()

        mock_dispatch.assert_called_once_with([])

    def test_interview_list_routes_to_dispatch_with_args(self):
        import sys
        from skilark_cli.main import main

        with patch("skilark_cli.commands.interview.dispatch") as mock_dispatch:
            with patch.object(sys, "argv", ["skilark", "interview", "list"]):
                main()

        mock_dispatch.assert_called_once_with(["list"])
