# 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 Sprint 8 skills: Cal.com, Jitsi, KoboToolbox, Instagram."""

from unittest.mock import MagicMock, patch

HTTPX_GET = "familiar.skills._api_utils.httpx.get"
HTTPX_POST = "familiar.skills._api_utils.httpx.post"
CACHE_GET = "familiar.core.api_cache.get_cached"
CACHE_SET = "familiar.core.api_cache.set_cached"

# ── Env helpers ──────────────────────────────────────────────────────────────

CALCOM_ENV = {"CALCOM_API_KEY": "cal_test_abc123", "CALCOM_URL": "https://api.cal.com"}
JITSI_ENV = {"JITSI_URL": "https://meet.jit.si"}
JITSI_JWT_ENV = {
    "JITSI_URL": "https://jitsi.example.com",
    "JITSI_APP_ID": "myapp",
    "JITSI_APP_SECRET": "supersecret",
}
KOBO_ENV = {"KOBO_URL": "https://kf.kobotoolbox.org", "KOBO_TOKEN": "tok_abc123"}
IG_ENV = {
    "INSTAGRAM_ACCESS_TOKEN": "IGtoken123",
    "INSTAGRAM_BUSINESS_ID": "17841405822304914",
}


def _calcom_env(key: str) -> str:
    return CALCOM_ENV.get(key, "")


def _jitsi_env(key: str) -> str:
    return JITSI_ENV.get(key, "")


def _jitsi_jwt_env(key: str) -> str:
    return JITSI_JWT_ENV.get(key, "")


def _kobo_env(key: str) -> str:
    return KOBO_ENV.get(key, "")


def _ig_env(key: str) -> str:
    return IG_ENV.get(key, "")


def _mock_response(data, status_code=200):
    resp = MagicMock()
    resp.status_code = status_code
    resp.json.return_value = data
    resp.raise_for_status = MagicMock()
    return resp


# ── Cal.com tests ────────────────────────────────────────────────────────────


class TestCalcomAvailability:
    def test_no_config(self):
        from familiar.skills.calcom.skill import calcom_availability

        with patch("familiar.skills.calcom.skill.get_env", return_value=""):
            result = calcom_availability({})
        assert "not configured" in result.lower()

    def test_missing_dates(self):
        from familiar.skills.calcom.skill import calcom_availability

        with patch("familiar.skills.calcom.skill.get_env", side_effect=_calcom_env):
            result = calcom_availability({})
        assert "start_date" in result.lower()

    def test_success(self):
        from familiar.skills.calcom.skill import calcom_availability

        resp = _mock_response(
            {
                "data": {
                    "2026-03-10": [
                        {
                            "start": "2026-03-10T09:00:00.000Z",
                            "end": "2026-03-10T09:30:00.000Z",
                        },
                        {
                            "start": "2026-03-10T10:00:00.000Z",
                            "end": "2026-03-10T10:30:00.000Z",
                        },
                    ]
                }
            }
        )
        with (
            patch("familiar.skills.calcom.skill.get_env", side_effect=_calcom_env),
            patch(HTTPX_GET, return_value=resp),
            patch(CACHE_GET, return_value=None),
            patch(CACHE_SET),
        ):
            result = calcom_availability({"start_date": "2026-03-10", "end_date": "2026-03-12"})
        assert "Available Slots" in result
        assert "09:00" in result


class TestCalcomBookings:
    def test_success(self):
        from familiar.skills.calcom.skill import calcom_bookings

        resp = _mock_response(
            {
                "data": [
                    {
                        "title": "Team standup",
                        "uid": "bk_001",
                        "status": "accepted",
                        "start": "2026-03-10T09:00:00Z",
                        "end": "2026-03-10T09:30:00Z",
                        "attendees": [{"name": "Alice", "email": "alice@test.com"}],
                    }
                ]
            }
        )
        with (
            patch("familiar.skills.calcom.skill.get_env", side_effect=_calcom_env),
            patch(HTTPX_GET, return_value=resp),
            patch(CACHE_GET, return_value=None),
            patch(CACHE_SET),
        ):
            result = calcom_bookings({})
        assert "Team standup" in result
        assert "accepted" in result


class TestCalcomCreateEventType:
    def test_no_title(self):
        from familiar.skills.calcom.skill import calcom_create_event_type

        with patch("familiar.skills.calcom.skill.get_env", side_effect=_calcom_env):
            result = calcom_create_event_type({})
        assert "title" in result.lower()

    def test_success(self):
        from familiar.skills.calcom.skill import calcom_create_event_type

        resp = _mock_response(
            {
                "data": {
                    "id": 42,
                    "title": "Consultation",
                    "slug": "consultation",
                    "bookingUrl": "https://cal.com/user/consultation",
                }
            }
        )
        with (
            patch("familiar.skills.calcom.skill.get_env", side_effect=_calcom_env),
            patch(HTTPX_POST, return_value=resp),
        ):
            result = calcom_create_event_type({"title": "Consultation", "length_minutes": 60})
        assert "created" in result.lower()
        assert "Consultation" in result


