# 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 Advocacy & Legislative Tracking skill."""

from unittest import mock

import pytest


@pytest.fixture(autouse=True)
def _patch_data_dir(tmp_path):
    with mock.patch("familiar.skills.advocacy.skill._data_dir", return_value=tmp_path):
        yield


# ── BillStore Tests ───────────────────────────────────────────────────


class TestBillStore:
    def test_track_bill(self):
        from familiar.skills.advocacy.skill import get_bill_store

        store = get_bill_store()
        bid = store.track_bill(bill_number="HB 1234", title="Anti-Trans Ban")
        assert bid.startswith("bill_")
        bill = store.get_bill(bid)
        assert bill["bill_number"] == "HB 1234"
        assert bill["status"] == "introduced"
        assert bill["our_position"] == "watch"

    def test_duplicate_bill_number(self):
        from familiar.skills.advocacy.skill import get_bill_store

        store = get_bill_store()
        store.track_bill(bill_number="HB 1234", title="Test", session="2026")
        result = store.track_bill(bill_number="HB 1234", title="Dupe", session="2026")
        assert result is None

    def test_list_bills_filter_position(self):
        from familiar.skills.advocacy.skill import get_bill_store

        store = get_bill_store()
        store.track_bill(bill_number="HB 1", title="A", our_position="support")
        store.track_bill(bill_number="HB 2", title="B", our_position="oppose")
        support = store.list_bills(position="support")
        assert len(support) == 1
        assert support[0]["bill_number"] == "HB 1"

    def test_list_bills_filter_priority(self):
        from familiar.skills.advocacy.skill import get_bill_store

        store = get_bill_store()
        store.track_bill(bill_number="HB 1", title="A", priority="critical")
        store.track_bill(bill_number="HB 2", title="B", priority="low")
        critical = store.list_bills(priority="critical")
        assert len(critical) == 1

    def test_update_bill(self):
        from familiar.skills.advocacy.skill import get_bill_store

        store = get_bill_store()
        bid = store.track_bill(bill_number="HB 1", title="Test")
        result = store.update_bill(bid, {"status": "committee", "our_position": "oppose"})
        assert result["status"] == "committee"
        assert result["our_position"] == "oppose"

    def test_remove_bill(self):
        from familiar.skills.advocacy.skill import get_bill_store

        store = get_bill_store()
        bid = store.track_bill(bill_number="HB 1", title="Test")
        assert store.remove_bill(bid) is True
        assert store.get_bill(bid) is None

    def test_count_active(self):
        from familiar.skills.advocacy.skill import get_bill_store

        store = get_bill_store()
        store.track_bill(bill_number="HB 1", title="A", status="committee")
        store.track_bill(bill_number="HB 2", title="B", status="dead")
        assert store.count_active() == 1


class TestHearings:
    def test_add_hearing(self):
        from familiar.skills.advocacy.skill import get_bill_store

        store = get_bill_store()
        bid = store.track_bill(bill_number="HB 1", title="Test")
        assert store.add_hearing(bid, "2026-04-01", time="10:00", location="Olympia") is True
        bill = store.get_bill(bid)
        assert len(bill["hearing_dates"]) == 1
        assert bill["hearing_dates"][0]["location"] == "Olympia"

    def test_upcoming_hearings(self):
        from datetime import datetime, timedelta, timezone

        from familiar.skills.advocacy.skill import get_bill_store

        store = get_bill_store()
        bid = store.track_bill(bill_number="HB 1", title="Test")
        future = (datetime.now(timezone.utc) + timedelta(days=5)).strftime("%Y-%m-%d")
        store.add_hearing(bid, future, location="Capitol")
        hearings = store.list_upcoming_hearings(days=10)
        assert len(hearings) == 1
        assert hearings[0]["bill_number"] == "HB 1"

    def test_no_hearings_for_dead_bills(self):
        from datetime import datetime, timedelta, timezone

        from familiar.skills.advocacy.skill import get_bill_store

        store = get_bill_store()
        bid = store.track_bill(bill_number="HB 1", title="Dead", status="dead")
        future = (datetime.now(timezone.utc) + timedelta(days=5)).strftime("%Y-%m-%d")
        store.add_hearing(bid, future)
        assert len(store.list_upcoming_hearings(days=10)) == 0


