"""Unit tests for Transport Mode (SPEC4)."""

import base64
from unittest.mock import patch

import pytest

from skillport.interfaces.mcp.instructions import build_xml_instructions
from skillport.interfaces.mcp.server import _get_registered_tools_list
from skillport.modules.skills.public.types import FileContent
from skillport.shared.config import Config


class TestRegisteredToolsList:
    """Tests for _get_registered_tools_list helper."""

    def test_local_mode_two_tools(self):
        """Local mode (stdio) registers 2 tools."""
        tools = _get_registered_tools_list(is_remote=False)
        assert tools == ["search_skills", "load_skill"]
        assert "read_skill_file" not in tools

    def test_remote_mode_three_tools(self):
        """Remote mode (HTTP) registers 3 tools including read_skill_file."""
        tools = _get_registered_tools_list(is_remote=True)
        assert tools == ["search_skills", "load_skill", "read_skill_file"]


class TestDynamicInstructions:
    """Dynamic instruction generation tests."""

    def test_local_mode_instructions_no_read_skill_file(self):
        """Local mode instructions don't mention read_skill_file in tools list."""
        cfg = Config(core_skills_mode="none")
        tools = ["search_skills", "load_skill"]

        with patch(
            "skillport.interfaces.mcp.instructions.get_core_skills",
            return_value=[],
        ):
            result = build_xml_instructions(cfg, tools)

        # Should mention using native Read tool
        assert "Use your Read tool" in result or "your native Read" in result
        # Tools section should not have read_skill_file
        lines = result.split("\n")
        tools_section = False
        tips_section = False
        for line in lines:
            if "## Tools" in line:
                tools_section = True
            if "## Tips" in line:
                tips_section = True
                tools_section = False
            if tools_section and not tips_section:
                if "read_skill_file" in line:
                    pytest.fail("read_skill_file should not be in Tools section for local mode")

    def test_remote_mode_instructions_has_read_skill_file(self):
        """Remote mode instructions include read_skill_file."""
        cfg = Config(core_skills_mode="none")
        tools = ["search_skills", "load_skill", "read_skill_file"]

        with patch(
            "skillport.interfaces.mcp.instructions.get_core_skills",
            return_value=[],
        ):
            result = build_xml_instructions(cfg, tools)

        # Should mention read_skill_file
        assert "read_skill_file" in result
        # Should mention encoding
        assert "encoding" in result or "base64" in result

    def test_instructions_workflow_differs_by_mode(self):
        """Workflow step 3 differs between local and remote mode."""
        cfg = Config(core_skills_mode="none")

        with patch(
            "skillport.interfaces.mcp.instructions.get_core_skills",
            return_value=[],
        ):
            local = build_xml_instructions(cfg, ["search_skills", "load_skill"])
            remote = build_xml_instructions(cfg, ["search_skills", "load_skill", "read_skill_file"])

        # Local mentions Read tool
        assert "Read tool" in local or "{path}/file" in local
        # Remote mentions read_skill_file
        assert "read_skill_file" in remote

    def test_default_tools_when_none_provided(self):
        """When registered_tools is None, defaults to local mode."""
        cfg = Config(core_skills_mode="none")

        with patch(
            "skillport.interfaces.mcp.instructions.get_core_skills",
            return_value=[],
        ):
            result = build_xml_instructions(cfg, None)

        # Should be local mode (no read_skill_file in Tools)
        assert "your Read tool" in result or "native Read" in result


class TestFileContentType:
    """FileContent type tests."""

    def test_file_content_encoding_default_utf8(self):
        """encoding defaults to utf-8."""
        fc = FileContent(content="hello", path="/test.txt", size=5)
        assert fc.encoding == "utf-8"

    def test_file_content_mime_type_default_text_plain(self):
        """mime_type defaults to text/plain."""
        fc = FileContent(content="hello", path="/test.txt", size=5)
        assert fc.mime_type == "text/plain"

    def test_file_content_with_base64_encoding(self):
        """FileContent can have base64 encoding."""
        fc = FileContent(
            content="SGVsbG8=",
            path="/test.bin",
            size=5,
            encoding="base64",
            mime_type="application/octet-stream",
        )
        assert fc.encoding == "base64"
        assert fc.mime_type == "application/octet-stream"

    def test_file_content_with_image_mime_type(self):
        """FileContent can have image mime type."""
        fc = FileContent(
            content="iVBORw0KGgo=",
            path="/test.png",
            size=100,
            encoding="base64",
            mime_type="image/png",
        )
        assert fc.mime_type == "image/png"


