"""Tests for portfolio skills — 5/24 calculation edge cases."""

from __future__ import annotations

from datetime import date, timedelta

from sqlalchemy.orm import Session

from luca_assistant.db.models import Card, UserCard
from luca_assistant.skills.portfolio import (
    add_user_card,
    check_chase_524_status,
    close_user_card,
    get_user_portfolio,
)


def _add_card_directly(session: Session, card_name: str, opened: date, **kwargs) -> None:
    """Helper to add a UserCard directly via session."""
    card = session.query(Card).filter(Card.name.ilike(f"%{card_name}%")).first()
    assert card is not None, f"Card {card_name} not found"
    uc = UserCard(card_id=card.id, opened_date=opened, **kwargs)
    session.add(uc)
    session.commit()


class TestGetUserPortfolio:
    def test_empty_portfolio(self, db_session: Session) -> None:
        result = get_user_portfolio()
        assert result["portfolio"] == []

    def test_portfolio_with_cards(self, db_session: Session) -> None:
        _add_card_directly(db_session, "Sapphire Preferred", date(2024, 1, 15), credit_limit=15000)
        _add_card_directly(db_session, "Freedom Flex", date(2024, 6, 1))

        result = get_user_portfolio()
        assert len(result["portfolio"]) == 2
        assert result["total_open"] == 2

    def test_closed_cards_shown(self, db_session: Session) -> None:
        _add_card_directly(
            db_session,
            "Sapphire Preferred",
            date(2023, 1, 1),
            closed_date=date(2024, 1, 1),
        )
        result = get_user_portfolio()
        assert len(result["portfolio"]) == 1
        assert result["portfolio"][0]["is_open"] is False
        assert result["total_open"] == 0


class TestChase524:
    def test_zero_cards(self, db_session: Session) -> None:
        result = check_chase_524_status()
        assert result["count_524"] == 0
        assert result["slots_remaining"] == 5
        assert result["is_under_524"] is True

    def test_under_524(self, db_session: Session) -> None:
        today = date.today()
        _add_card_directly(db_session, "Sapphire Preferred", today - timedelta(days=100))
        _add_card_directly(db_session, "Gold", today - timedelta(days=200))

        result = check_chase_524_status()
        assert result["count_524"] == 2
        assert result["slots_remaining"] == 3
        assert result["is_under_524"] is True

    def test_at_524(self, db_session: Session) -> None:
        today = date.today()
        cards_to_add = ["Sapphire Preferred", "Freedom Flex", "Gold", "Venture X", "Custom Cash"]
        for i, name in enumerate(cards_to_add):
            _add_card_directly(db_session, name, today - timedelta(days=30 * (i + 1)))

        result = check_chase_524_status()
        assert result["count_524"] == 5
        assert result["slots_remaining"] == 0
        assert result["is_under_524"] is False

    def test_chase_biz_cards_dont_count(self, db_session: Session) -> None:
        """Chase business cards should NOT count toward 5/24."""
        today = date.today()
        _add_card_directly(db_session, "Ink Business Preferred", today - timedelta(days=60))
        _add_card_directly(db_session, "Ink Business Cash", today - timedelta(days=90))

        result = check_chase_524_status()
        assert result["count_524"] == 0
        assert len(result["non_counting_cards"]) == 2

    def test_other_biz_cards_count(self, db_session: Session) -> None:
        """Non-Chase business cards DO count toward 5/24."""
        today = date.today()
        _add_card_directly(db_session, "Business Gold", today - timedelta(days=60))

        result = check_chase_524_status()
        assert result["count_524"] == 1

    def test_old_cards_dont_count(self, db_session: Session) -> None:
        """Cards older than 24 months should not count."""
        _add_card_directly(db_session, "Sapphire Preferred", date.today() - timedelta(days=800))

        result = check_chase_524_status()
        assert result["count_524"] == 0

    def test_next_falloff(self, db_session: Session) -> None:
        """Should report when the next card falls off 5/24."""
        today = date.today()
        opened = today - timedelta(days=600)
        _add_card_directly(db_session, "Sapphire Preferred", opened)

        result = check_chase_524_status()
        assert result["next_falloff"] is not None
        expected_falloff = opened + timedelta(days=730)
        assert result["next_falloff"]["falls_off"] == str(expected_falloff)


class TestAddUserCard:
    def test_add_card(self, db_session: Session) -> None:
        result = add_user_card("Sapphire Preferred", "2024-06-15", credit_limit=15000)
        assert result["success"] is True
        assert "Chase" in result["card"]

    def test_add_card_with_bank(self, db_session: Session) -> None:
        result = add_user_card("Platinum", "2024-03-01", bank="Amex")
        assert result["success"] is True
        assert "Amex" in result["card"]

    def test_add_nonexistent_card(self, db_session: Session) -> None:
        result = add_user_card("Fake Card XYZ", "2024-01-01")
        assert "error" in result

    def test_annual_fee_due_calculated(self, db_session: Session) -> None:
        result = add_user_card("Sapphire Preferred", "2024-06-15")
        assert result["annual_fee_due"] == "2025-06-15"

    def test_no_fee_card_no_due_date(self, db_session: Session) -> None:
        result = add_user_card("Freedom Flex", "2024-06-15")
        assert result["annual_fee_due"] is None


class TestCloseUserCard:
    def test_close_card(self, db_session: Session) -> None:
        add_user_card("Sapphire Preferred", "2024-01-01")
        result = close_user_card("Sapphire Preferred", "2025-01-01")
        assert result["success"] is True

    def test_close_nonexistent(self, db_session: Session) -> None:
        result = close_user_card("Fake Card", "2025-01-01")
        assert "error" in result
