# 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 Event Management skill — events, vendors, volunteers, milestones."""

from unittest import mock

import pytest


@pytest.fixture(autouse=True)
def _patch_data_dir(tmp_path):
    """Redirect all event stores to tmp_path."""
    with mock.patch("familiar.skills.events.skill._data_dir", return_value=tmp_path):
        yield


# ── EventStore Tests ──────────────────────────────────────────────────


class TestEventStore:
    def test_create_event(self):
        from familiar.skills.events.skill import get_event_store

        store = get_event_store()
        eid = store.create_event(name="Trans Pride Seattle", event_type="pride_march")
        assert eid.startswith("ev_")
        event = store.get_event(eid)
        assert event["name"] == "Trans Pride Seattle"
        assert event["event_type"] == "pride_march"
        assert event["status"] == "planning"

    def test_create_event_with_milestones(self):
        from familiar.skills.events.skill import get_event_store

        store = get_event_store()
        eid = store.create_event(
            name="Gala Night",
            event_type="gala",
            start_date="2026-09-15",
        )
        event = store.get_event(eid)
        assert len(event["milestones"]) == 10  # gala has 10 milestones

    def test_create_event_no_milestones_without_date(self):
        from familiar.skills.events.skill import get_event_store

        store = get_event_store()
        eid = store.create_event(name="TBD Event", auto_milestones=True)
        event = store.get_event(eid)
        assert len(event["milestones"]) == 0  # no start_date = no milestones

    def test_list_events_filter_by_status(self):
        from familiar.skills.events.skill import get_event_store

        store = get_event_store()
        store.create_event(name="Event A", status="planning")
        store.create_event(name="Event B", status="completed")
        planning = store.list_events(status="planning")
        assert len(planning) == 1
        assert planning[0]["name"] == "Event A"

    def test_update_event(self):
        from familiar.skills.events.skill import get_event_store

        store = get_event_store()
        eid = store.create_event(name="Old Name")
        result = store.update_event(eid, {"name": "New Name", "budget_total": 5000})
        assert result["name"] == "New Name"
        assert result["budget_total"] == 5000

    def test_update_event_nonexistent(self):
        from familiar.skills.events.skill import get_event_store

        store = get_event_store()
        result = store.update_event("ev_nonexistent", {"name": "X"})
        assert result is None

    def test_delete_event(self):
        from familiar.skills.events.skill import get_event_store

        store = get_event_store()
        eid = store.create_event(name="Delete Me")
        assert store.delete_event(eid) is True
        assert store.get_event(eid) is None

    def test_delete_event_nonexistent(self):
        from familiar.skills.events.skill import get_event_store

        store = get_event_store()
        assert store.delete_event("ev_nonexistent") is False

    def test_invalid_event_type_defaults(self):
        from familiar.skills.events.skill import get_event_store

        store = get_event_store()
        eid = store.create_event(name="Test", event_type="invalid_type")
        event = store.get_event(eid)
        assert event["event_type"] == "meeting"  # defaults to meeting

    def test_budget_tracking(self):
        from familiar.skills.events.skill import get_event_store

        store = get_event_store()
        eid = store.create_event(name="Gala", budget_total=50000)
        event = store.get_event(eid)
        assert event["budget_total"] == 50000
        assert event["budget_spent"] == 0

    def test_count_upcoming(self):
        from familiar.skills.events.skill import get_event_store

        store = get_event_store()
        store.create_event(name="A", status="planning")
        store.create_event(name="B", status="confirmed")
        store.create_event(name="C", status="completed")
        assert store.count_upcoming() == 2


