from __future__ import annotations

import json
from unittest import mock

from dspy_file.file_analyzer import FileTeachingAnalyzer, TeachingConfig


class _Response:
    def __init__(self, payload: dict) -> None:
        self._payload = payload

    def read(self) -> bytes:
        return json.dumps(self._payload).encode("utf-8")

    def __enter__(self) -> "_Response":
        return self

    def __exit__(self, exc_type, exc, tb) -> None:
        return None


def test_file_teaching_analyzer_uses_lmstudio_structured_outputs(monkeypatch: mock.MagicMock) -> None:
    monkeypatch.setenv("DSPYTEACH_ACTIVE_PROVIDER", "lmstudio")
    monkeypatch.setenv("DSPYTEACH_ACTIVE_MODEL", "qwen-test")
    monkeypatch.setenv("DSPYTEACH_ACTIVE_API_BASE", "http://localhost:1234/v1")
    monkeypatch.setenv("DSPYTEACH_ACTIVE_API_KEY", "lm-studio")

    overview_payload = {
        "choices": [
            {
                "message": {
                    "content": json.dumps(
                        {
                            "overview": "A solid overview.",
                            "section_notes": ["First section", "Second section"],
                        }
                    )
                }
            }
        ]
    }
    teachings_payload = {
        "choices": [
            {
                "message": {
                    "content": json.dumps(
                        {
                            "key_concepts": ["Concept A", "Concept B"],
                            "practical_steps": ["Step 1", "Step 2"],
                            "pitfalls": ["Pitfall 1"],
                            "references": [],
                            "usage_patterns": ["Pattern 1"],
                            "key_functions": ["Function 1"],
                            "code_walkthroughs": ["Walkthrough 1"],
                            "integration_notes": ["Integration 1"],
                            "testing_focus": ["Test 1"],
                        }
                    )
                }
            }
        ]
    }

    report_payload = {
        "choices": [{"message": {"content": "# Report\n\nBody"}}]
    }

    responses = [
        _Response(overview_payload),
        _Response(teachings_payload),
        _Response(report_payload),
    ]

    def fake_urlopen(req: object, timeout: float = 0.0):
        return responses.pop(0)

    analyzer = FileTeachingAnalyzer(config=TeachingConfig(report_refine_attempts=1))

    with mock.patch("dspy_file.file_analyzer.request.urlopen", side_effect=fake_urlopen) as urlopen_mock:
        prediction = analyzer(file_path="README.md", file_content="# Title\n\nBody")

    assert urlopen_mock.call_count == 3
    first_request = urlopen_mock.call_args_list[0].args[0]
    first_payload = json.loads(first_request.data.decode("utf-8"))
    third_request = urlopen_mock.call_args_list[2].args[0]
    third_payload = json.loads(third_request.data.decode("utf-8"))
    assert first_payload["response_format"]["type"] == "json_schema"
    assert "response_format" not in third_payload
    assert prediction.report.report_markdown == "# Report\n\nBody"
    assert prediction.structured["section_notes"] == ["- First section", "- Second section"]
    assert prediction.structured["key_concepts"] == ["- Concept A", "- Concept B"]