class TestTestimony:
    def test_add_testimony(self):
        from familiar.skills.advocacy.skill import get_bill_store

        store = get_bill_store()
        bid = store.track_bill(bill_number="HB 1", title="Test")
        assert store.add_testimony(bid, "Danni Askini", position="oppose") is True
        bill = store.get_bill(bid)
        assert len(bill["testimony_submitted"]) == 1
        assert bill["testimony_submitted"][0]["testifier"] == "Danni Askini"

    def test_add_coalition_partner(self):
        from familiar.skills.advocacy.skill import get_bill_store

        store = get_bill_store()
        bid = store.track_bill(bill_number="HB 1", title="Test")
        store.add_coalition_partner(bid, "ACLU Washington")
        store.add_coalition_partner(bid, "ACLU Washington")  # duplicate
        bill = store.get_bill(bid)
        assert len(bill["coalition_partners"]) == 1


# ── SupporterStore Tests ──────────────────────────────────────────────


class TestSupporterStore:
    def test_add_supporter(self):
        from familiar.skills.advocacy.skill import get_supporter_store

        store = get_supporter_store()
        sid = store.add_supporter(name="Alex", email="alex@example.com")
        assert sid.startswith("sup_")
        assert store.count_active() == 1

    def test_duplicate_email(self):
        from familiar.skills.advocacy.skill import get_supporter_store

        store = get_supporter_store()
        store.add_supporter(name="Alex", email="alex@example.com")
        result = store.add_supporter(name="Alex 2", email="alex@example.com")
        assert result is None

    def test_import_csv(self):
        from familiar.skills.advocacy.skill import get_supporter_store

        store = get_supporter_store()
        csv_text = "name,email,phone,zip\nAlice,alice@x.com,,98101\nBob,bob@x.com,,98102\n"
        count, errors = store.import_csv(csv_text)
        assert count == 2
        assert len(errors) == 0
        assert store.count_active() == 2

    def test_import_csv_duplicate(self):
        from familiar.skills.advocacy.skill import get_supporter_store

        store = get_supporter_store()
        store.add_supporter(name="Alice", email="alice@x.com")
        csv_text = "name,email\nAlice,alice@x.com\nBob,bob@x.com\n"
        count, errors = store.import_csv(csv_text)
        assert count == 1  # only Bob
        assert len(errors) == 1  # Alice duplicate

    def test_import_csv_missing_name(self):
        from familiar.skills.advocacy.skill import get_supporter_store

        store = get_supporter_store()
        csv_text = "name,email\n,noname@x.com\nBob,bob@x.com\n"
        count, errors = store.import_csv(csv_text)
        assert count == 1
        assert len(errors) == 1

    def test_remove_supporter(self):
        from familiar.skills.advocacy.skill import get_supporter_store

        store = get_supporter_store()
        sid = store.add_supporter(name="Alex")
        assert store.remove_supporter(sid) is True
        assert store.count_active() == 0

    def test_list_supporters_active_only(self):
        from familiar.skills.advocacy.skill import get_supporter_store

        store = get_supporter_store()
        sid = store.add_supporter(name="Alex")
        store.add_supporter(name="Bob")
        store.remove_supporter(sid)
        active = store.list_supporters(active_only=True)
        assert len(active) == 1
        all_s = store.list_supporters(active_only=False)
        assert len(all_s) == 2


# ── Action Alert Tests ────────────────────────────────────────────────