class TestMilestones:
    def test_milestone_auto_generation_pride_march(self):
        from familiar.skills.events.skill import get_event_store

        store = get_event_store()
        eid = store.create_event(name="Pride", event_type="pride_march", start_date="2026-06-28")
        event = store.get_event(eid)
        assert len(event["milestones"]) == 12
        # First milestone should be earliest (most days before)
        assert event["milestones"][0]["name"] == "Form planning committee"
        # All milestones should have pending status
        assert all(m["status"] == "pending" for m in event["milestones"])

    def test_update_milestone_status(self):
        from familiar.skills.events.skill import get_event_store

        store = get_event_store()
        eid = store.create_event(name="Gala", event_type="gala", start_date="2026-09-15")
        result = store.update_milestone(eid, 0, "done")
        assert result["status"] == "done"
        assert result["completed_at"] is not None

    def test_update_milestone_invalid_index(self):
        from familiar.skills.events.skill import get_event_store

        store = get_event_store()
        eid = store.create_event(name="Test", event_type="meeting", start_date="2026-04-01")
        result = store.update_milestone(eid, 99, "done")
        assert result is None

    def test_upcoming_milestones(self):
        from familiar.skills.events.skill import get_event_store

        store = get_event_store()
        # Create event with start date far enough that some milestones fall near today
        from datetime import datetime, timedelta, timezone

        near_date = (datetime.now(timezone.utc) + timedelta(days=14)).strftime("%Y-%m-%d")
        store.create_event(name="Soon Meeting", event_type="meeting", start_date=near_date)
        upcoming = store.get_upcoming_milestones(days=30)
        # Should find some milestones (meeting has 4 milestones at 14, 7, 3, -3 days)
        assert len(upcoming) > 0
        assert all("event_name" in m for m in upcoming)

    def test_milestone_due_dates_are_correct(self):
        from familiar.skills.events.skill import _generate_milestones

        milestones = _generate_milestones("meeting", "2026-06-15")
        # meeting: 14d, 7d, 3d, -3d before event
        assert milestones[0]["due_date"] == "2026-06-01"  # 14 days before
        assert milestones[1]["due_date"] == "2026-06-08"  # 7 days before
        assert milestones[2]["due_date"] == "2026-06-12"  # 3 days before
        assert milestones[3]["due_date"] == "2026-06-18"  # 3 days after


# ── VendorStore Tests ─────────────────────────────────────────────────


class TestVendorStore:
    def test_add_vendor(self):
        from familiar.skills.events.skill import get_vendor_store

        store = get_vendor_store()
        vid = store.add_vendor(
            event_id="ev_test1",
            name="Seattle Sound Co",
            service_type="sound",
            quote_amount=3500,
        )
        assert vid.startswith("vn_")
        vendors = store.list_vendors(event_id="ev_test1")
        assert len(vendors) == 1
        assert vendors[0]["name"] == "Seattle Sound Co"
        assert vendors[0]["contract_status"] == "inquired"

    def test_list_vendors_filter_by_event(self):
        from familiar.skills.events.skill import get_vendor_store

        store = get_vendor_store()
        store.add_vendor(event_id="ev_1", name="Vendor A")
        store.add_vendor(event_id="ev_2", name="Vendor B")
        v1 = store.list_vendors(event_id="ev_1")
        assert len(v1) == 1
        all_v = store.list_vendors()
        assert len(all_v) == 2

    def test_update_vendor(self):
        from familiar.skills.events.skill import get_vendor_store

        store = get_vendor_store()
        vid = store.add_vendor(event_id="ev_1", name="Test Vendor")
        result = store.update_vendor(
            vid,
            {
                "contract_status": "contracted",
                "insurance_verified": True,
            },
        )
        assert result["contract_status"] == "contracted"
        assert result["insurance_verified"] is True

    def test_delete_vendor(self):
        from familiar.skills.events.skill import get_vendor_store

        store = get_vendor_store()
        vid = store.add_vendor(event_id="ev_1", name="Delete Me")
        assert store.delete_vendor(vid) is True
        assert len(store.list_vendors()) == 0

    def test_invalid_service_type_defaults(self):
        from familiar.skills.events.skill import get_vendor_store

        store = get_vendor_store()
        store.add_vendor(event_id="ev_1", name="V", service_type="invalid")
        vendors = store.list_vendors()
        assert vendors[0]["service_type"] == "other"


# ── VolunteerStore Tests ──────────────────────────────────────────────


class TestVolunteerStore:
    def test_add_volunteer(self):
        from familiar.skills.events.skill import get_volunteer_store

        store = get_volunteer_store()
        vid = store.add_volunteer(
            event_id="ev_test1",
            name="Alex Johnson",
            role="coordinator",
            shift_start="08:00",
            shift_end="12:00",
        )
        assert vid.startswith("vol_")
        vols = store.list_volunteers(event_id="ev_test1")
        assert len(vols) == 1
        assert vols[0]["checked_in"] is False

    def test_check_in_volunteer(self):
        from familiar.skills.events.skill import get_volunteer_store

        store = get_volunteer_store()
        vid = store.add_volunteer(event_id="ev_1", name="Test Vol")
        assert store.check_in(vid) is True
        vols = store.list_volunteers()
        assert vols[0]["checked_in"] is True
        assert vols[0]["checked_in_at"] is not None

    def test_check_in_nonexistent(self):
        from familiar.skills.events.skill import get_volunteer_store

        store = get_volunteer_store()
        assert store.check_in("vol_nonexistent") is False

    def test_summary_by_role(self):
        from familiar.skills.events.skill import get_volunteer_store

        store = get_volunteer_store()
        store.add_volunteer(event_id="ev_1", name="A", role="security")
        store.add_volunteer(event_id="ev_1", name="B", role="security")
        store.add_volunteer(event_id="ev_1", name="C", role="medical")
        summary = store.summary_by_role("ev_1")
        assert summary == {"security": 2, "medical": 1}

    def test_list_volunteers_filter_by_event(self):
        from familiar.skills.events.skill import get_volunteer_store

        store = get_volunteer_store()
        store.add_volunteer(event_id="ev_1", name="A")
        store.add_volunteer(event_id="ev_2", name="B")
        assert len(store.list_volunteers(event_id="ev_1")) == 1
        assert len(store.list_volunteers()) == 2

    def test_delete_volunteer(self):
        from familiar.skills.events.skill import get_volunteer_store

        store = get_volunteer_store()
        vid = store.add_volunteer(event_id="ev_1", name="Delete Me")
        assert store.delete_volunteer(vid) is True
        assert len(store.list_volunteers()) == 0