class TestCalcomCancel:
    def test_no_uid(self):
        from familiar.skills.calcom.skill import calcom_cancel

        with patch("familiar.skills.calcom.skill.get_env", side_effect=_calcom_env):
            result = calcom_cancel({})
        assert "booking_uid" in result.lower()

    def test_success(self):
        from familiar.skills.calcom.skill import calcom_cancel

        resp = _mock_response({"data": {"uid": "bk_001", "status": "cancelled"}})
        with (
            patch("familiar.skills.calcom.skill.get_env", side_effect=_calcom_env),
            patch(HTTPX_POST, return_value=resp),
        ):
            result = calcom_cancel({"booking_uid": "bk_001"})
        assert "bk_001" in result
        assert "cancelled" in result


class TestCalcomTools:
    def test_tools_registered(self):
        from familiar.skills.calcom.skill import TOOLS

        names = {t["name"] for t in TOOLS}
        assert "calcom_availability" in names
        assert "calcom_bookings" in names
        assert "calcom_create_event_type" in names
        assert "calcom_cancel" in names


# ── Jitsi tests ──────────────────────────────────────────────────────────────


class TestCreateMeetingLink:
    def test_basic_link(self):
        from familiar.skills.jitsi.skill import create_meeting_link

        with patch("familiar.skills.jitsi.skill.get_env", side_effect=_jitsi_env):
            result = create_meeting_link({"topic": "Team sync"})
        assert "meet.jit.si" in result
        assert "Meeting link created" in result

    def test_custom_room(self):
        from familiar.skills.jitsi.skill import create_meeting_link

        with patch("familiar.skills.jitsi.skill.get_env", side_effect=_jitsi_env):
            result = create_meeting_link({"room_name": "my-custom-room"})
        assert "my-custom-room" in result

    def test_jwt_link(self):
        from familiar.skills.jitsi.skill import create_meeting_link

        with patch("familiar.skills.jitsi.skill.get_env", side_effect=_jitsi_jwt_env):
            result = create_meeting_link({"topic": "Private meeting", "moderator": True})
        assert "jitsi.example.com" in result
        # JWT may or may not be available (depends on pyjwt)
        if "jwt=" in result:
            assert "Moderator" in result


class TestJitsiStatus:
    def test_public(self):
        from familiar.skills.jitsi.skill import jitsi_status

        with patch("familiar.skills.jitsi.skill.get_env", side_effect=_jitsi_env):
            result = jitsi_status({})
        assert "meet.jit.si" in result
        assert "Disabled" in result

    def test_self_hosted_jwt(self):
        from familiar.skills.jitsi.skill import jitsi_status

        with patch("familiar.skills.jitsi.skill.get_env", side_effect=_jitsi_jwt_env):
            result = jitsi_status({})
        assert "Self-hosted" in result
        assert "Enabled" in result


class TestJitsiTools:
    def test_tools_registered(self):
        from familiar.skills.jitsi.skill import TOOLS

        names = {t["name"] for t in TOOLS}
        assert "create_meeting_link" in names
        assert "jitsi_status" in names


# ── KoboToolbox tests ───────────────────────────────────────────────────────


class TestKoboForms:
    def test_no_config(self):
        from familiar.skills.kobo.skill import kobo_forms

        with patch("familiar.skills.kobo.skill.get_env", return_value=""):
            result = kobo_forms({})
        assert "not configured" in result.lower()

    def test_success(self):
        from familiar.skills.kobo.skill import kobo_forms

        resp = _mock_response(
            {
                "count": 2,
                "results": [
                    {
                        "name": "Client Intake",
                        "uid": "aAbBcCdD",
                        "date_modified": "2026-03-01T10:00:00Z",
                        "owner": "socialworker1",
                        "deployment__active": True,
                    },
                    {
                        "name": "Follow-up Survey",
                        "uid": "eEfFgGhH",
                        "date_modified": "2026-02-15T08:00:00Z",
                        "owner": "socialworker1",
                        "deployment__active": False,
                    },
                ],
            }
        )
        with (
            patch("familiar.skills.kobo.skill.get_env", side_effect=_kobo_env),
            patch(HTTPX_GET, return_value=resp),
            patch(CACHE_GET, return_value=None),
            patch(CACHE_SET),
        ):
            result = kobo_forms({})
        assert "Client Intake" in result
        assert "Active" in result
        assert "Draft" in result