class TestReadSkillFileBinarySupport:
    """Binary file support in read_skill_file."""

    def test_text_file_returns_utf8(self, tmp_path):
        """Text files return encoding=utf-8."""
        # Setup test skill
        skill_dir = tmp_path / "test-skill"
        skill_dir.mkdir()
        text_file = skill_dir / "readme.txt"
        text_file.write_text("Hello, World!", encoding="utf-8")

        skill_record = {
            "id": "test-skill",
            "path": str(skill_dir),
            "category": "",
        }

        with patch(
            "skillport.modules.skills.public.read.idx_get_by_id",
            return_value=skill_record,
        ):
            from skillport.modules.skills.public.read import read_skill_file

            cfg = Config()
            result = read_skill_file("test-skill", "readme.txt", config=cfg)

        assert result.encoding == "utf-8"
        assert result.content == "Hello, World!"
        assert "text" in result.mime_type

    def test_binary_file_returns_base64(self, tmp_path):
        """Binary files return encoding=base64."""
        # Setup test skill with binary file
        skill_dir = tmp_path / "test-skill"
        skill_dir.mkdir()
        binary_file = skill_dir / "image.png"
        # Minimal PNG header
        png_bytes = bytes([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])
        binary_file.write_bytes(png_bytes)

        skill_record = {
            "id": "test-skill",
            "path": str(skill_dir),
            "category": "",
        }

        with patch(
            "skillport.modules.skills.public.read.idx_get_by_id",
            return_value=skill_record,
        ):
            from skillport.modules.skills.public.read import read_skill_file

            cfg = Config()
            result = read_skill_file("test-skill", "image.png", config=cfg)

        assert result.encoding == "base64"
        assert result.mime_type == "image/png"
        # Verify base64 can be decoded
        decoded = base64.b64decode(result.content)
        assert decoded == png_bytes

    def test_json_file_returns_utf8(self, tmp_path):
        """JSON files (text extension) return encoding=utf-8."""
        skill_dir = tmp_path / "test-skill"
        skill_dir.mkdir()
        json_file = skill_dir / "config.json"
        json_file.write_text('{"key": "value"}', encoding="utf-8")

        skill_record = {
            "id": "test-skill",
            "path": str(skill_dir),
            "category": "",
        }

        with patch(
            "skillport.modules.skills.public.read.idx_get_by_id",
            return_value=skill_record,
        ):
            from skillport.modules.skills.public.read import read_skill_file

            cfg = Config()
            result = read_skill_file("test-skill", "config.json", config=cfg)

        assert result.encoding == "utf-8"
        assert result.content == '{"key": "value"}'

    def test_yaml_file_returns_utf8(self, tmp_path):
        """YAML files return encoding=utf-8."""
        skill_dir = tmp_path / "test-skill"
        skill_dir.mkdir()
        yaml_file = skill_dir / "config.yaml"
        yaml_file.write_text("key: value", encoding="utf-8")

        skill_record = {
            "id": "test-skill",
            "path": str(skill_dir),
            "category": "",
        }

        with patch(
            "skillport.modules.skills.public.read.idx_get_by_id",
            return_value=skill_record,
        ):
            from skillport.modules.skills.public.read import read_skill_file

            cfg = Config()
            result = read_skill_file("test-skill", "config.yaml", config=cfg)

        assert result.encoding == "utf-8"
        assert result.content == "key: value"

    def test_unknown_extension_binary_returns_base64(self, tmp_path):
        """Unknown extensions with binary content return base64."""
        skill_dir = tmp_path / "test-skill"
        skill_dir.mkdir()
        # Use a truly unknown extension
        bin_file = skill_dir / "data.qqqxxx"
        bin_file.write_bytes(bytes([0x00, 0x01, 0x02, 0xFF]))

        skill_record = {
            "id": "test-skill",
            "path": str(skill_dir),
            "category": "",
        }

        with patch(
            "skillport.modules.skills.public.read.idx_get_by_id",
            return_value=skill_record,
        ):
            from skillport.modules.skills.public.read import read_skill_file

            cfg = Config()
            result = read_skill_file("test-skill", "data.qqqxxx", config=cfg)

        assert result.encoding == "base64"
        assert result.mime_type == "application/octet-stream"
