"""Minimal integration tests for BillingService."""

from datetime import UTC, datetime
from unittest.mock import Mock

import pytest

from augint_billing_lib.models import Link
from augint_billing_lib.service import BillingService


@pytest.mark.integration
class TestServiceIntegration:
    """Test BillingService with mocked adapters."""

    @pytest.fixture
    def mock_stripe(self):
        """Create mock Stripe adapter."""
        stripe = Mock()
        stripe.has_default_payment_method.return_value = True
        stripe.ensure_metered_subscription.return_value = "si_test123"
        stripe.cancel_subscription_if_any.return_value = None
        stripe.report_usage.return_value = {"id": "ur_test123", "object": "usage_record"}
        return stripe

    @pytest.fixture
    def mock_repo(self):
        """Create mock repository."""
        repo = Mock()
        repo.get_by_customer.return_value = Link(
            api_key_id="test-key-123",
            stripe_customer_id="cus_test123",
            plan="FREE",
            usage_plan_id="FREE_10K",
        )
        repo.get_by_api_key.return_value = Link(
            api_key_id="test-key-123",
            stripe_customer_id="cus_test123",
            plan="FREE",
            usage_plan_id="FREE_10K",
        )
        repo.scan_metered.return_value = []
        repo.save.return_value = None
        return repo

    @pytest.fixture
    def mock_usage(self):
        """Create mock usage source."""
        usage = Mock()
        usage.get_usage.return_value = 1000
        return usage

    @pytest.fixture
    def mock_admin(self):
        """Create mock plan admin."""
        admin = Mock()
        admin.move_key_to_plan.return_value = None
        return admin

    @pytest.fixture
    def service(self, mock_stripe, mock_repo, mock_usage, mock_admin):
        """Create BillingService with mocked dependencies."""
        return BillingService(
            repo=mock_repo,
            stripe=mock_stripe,
            usage=mock_usage,
            plan_admin=mock_admin,
        )

    def test_handle_stripe_event_setup_intent_succeeded(self, service, mock_stripe, mock_repo):
        """Test handling setup_intent.succeeded event."""
        event = {
            "type": "setup_intent.succeeded",
            "data": {
                "object": {
                    "customer": "cus_test123",
                    "payment_method": "pm_test123",
                }
            },
        }

        result = service.handle_stripe_event(event)

        # Should check for payment method
        mock_stripe.has_default_payment_method.assert_called_with("cus_test123")
        # Should ensure subscription
        mock_stripe.ensure_metered_subscription.assert_called_with("cus_test123")
        # Should return metered plan
        assert result["target_plan"] == "metered"
        assert result["stripe_customer_id"] == "cus_test123"
        assert result["subscription_item_id"] == "si_test123"

    def test_handle_stripe_event_invoice_payment_failed(self, service):
        """Test handling invoice.payment_failed event."""
        event = {
            "type": "invoice.payment_failed",
            "data": {
                "object": {
                    "customer": "cus_test123",
                }
            },
        }

        result = service.handle_stripe_event(event)

        # Should return free plan on payment failure
        assert result["target_plan"] == "free"
        assert result["stripe_customer_id"] == "cus_test123"
        assert result["subscription_item_id"] is None
        assert result["reason"] == "invoice.payment_failed"

    def test_reconcile_usage_window(self, service, mock_stripe, mock_repo, mock_usage):
        """Test reconciling usage for a time window."""
        # Setup: return metered customers
        metered_link = Link(
            api_key_id="metered-key-123",
            stripe_customer_id="cus_metered123",
            plan="METERED",
            usage_plan_id="METERED",
            metered_subscription_item_id="si_metered123",
        )
        mock_repo.scan_metered.return_value = [metered_link]

        # Run reconciliation
        from_ts = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC)
        to_ts = datetime(2024, 1, 1, 1, 0, 0, tzinfo=UTC)
        result = service.reconcile_usage_window(from_ts, to_ts)

        # Should scan for metered customers
        mock_repo.scan_metered.assert_called_once()
        # Should get usage for the customer
        mock_usage.get_usage.assert_called_with("METERED", "metered-key-123", from_ts, to_ts)
        # Should report usage to Stripe
        mock_stripe.report_usage.assert_called()

        # Result is a list of reports
        assert len(result) == 1
        assert result[0]["api_key_id"] == "metered-key-123"
        assert result[0]["units"] == 1000

    def test_reconcile_usage_window_no_metered_customers(self, service, mock_repo):
        """Test reconciling when no metered customers."""
        mock_repo.scan_metered.return_value = []

        from_ts = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC)
        to_ts = datetime(2024, 1, 1, 1, 0, 0, tzinfo=UTC)
        result = service.reconcile_usage_window(from_ts, to_ts)

        # Result is an empty list when no metered customers
        assert result == []