# ── Tool Handler Tests ────────────────────────────────────────────────


class TestToolHandlers:
    def test_create_event_tool(self):
        from familiar.skills.events.skill import create_event

        result = create_event({"name": "Test Event", "event_type": "gala"})
        assert "Created event" in result
        assert "Test Event" in result

    def test_create_event_tool_with_milestones(self):
        from familiar.skills.events.skill import create_event

        result = create_event(
            {
                "name": "Pride March",
                "event_type": "pride_march",
                "start_date": "2026-06-28",
            }
        )
        assert "12 auto-generated milestones" in result

    def test_create_event_tool_no_name(self):
        from familiar.skills.events.skill import create_event

        result = create_event({})
        assert "Error" in result

    def test_list_events_tool_empty(self):
        from familiar.skills.events.skill import list_events

        result = list_events({})
        assert "No events found" in result

    def test_event_details_tool(self):
        from familiar.skills.events.skill import create_event, event_details, get_event_store

        create_event(
            {
                "name": "Gala",
                "event_type": "gala",
                "start_date": "2026-09-15",
                "budget_total": 25000,
            }
        )
        store = get_event_store()
        events = store.list_events()
        eid = events[0]["id"]
        result = event_details({"event_id": eid})
        assert "Gala" in result
        assert "$25,000" in result
        assert "Milestones" in result

    def test_event_timeline_tool(self):
        from familiar.skills.events.skill import create_event, event_timeline, get_event_store

        create_event(
            {
                "name": "Meeting",
                "event_type": "meeting",
                "start_date": "2026-04-15",
            }
        )
        store = get_event_store()
        events = store.list_events()
        result = event_timeline({"event_id": events[0]["id"]})
        assert "Timeline" in result

    def test_add_vendor_tool(self):
        from familiar.skills.events.skill import add_vendor

        result = add_vendor(
            {
                "event_id": "ev_test",
                "name": "Sound Vendor",
                "service_type": "sound",
            }
        )
        assert "Added vendor" in result

    def test_add_vendor_tool_no_name(self):
        from familiar.skills.events.skill import add_vendor

        result = add_vendor({"event_id": "ev_test"})
        assert "Error" in result

    def test_add_volunteer_tool(self):
        from familiar.skills.events.skill import add_volunteer

        result = add_volunteer(
            {
                "event_id": "ev_test",
                "name": "Alex",
                "role": "security",
            }
        )
        assert "Added volunteer" in result

    def test_volunteer_summary_tool(self):
        from familiar.skills.events.skill import add_volunteer, volunteer_summary

        add_volunteer({"event_id": "ev_1", "name": "A", "role": "security"})
        add_volunteer({"event_id": "ev_1", "name": "B", "role": "medical"})
        result = volunteer_summary({"event_id": "ev_1"})
        assert "2 total" in result


# ── Persistence Tests ─────────────────────────────────────────────────


class TestPersistence:
    def test_event_persists_across_instances(self):
        from familiar.skills.events.skill import get_event_store

        store1 = get_event_store()
        store1.create_event(name="Persistent Event")
        store2 = get_event_store()
        events = store2.list_events()
        assert len(events) == 1
        assert events[0]["name"] == "Persistent Event"

    def test_vendor_persists_across_instances(self):
        from familiar.skills.events.skill import get_vendor_store

        store1 = get_vendor_store()
        store1.add_vendor(event_id="ev_1", name="Persistent Vendor")
        store2 = get_vendor_store()
        assert len(store2.list_vendors()) == 1

    def test_volunteer_persists_across_instances(self):
        from familiar.skills.events.skill import get_volunteer_store

        store1 = get_volunteer_store()
        store1.add_volunteer(event_id="ev_1", name="Persistent Vol")
        store2 = get_volunteer_store()
        assert len(store2.list_volunteers()) == 1
