from datetime import date
from decimal import Decimal

from django.contrib.auth import get_user_model
from django.test import TestCase, override_settings

from apps.accounts.models import Account, AccountGroup
from apps.currencies.models import Currency
from apps.transactions.models import (
    Transaction,
    TransactionCategory,
    TransactionTag,
)


@override_settings(
    STORAGES={
        "default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
        "staticfiles": {
            "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"
        },
    },
    WHITENOISE_AUTOREFRESH=True,
)
class MonthlySummaryFilterBehaviorTests(TestCase):
    """Tests for monthly summary views filter behavior.

    These tests verify that:
    1. Views work correctly without any filters
    2. Views work correctly with filters applied
    3. The filter detection logic properly uses different querysets
    4. Calculated values reflect the applied filters
    """

    def setUp(self):
        """Set up test data"""
        User = get_user_model()
        self.user = User.objects.create_user(
            email="testuser@test.com", password="testpass123"
        )
        self.client.login(username="testuser@test.com", password="testpass123")

        self.currency = Currency.objects.create(
            code="USD", name="US Dollar", decimal_places=2, prefix="$ "
        )
        self.account_group = AccountGroup.objects.create(name="Test Group")
        self.account = Account.objects.create(
            name="Test Account",
            group=self.account_group,
            currency=self.currency,
            is_asset=False,
        )
        self.category = TransactionCategory.objects.create(
            name="Test Category", owner=self.user
        )
        self.tag = TransactionTag.objects.create(name="TestTag", owner=self.user)

        # Create test transactions for December 2025
        # Income: 1000 (paid)
        self.income_transaction = Transaction.objects.create(
            account=self.account,
            type=Transaction.Type.INCOME,
            is_paid=True,
            date=date(2025, 12, 10),
            reference_date=date(2025, 12, 1),
            amount=Decimal("1000.00"),
            description="December Income",
            owner=self.user,
        )

        # Expense: 200 (paid)
        self.expense_transaction = Transaction.objects.create(
            account=self.account,
            type=Transaction.Type.EXPENSE,
            is_paid=True,
            date=date(2025, 12, 15),
            reference_date=date(2025, 12, 1),
            amount=Decimal("200.00"),
            description="December Expense",
            category=self.category,
            owner=self.user,
        )
        self.expense_transaction.tags.add(self.tag)

        # Expense: 150 (projected/unpaid)
        self.projected_expense = Transaction.objects.create(
            account=self.account,
            type=Transaction.Type.EXPENSE,
            is_paid=False,
            date=date(2025, 12, 20),
            reference_date=date(2025, 12, 1),
            amount=Decimal("150.00"),
            description="Projected Expense",
            owner=self.user,
        )

    def _get_currency_data(self, context_dict):
        """Helper to extract data for our test currency from context dict.

        The context dict is keyed by currency ID, so we need to find
        the entry for our currency.
        """
        if not context_dict:
            return None
        for currency_id, data in context_dict.items():
            if data.get("currency", {}).get("code") == "USD":
                return data
        return None

    def _create_asset_income(self):
        asset_account = Account.objects.create(
            name="Asset Account",
            group=self.account_group,
            currency=self.currency,
            is_asset=True,
        )
        Transaction.objects.create(
            account=asset_account,
            type=Transaction.Type.INCOME,
            is_paid=True,
            date=date(2025, 12, 25),
            reference_date=date(2025, 12, 1),
            amount=Decimal("50.00"),
            description="Asset Income",
            owner=self.user,
        )
        return asset_account

    # --- monthly_summary view tests ---

    def test_monthly_summary_no_filter_returns_200(self):
        """Test that monthly_summary returns 200 without filters"""
        response = self.client.get(
            "/monthly/12/2025/summary/",
            HTTP_HX_REQUEST="true",
        )
        self.assertEqual(response.status_code, 200)

    def test_monthly_summary_no_filter_includes_all_transactions(self):
        """Without filters, summary should include all transactions"""
        response = self.client.get(
            "/monthly/12/2025/summary/",
            HTTP_HX_REQUEST="true",
        )
        context = response.context

        # income_current should have the income: 1000
        income_current = context.get("income_current", {})
        usd_data = self._get_currency_data(income_current)
        self.assertIsNotNone(usd_data)
        self.assertEqual(usd_data["income_current"], Decimal("1000.00"))

        # expense_current should have paid expense: 200
        expense_current = context.get("expense_current", {})
        usd_data = self._get_currency_data(expense_current)
        self.assertIsNotNone(usd_data)
        self.assertEqual(usd_data["expense_current"], Decimal("200.00"))

        # expense_projected should have unpaid expense: 150
        expense_projected = context.get("expense_projected", {})
        usd_data = self._get_currency_data(expense_projected)
        self.assertIsNotNone(usd_data)
        self.assertEqual(usd_data["expense_projected"], Decimal("150.00"))

    def test_monthly_summary_type_filter_only_income(self):
        """With type=IN filter, summary should only include income"""
        response = self.client.get(
            "/monthly/12/2025/summary/?type=IN",
            HTTP_HX_REQUEST="true",
        )
        context = response.context

        # income_current should still have 1000
        income_current = context.get("income_current", {})
        usd_data = self._get_currency_data(income_current)
        self.assertIsNotNone(usd_data)
        self.assertEqual(usd_data["income_current"], Decimal("1000.00"))

        # expense_current should be empty/zero (filtered out)
        expense_current = context.get("expense_current", {})
        usd_data = self._get_currency_data(expense_current)
        if usd_data:
            self.assertEqual(usd_data.get("expense_current", 0), Decimal("0"))

        # expense_projected should be empty/zero (filtered out)
        expense_projected = context.get("expense_projected", {})
        usd_data = self._get_currency_data(expense_projected)
        if usd_data:
            self.assertEqual(usd_data.get("expense_projected", 0), Decimal("0"))

    def test_monthly_summary_type_filter_only_expenses(self):
        """With type=EX filter, summary should only include expenses"""
        response = self.client.get(
            "/monthly/12/2025/summary/?type=EX",
            HTTP_HX_REQUEST="true",
        )
        context = response.context

        # income_current should be empty/zero (filtered out)
        income_current = context.get("income_current", {})
        usd_data = self._get_currency_data(income_current)
        if usd_data:
            self.assertEqual(usd_data.get("income_current", 0), Decimal("0"))

        # expense_current should have 200
        expense_current = context.get("expense_current", {})
        usd_data = self._get_currency_data(expense_current)
        self.assertIsNotNone(usd_data)
        self.assertEqual(usd_data["expense_current"], Decimal("200.00"))

        # expense_projected should have 150
        expense_projected = context.get("expense_projected", {})
        usd_data = self._get_currency_data(expense_projected)
        self.assertIsNotNone(usd_data)
        self.assertEqual(usd_data["expense_projected"], Decimal("150.00"))

    def test_monthly_summary_is_paid_filter_only_paid(self):
        """With is_paid=1 filter, summary should only include paid transactions"""
        response = self.client.get(
            "/monthly/12/2025/summary/?is_paid=1",
            HTTP_HX_REQUEST="true",
        )
        context = response.context

        # income_current should have 1000 (paid)
        income_current = context.get("income_current", {})
        usd_data = self._get_currency_data(income_current)
        self.assertIsNotNone(usd_data)
        self.assertEqual(usd_data["income_current"], Decimal("1000.00"))

        # expense_current should have 200 (paid)
        expense_current = context.get("expense_current", {})
        usd_data = self._get_currency_data(expense_current)
        self.assertIsNotNone(usd_data)
        self.assertEqual(usd_data["expense_current"], Decimal("200.00"))

        # expense_projected should be empty/zero (filtered out - unpaid)
        expense_projected = context.get("expense_projected", {})
        usd_data = self._get_currency_data(expense_projected)
        if usd_data:
            self.assertEqual(usd_data.get("expense_projected", 0), Decimal("0"))

    def test_monthly_summary_is_paid_filter_only_unpaid(self):
        """With is_paid=0 filter, summary should only include unpaid transactions"""
        response = self.client.get(
            "/monthly/12/2025/summary/?is_paid=0",
            HTTP_HX_REQUEST="true",
        )
        context = response.context

        # income_current should be empty/zero (filtered out - paid)
        income_current = context.get("income_current", {})
        usd_data = self._get_currency_data(income_current)
        if usd_data:
            self.assertEqual(usd_data.get("income_current", 0), Decimal("0"))

        # expense_current should be empty/zero (filtered out - paid)
        expense_current = context.get("expense_current", {})
        usd_data = self._get_currency_data(expense_current)
        if usd_data:
            self.assertEqual(usd_data.get("expense_current", 0), Decimal("0"))

        # expense_projected should have 150 (unpaid)
        expense_projected = context.get("expense_projected", {})
        usd_data = self._get_currency_data(expense_projected)
        self.assertIsNotNone(usd_data)
        self.assertEqual(usd_data["expense_projected"], Decimal("150.00"))

    def test_monthly_summary_description_filter(self):
        """With description filter, summary should only include matching transactions"""
        response = self.client.get(
            "/monthly/12/2025/summary/?description=Income",
            HTTP_HX_REQUEST="true",
        )
        context = response.context

        # Only income matches "Income" description
        income_current = context.get("income_current", {})
        usd_data = self._get_currency_data(income_current)
        self.assertIsNotNone(usd_data)
        self.assertEqual(usd_data["income_current"], Decimal("1000.00"))

        # Expenses should be filtered out
        expense_current = context.get("expense_current", {})
        usd_data = self._get_currency_data(expense_current)
        if usd_data:
            self.assertEqual(usd_data.get("expense_current", 0), Decimal("0"))

    def test_monthly_summary_amount_filter(self):
        """With amount filter, summary should only include transactions in range"""
        # Filter to only get transactions between 100 and 250 (should get 200 and 150)
        response = self.client.get(
            "/monthly/12/2025/summary/?from_amount=100&to_amount=250",
            HTTP_HX_REQUEST="true",
        )
        context = response.context

        # Income (1000) should be filtered out
        income_current = context.get("income_current", {})
        usd_data = self._get_currency_data(income_current)
        if usd_data:
            self.assertEqual(usd_data.get("income_current", 0), Decimal("0"))

        # expense_current should have 200
        expense_current = context.get("expense_current", {})
        usd_data = self._get_currency_data(expense_current)
        self.assertIsNotNone(usd_data)
        self.assertEqual(usd_data["expense_current"], Decimal("200.00"))

        # expense_projected should have 150
        expense_projected = context.get("expense_projected", {})
        usd_data = self._get_currency_data(expense_projected)
        self.assertIsNotNone(usd_data)
        self.assertEqual(usd_data["expense_projected"], Decimal("150.00"))

    # --- monthly_account_summary view tests ---

    def test_monthly_account_summary_no_filter_returns_200(self):
        """Test that monthly_account_summary returns 200 without filters"""
        response = self.client.get(
            "/monthly/12/2025/summary/accounts/",
            HTTP_HX_REQUEST="true",
        )
        self.assertEqual(response.status_code, 200)

    def test_monthly_account_summary_includes_asset_accounts(self):
        asset_account = self._create_asset_income()
        response = self.client.get(
            "/monthly/12/2025/summary/accounts/",
            HTTP_HX_REQUEST="true",
        )

        self.assertIn(asset_account.id, response.context["account_data"])

    def test_monthly_account_summary_with_filter_returns_200(self):
        """Test that monthly_account_summary returns 200 with filter"""
        response = self.client.get(
            "/monthly/12/2025/summary/accounts/?type=IN",
            HTTP_HX_REQUEST="true",
        )
        self.assertEqual(response.status_code, 200)

    # --- monthly_currency_summary view tests ---

    def test_monthly_currency_summary_no_filter_returns_200(self):
        """Test that monthly_currency_summary returns 200 without filters"""
        response = self.client.get(
            "/monthly/12/2025/summary/currencies/",
            HTTP_HX_REQUEST="true",
        )
        self.assertEqual(response.status_code, 200)

    def test_monthly_currency_summary_includes_asset_account_transactions(self):
        self._create_asset_income()
        response = self.client.get(
            "/monthly/12/2025/summary/currencies/",
            HTTP_HX_REQUEST="true",
        )

        usd_data = self._get_currency_data(response.context["currency_data"])
        self.assertEqual(usd_data["income_current"], Decimal("1050.00"))

    def test_monthly_currency_summary_with_filter_returns_200(self):
        """Test that monthly_currency_summary returns 200 with filter"""
        response = self.client.get(
            "/monthly/12/2025/summary/currencies/?type=EX",
            HTTP_HX_REQUEST="true",
        )
        self.assertEqual(response.status_code, 200)
