# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""Tests for Mastodon and Bluesky skill tools."""

import os
from unittest import mock

# =============================================================================
# Mastodon Skill tests
# =============================================================================


class TestMastodonSkill:
    """Tests for Mastodon skill tools."""

    def test_tool_count(self):
        from familiar.skills.mastodon.skill import TOOLS

        assert len(TOOLS) == 5

    def test_tool_names(self):
        from familiar.skills.mastodon.skill import TOOLS

        names = {t["name"] for t in TOOLS}
        expected = {
            "mastodon_post", "mastodon_timeline", "mastodon_mentions",
            "mastodon_search", "mastodon_profile",
        }
        assert names == expected

    def test_all_tools_have_handlers(self):
        from familiar.skills.mastodon.skill import TOOLS

        for tool in TOOLS:
            assert callable(tool["handler"]), f"{tool['name']} missing handler"
            assert tool.get("category") == "social"

    def test_all_tools_have_input_schema(self):
        from familiar.skills.mastodon.skill import TOOLS

        for tool in TOOLS:
            assert "input_schema" in tool, f"{tool['name']} missing input_schema"
            assert tool["input_schema"]["type"] == "object"

    def test_post_requires_text(self):
        from familiar.skills.mastodon.skill import TOOLS

        post = next(t for t in TOOLS if t["name"] == "mastodon_post")
        assert "text" in post["input_schema"].get("required", [])

    def test_search_requires_query(self):
        from familiar.skills.mastodon.skill import TOOLS

        search = next(t for t in TOOLS if t["name"] == "mastodon_search")
        assert "query" in search["input_schema"].get("required", [])

    def test_not_configured_returns_message(self):
        from familiar.skills.mastodon.skill import mastodon_post

        with mock.patch.dict(os.environ, {}, clear=True):
            result = mastodon_post({"text": "hello"})
            assert "not configured" in result.lower()

    def test_post_empty_text_returns_error(self):
        from familiar.skills.mastodon.skill import mastodon_post

        with mock.patch.dict(os.environ, {
            "MASTODON_INSTANCE_URL": "https://example.org",
            "MASTODON_ACCESS_TOKEN": "test",
        }):
            result = mastodon_post({"text": ""})
            assert "provide" in result.lower()

    def test_timeline_not_configured(self):
        from familiar.skills.mastodon.skill import mastodon_timeline

        with mock.patch.dict(os.environ, {}, clear=True):
            result = mastodon_timeline({})
            assert "not configured" in result.lower()

    def test_profile_not_configured(self):
        from familiar.skills.mastodon.skill import mastodon_profile

        with mock.patch.dict(os.environ, {}, clear=True):
            result = mastodon_profile({})
            assert "not configured" in result.lower()


# =============================================================================
# Bluesky Skill tests
# =============================================================================


class TestBlueskySkill:
    """Tests for Bluesky skill tools."""

    def test_tool_count(self):
        from familiar.skills.bluesky.skill import TOOLS

        assert len(TOOLS) == 5

    def test_tool_names(self):
        from familiar.skills.bluesky.skill import TOOLS

        names = {t["name"] for t in TOOLS}
        expected = {
            "bluesky_post", "bluesky_timeline", "bluesky_notifications",
            "bluesky_search", "bluesky_profile",
        }
        assert names == expected

    def test_all_tools_have_handlers(self):
        from familiar.skills.bluesky.skill import TOOLS

        for tool in TOOLS:
            assert callable(tool["handler"]), f"{tool['name']} missing handler"
            assert tool.get("category") == "social"

    def test_all_tools_have_input_schema(self):
        from familiar.skills.bluesky.skill import TOOLS

        for tool in TOOLS:
            assert "input_schema" in tool, f"{tool['name']} missing input_schema"
            assert tool["input_schema"]["type"] == "object"

    def test_post_requires_text(self):
        from familiar.skills.bluesky.skill import TOOLS

        post = next(t for t in TOOLS if t["name"] == "bluesky_post")
        assert "text" in post["input_schema"].get("required", [])

    def test_search_requires_query(self):
        from familiar.skills.bluesky.skill import TOOLS

        search = next(t for t in TOOLS if t["name"] == "bluesky_search")
        assert "query" in search["input_schema"].get("required", [])

    def test_not_configured_returns_message(self):
        from familiar.skills.bluesky.skill import bluesky_post

        with mock.patch.dict(os.environ, {}, clear=True):
            result = bluesky_post({"text": "hello"})
            assert "not configured" in result.lower()

    def test_post_empty_text_returns_error(self):
        from familiar.skills.bluesky.skill import bluesky_post

        with mock.patch.dict(os.environ, {
            "BLUESKY_HANDLE": "test.bsky.social",
            "BLUESKY_APP_PASSWORD": "xxxx",
        }):
            result = bluesky_post({"text": ""})
            assert "provide" in result.lower()

    def test_timeline_not_configured(self):
        from familiar.skills.bluesky.skill import bluesky_timeline

        with mock.patch.dict(os.environ, {}, clear=True):
            result = bluesky_timeline({})
            assert "not configured" in result.lower()

    def test_profile_not_configured(self):
        from familiar.skills.bluesky.skill import bluesky_profile

        with mock.patch.dict(os.environ, {}, clear=True):
            result = bluesky_profile({})
            assert "not configured" in result.lower()