class TestKoboSubmissions:
    def test_no_uid(self):
        from familiar.skills.kobo.skill import kobo_submissions

        with patch("familiar.skills.kobo.skill.get_env", side_effect=_kobo_env):
            result = kobo_submissions({})
        assert "asset_uid" in result.lower()

    def test_success(self):
        from familiar.skills.kobo.skill import kobo_submissions

        resp = _mock_response(
            {
                "count": 1,
                "results": [
                    {
                        "_id": 98765,
                        "_submission_time": "2026-03-05T09:15:30Z",
                        "_submitted_by": "socialworker2",
                        "client_name": "Jane Doe",
                        "date_of_birth": "1990-05-12",
                    }
                ],
            }
        )
        with (
            patch("familiar.skills.kobo.skill.get_env", side_effect=_kobo_env),
            patch(HTTPX_GET, return_value=resp),
            patch(CACHE_GET, return_value=None),
            patch(CACHE_SET),
        ):
            result = kobo_submissions({"asset_uid": "aAbBcCdD"})
        assert "98765" in result
        assert "Jane Doe" in result


class TestKoboFormStats:
    def test_success(self):
        from familiar.skills.kobo.skill import kobo_form_stats

        resp = _mock_response(
            {
                "name": "Client Intake",
                "uid": "aAbBcCdD",
                "owner": "socialworker1",
                "date_created": "2026-01-15T10:30:00Z",
                "date_modified": "2026-03-01T14:22:00Z",
                "deployment__active": True,
                "deployment__submission_count": 42,
            }
        )
        with (
            patch("familiar.skills.kobo.skill.get_env", side_effect=_kobo_env),
            patch(HTTPX_GET, return_value=resp),
            patch(CACHE_GET, return_value=None),
            patch(CACHE_SET),
        ):
            result = kobo_form_stats({"asset_uid": "aAbBcCdD"})
        assert "Client Intake" in result
        assert "42" in result


class TestKoboTools:
    def test_tools_registered(self):
        from familiar.skills.kobo.skill import TOOLS

        names = {t["name"] for t in TOOLS}
        assert "kobo_forms" in names
        assert "kobo_submissions" in names
        assert "kobo_form_stats" in names


# ── Instagram tests ──────────────────────────────────────────────────────────


class TestIgInsights:
    def test_no_config(self):
        from familiar.skills.instagram.skill import ig_insights

        with patch("familiar.skills.instagram.skill.get_env", return_value=""):
            result = ig_insights({})
        assert "not configured" in result.lower()

    def test_success(self):
        from familiar.skills.instagram.skill import ig_insights

        resp = _mock_response(
            {
                "data": [
                    {
                        "name": "impressions",
                        "title": "Impressions",
                        "values": [{"value": 5432, "end_time": "2026-03-06T00:00:00Z"}],
                    },
                    {
                        "name": "reach",
                        "title": "Reach",
                        "values": [{"value": 1234, "end_time": "2026-03-06T00:00:00Z"}],
                    },
                ]
            }
        )
        with (
            patch("familiar.skills.instagram.skill.get_env", side_effect=_ig_env),
            patch(HTTPX_GET, return_value=resp),
            patch(CACHE_GET, return_value=None),
            patch(CACHE_SET),
        ):
            result = ig_insights({})
        assert "5,432" in result
        assert "Reach" in result


class TestIgRecentMedia:
    def test_success(self):
        from familiar.skills.instagram.skill import ig_recent_media

        resp = _mock_response(
            {
                "data": [
                    {
                        "id": "post_001",
                        "caption": "New artwork just dropped!",
                        "media_type": "IMAGE",
                        "timestamp": "2026-03-05T18:10:00+0000",
                        "permalink": "https://instagram.com/p/ABC123/",
                    }
                ]
            }
        )
        with (
            patch("familiar.skills.instagram.skill.get_env", side_effect=_ig_env),
            patch(HTTPX_GET, return_value=resp),
            patch(CACHE_GET, return_value=None),
            patch(CACHE_SET),
        ):
            result = ig_recent_media({})
        assert "New artwork" in result
        assert "Photo" in result


class TestIgAudience:
    def test_success(self):
        from familiar.skills.instagram.skill import ig_audience

        resp = _mock_response(
            {
                "data": [
                    {
                        "name": "audience_country",
                        "title": "Audience Country",
                        "values": [{"value": {"US": 45.2, "CA": 12.3, "UK": 8.9}}],
                    }
                ]
            }
        )
        with (
            patch("familiar.skills.instagram.skill.get_env", side_effect=_ig_env),
            patch(HTTPX_GET, return_value=resp),
            patch(CACHE_GET, return_value=None),
            patch(CACHE_SET),
        ):
            result = ig_audience({})
        assert "US" in result
        assert "45.2" in result


class TestInstagramTools:
    def test_tools_registered(self):
        from familiar.skills.instagram.skill import TOOLS

        names = {t["name"] for t in TOOLS}
        assert "ig_insights" in names
        assert "ig_recent_media" in names
        assert "ig_audience" in names