class TestActionAlerts:
    def test_draft_call_to_action(self):
        from familiar.skills.advocacy.skill import draft_action_alert

        bill = {
            "bill_number": "HB 1234",
            "title": "Anti-Trans Ban",
            "our_position": "oppose",
            "talking_points": ["Harms trans youth", "Violates civil rights"],
        }
        alert = draft_action_alert(bill, "call_to_action")
        assert "ACTION NEEDED" in alert["subject"]
        assert "oppose" in alert["body"].lower()
        assert "Harms trans youth" in alert["body"]

    def test_draft_hearing_notice(self):
        from familiar.skills.advocacy.skill import draft_action_alert

        bill = {
            "bill_number": "HB 1234",
            "title": "Test Bill",
            "our_position": "support",
            "hearing_dates": [{"date": "2026-04-01", "time": "10:00", "location": "Olympia"}],
        }
        alert = draft_action_alert(bill, "hearing_notice")
        assert "HEARING" in alert["subject"]
        assert "2026-04-01" in alert["body"]

    def test_draft_vote_alert(self):
        from familiar.skills.advocacy.skill import draft_action_alert

        bill = {"bill_number": "SB 5678", "title": "Test", "our_position": "support"}
        alert = draft_action_alert(bill, "vote_alert")
        assert "VOTE ALERT" in alert["subject"]

    def test_draft_victory_report(self):
        from familiar.skills.advocacy.skill import draft_action_alert

        bill = {
            "bill_number": "HB 1",
            "title": "Test",
            "our_position": "support",
            "status": "signed",
            "vote_history": [{"result": "passed", "vote_count": "65-33"}],
        }
        alert = draft_action_alert(bill, "victory_report")
        assert "UPDATE" in alert["subject"]
        assert "passed" in alert["body"]

    def test_record_action_alert(self):
        from familiar.skills.advocacy.skill import get_bill_store

        store = get_bill_store()
        bid = store.track_bill(bill_number="HB 1", title="Test")
        store.record_action_alert(bid, "call_to_action", 150)
        bill = store.get_bill(bid)
        assert len(bill["action_alerts_sent"]) == 1
        assert bill["action_alerts_sent"][0]["recipients_count"] == 150


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


class TestToolHandlers:
    def test_track_bill_tool(self):
        from familiar.skills.advocacy.skill import track_bill

        result = track_bill({"bill_number": "HB 1234", "title": "Test Bill"})
        assert "Now tracking" in result

    def test_track_bill_tool_no_title(self):
        from familiar.skills.advocacy.skill import track_bill

        result = track_bill({"bill_number": "HB 1234"})
        assert "Error" in result

    def test_list_bills_tool_empty(self):
        from familiar.skills.advocacy.skill import list_bills

        result = list_bills({})
        assert "No bills tracked" in result

    def test_bill_details_tool(self):
        from familiar.skills.advocacy.skill import bill_details, get_bill_store, track_bill

        track_bill({"bill_number": "HB 1234", "title": "Test Bill"})
        store = get_bill_store()
        bills = store.list_bills()
        result = bill_details({"bill_id": bills[0]["id"]})
        assert "HB 1234" in result
        assert "Test Bill" in result

    def test_advocacy_dashboard_tool(self):
        from familiar.skills.advocacy.skill import advocacy_dashboard, track_bill

        track_bill(
            {
                "bill_number": "HB 1",
                "title": "Critical Bill",
                "priority": "critical",
            }
        )
        result = advocacy_dashboard({})
        assert "Advocacy Dashboard" in result
        assert "Critical Bills" in result

    def test_add_hearing_tool(self):
        from familiar.skills.advocacy.skill import add_hearing, get_bill_store, track_bill

        track_bill({"bill_number": "HB 1", "title": "Test"})
        store = get_bill_store()
        bid = store.list_bills()[0]["id"]
        result = add_hearing({"bill_id": bid, "date": "2026-04-01"})
        assert "Added hearing" in result

    def test_draft_alert_tool(self):
        from familiar.skills.advocacy.skill import draft_alert, get_bill_store, track_bill

        track_bill({"bill_number": "HB 1", "title": "Test", "our_position": "oppose"})
        store = get_bill_store()
        bid = store.list_bills()[0]["id"]
        result = draft_alert({"bill_id": bid, "alert_type": "call_to_action"})
        assert "ACTION NEEDED" in result

    def test_import_supporters_tool(self):
        from familiar.skills.advocacy.skill import import_supporters

        csv_data = "name,email\nAlice,alice@x.com\nBob,bob@x.com\n"
        result = import_supporters({"csv_data": csv_data})
        assert "Imported 2" in result


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


class TestPersistence:
    def test_bill_persists(self):
        from familiar.skills.advocacy.skill import get_bill_store

        store1 = get_bill_store()
        store1.track_bill(bill_number="HB 1", title="Persistent")
        store2 = get_bill_store()
        assert len(store2.list_bills()) == 1

    def test_supporter_persists(self):
        from familiar.skills.advocacy.skill import get_supporter_store

        store1 = get_supporter_store()
        store1.add_supporter(name="Persistent Person")
        store2 = get_supporter_store()
        assert store2.count_active() == 1
