"""Seed database from external sources, with curated fallbacks for gaps."""

from __future__ import annotations

import logging

from sqlalchemy import and_
from sqlalchemy.orm import Session

from luca_assistant.db.models import BankRule, Card, CardBenefit, CardOffer

logger = logging.getLogger(__name__)


def seed_all(session: Session) -> None:
    """Seed the database. Prefers Offer Optimist YAML; falls back to hardcoded data."""
    if session.query(Card).count() > 0:
        return  # Already seeded

    # 1. Bank rules (no external source has these)
    _seed_bank_rules(session)

    # 2. Try Offer Optimist import for cards + offers
    imported = False
    try:
        from luca_assistant.db.importers import OfferOptimistImporter

        importer = OfferOptimistImporter()
        path = importer.download()
        data = importer.parse(path)
        result = importer.import_cards(session, data)
        logger.info(
            "Offer Optimist import: %d created, %d updated, %d offers",
            result.cards_created,
            result.cards_updated,
            result.offers_created,
        )
        imported = True
    except Exception as exc:
        logger.warning("Offer Optimist import failed (%s), using fallback seed", exc)
        _seed_fallback_cards(session)

    # 3. Gap cards not covered by Offer Optimist
    _seed_gap_cards(session)

    # 4. Curated multiplier benefits for popular cards
    _seed_core_benefits(session)

    session.commit()
    if imported:
        logger.info("Seed complete (Offer Optimist + curated benefits + bank rules)")
    else:
        logger.info("Seed complete (fallback cards + curated benefits + bank rules)")


# ---------------------------------------------------------------------------
# Gap cards — issuers/cards not in Offer Optimist
# ---------------------------------------------------------------------------

_GAP_CARDS = [
    # (bank, name, full_name, network, annual_fee, waived, family, biz)
    ("Bilt", "Mastercard", "Bilt Mastercard", "Mastercard", 0, False, None, False),
    (
        "US Bank",
        "Altitude Reserve",
        "US Bank Altitude Reserve Visa Infinite Card",
        "Visa",
        400,
        False,
        None,
        False,
    ),
    ("Wells Fargo", "Autograph", "Wells Fargo Autograph Card", "Visa", 0, False, None, False),
    (
        "Wells Fargo",
        "Autograph Journey",
        "Wells Fargo Autograph Journey Card",
        "Visa",
        95,
        False,
        None,
        False,
    ),
    ("Robinhood", "Gold Card", "Robinhood Gold Card", "Visa", 0, False, None, False),
]

_GAP_OFFERS = [
    # (bank, name, bonus_amount, bonus_value_usd, spend, months, is_highest)
    ("Bilt", "Mastercard", "N/A (no public SUB)", 0, 0, 0, False),
    ("US Bank", "Altitude Reserve", "50,000 points", 750, 4500, 3, False),
    ("Wells Fargo", "Autograph", "20,000 points", 200, 1500, 3, False),
    ("Wells Fargo", "Autograph Journey", "60,000 points", 600, 4000, 3, False),
    ("Robinhood", "Gold Card", "N/A", 0, 0, 0, False),
]


def _seed_gap_cards(session: Session) -> None:
    """Insert cards not covered by Offer Optimist (upsert-safe)."""
    for bank, name, full_name, network, af, waived, family, biz in _GAP_CARDS:
        existing = session.query(Card).filter(and_(Card.bank == bank, Card.name == name)).first()
        if existing:
            continue
        card = Card(
            bank=bank,
            name=name,
            full_name=full_name,
            network=network,
            annual_fee=af,
            annual_fee_waived_first_year=waived,
            product_family=family,
            is_business=biz,
        )
        session.add(card)

    session.flush()

    for bank, name, bonus, value, spend, months, highest in _GAP_OFFERS:
        card = session.query(Card).filter(and_(Card.bank == bank, Card.name == name)).first()
        if not card:
            continue
        # Skip if already has a current offer
        has_offer = any(o.is_current for o in card.offers)
        if has_offer:
            continue
        session.add(
            CardOffer(
                card_id=card.id,
                bonus_amount=bonus,
                bonus_value_usd=value,
                spend_requirement=spend,
                spend_timeframe_months=months,
                is_current=True,
                is_highest_known=highest,
            )
        )


# ---------------------------------------------------------------------------
# Fallback cards — minimal set for offline / CI
# ---------------------------------------------------------------------------


def _seed_fallback_cards(session: Session) -> None:
    """Hardcoded core cards when Offer Optimist is unavailable."""
    card_data = [
        (
            "Chase",
            "Sapphire Preferred",
            "Chase Sapphire Preferred",
            "Visa",
            95,
            False,
            "Sapphire",
            False,
        ),
        (
            "Chase",
            "Sapphire Reserve",
            "Chase Sapphire Reserve",
            "Visa",
            550,
            False,
            "Sapphire",
            False,
        ),
        ("Chase", "Freedom Flex", "Chase Freedom Flex", "Mastercard", 0, False, "Freedom", False),
        (
            "Chase",
            "Freedom Unlimited",
            "Chase Freedom Unlimited",
            "Visa",
            0,
            False,
            "Freedom",
            False,
        ),
        (
            "Chase",
            "Ink Business Preferred",
            "Chase Ink Business Preferred",
            "Visa",
            95,
            False,
            "Ink",
            True,
        ),
        ("Chase", "Ink Business Cash", "Chase Ink Business Cash", "Visa", 0, False, "Ink", True),
        (
            "Chase",
            "Ink Business Unlimited",
            "Chase Ink Business Unlimited",
            "Visa",
            0,
            False,
            "Ink",
            True,
        ),
        (
            "Amex",
            "Platinum",
            "The Platinum Card from American Express",
            "Amex",
            695,
            False,
            "Platinum",
            False,
        ),
        ("Amex", "Gold", "American Express Gold Card", "Amex", 250, False, "Gold", False),
        (
            "Capital One",
            "Venture X",
            "Capital One Venture X Rewards Credit Card",
            "Visa",
            395,
            False,
            "Venture",
            False,
        ),
        (
            "Citi",
            "Strata Premier",
            "Citi Strata Premier Card",
            "Mastercard",
            95,
            True,
            "Strata",
            False,
        ),
    ]

    offers_data = [
        ("Chase", "Sapphire Preferred", "60,000 UR", 750, 4000, 3),
        ("Chase", "Sapphire Reserve", "60,000 UR", 900, 4000, 3),
        ("Chase", "Freedom Flex", "20,000 UR", 200, 500, 3),
        ("Chase", "Freedom Unlimited", "20,000 UR", 300, 500, 3),
        ("Chase", "Ink Business Preferred", "100,000 UR", 1250, 8000, 3),
        ("Chase", "Ink Business Cash", "75,000 UR", 750, 6000, 3),
        ("Chase", "Ink Business Unlimited", "75,000 UR", 750, 6000, 3),
        ("Amex", "Platinum", "80,000 MR", 1200, 8000, 6),
        ("Amex", "Gold", "60,000 MR", 720, 6000, 6),
        ("Capital One", "Venture X", "75,000 miles", 750, 4000, 3),
        ("Citi", "Strata Premier", "75,000 TYP", 750, 4000, 3),
    ]

    cards: dict[tuple[str, str], Card] = {}
    for bank, name, full_name, network, af, waived, family, biz in card_data:
        card = Card(
            bank=bank,
            name=name,
            full_name=full_name,
            network=network,
            annual_fee=af,
            annual_fee_waived_first_year=waived,
            product_family=family,
            is_business=biz,
        )
        session.add(card)
        cards[(bank, name)] = card

    session.flush()

    for bank, name, bonus, value, spend, months in offers_data:
        card = cards[(bank, name)]
        session.add(
            CardOffer(
                card_id=card.id,
                bonus_amount=bonus,
                bonus_value_usd=value,
                spend_requirement=spend,
                spend_timeframe_months=months,
                is_current=True,
                is_highest_known=False,
            )
        )


# ---------------------------------------------------------------------------
# Curated benefits (multipliers + credits) for core cards
# ---------------------------------------------------------------------------


def _seed_core_benefits(session: Session) -> None:
    """Seed category multipliers and credits for popular cards.

    These come from curated data since no public structured source has multipliers.
    Benefits are skipped if the card doesn't exist or the category already has data
    (e.g. from Offer Optimist credits import).
    """
    benefits = [
        # Chase Sapphire Preferred
        ("Chase", "Sapphire Preferred", "dining", 3.0, "UR", None, None, None),
        (
            "Chase",
            "Sapphire Preferred",
            "travel",
            2.0,
            "UR",
            None,
            None,
            "Excluding hotels via Chase portal at 5x",
        ),
        ("Chase", "Sapphire Preferred", "online grocery", 3.0, "UR", None, None, None),
        ("Chase", "Sapphire Preferred", "streaming", 3.0, "UR", None, None, None),
        ("Chase", "Sapphire Preferred", "other", 1.0, "UR", None, None, None),
        (
            "Chase",
            "Sapphire Preferred",
            "travel credit",
            1.0,
            "UR",
            50,
            "hotels via Chase",
            "$50 annual Chase hotel credit",
        ),
        # Chase Sapphire Reserve
        ("Chase", "Sapphire Reserve", "dining", 3.0, "UR", None, None, None),
        ("Chase", "Sapphire Reserve", "travel", 3.0, "UR", None, None, None),
        ("Chase", "Sapphire Reserve", "other", 1.0, "UR", None, None, None),
        (
            "Chase",
            "Sapphire Reserve",
            "travel credit",
            1.0,
            "UR",
            300,
            "travel",
            "$300 annual travel credit",
        ),
        # Chase Freedom Flex
        (
            "Chase",
            "Freedom Flex",
            "rotating quarterly",
            5.0,
            "UR",
            None,
            None,
            "Up to $1,500/quarter",
        ),
        ("Chase", "Freedom Flex", "travel via Chase", 5.0, "UR", None, None, None),
        ("Chase", "Freedom Flex", "dining", 3.0, "UR", None, None, None),
        ("Chase", "Freedom Flex", "drugstores", 3.0, "UR", None, None, None),
        ("Chase", "Freedom Flex", "other", 1.0, "UR", None, None, None),
        # Chase Ink Business Preferred
        ("Chase", "Ink Business Preferred", "travel", 3.0, "UR", None, None, None),
        ("Chase", "Ink Business Preferred", "shipping", 3.0, "UR", None, None, None),
        ("Chase", "Ink Business Preferred", "internet/cable/phone", 3.0, "UR", None, None, None),
        (
            "Chase",
            "Ink Business Preferred",
            "advertising (social media)",
            3.0,
            "UR",
            None,
            None,
            "First $150K/year",
        ),
        ("Chase", "Ink Business Preferred", "other", 1.0, "UR", None, None, None),
        # Amex Platinum
        ("Amex", "Platinum", "flights booked directly", 5.0, "MR", None, None, None),
        ("Amex", "Platinum", "flights via Amex Travel", 5.0, "MR", None, None, None),
        ("Amex", "Platinum", "hotels via Amex Travel", 5.0, "MR", None, None, "Prepaid only"),
        ("Amex", "Platinum", "other", 1.0, "MR", None, None, None),
        (
            "Amex",
            "Platinum",
            "airline credit",
            1.0,
            "MR",
            200,
            "airline incidentals",
            "$200 airline fee credit",
        ),
        ("Amex", "Platinum", "Uber credit", 1.0, "MR", 200, "Uber", "$200 annual Uber credit"),
        (
            "Amex",
            "Platinum",
            "Saks credit",
            1.0,
            "MR",
            100,
            "Saks Fifth Avenue",
            "$100 annual Saks credit",
        ),
        (
            "Amex",
            "Platinum",
            "digital entertainment",
            1.0,
            "MR",
            240,
            "digital entertainment",
            "$20/mo digital entertainment",
        ),
        # Amex Gold
        ("Amex", "Gold", "restaurants", 4.0, "MR", None, None, None),
        ("Amex", "Gold", "US supermarkets", 4.0, "MR", None, None, "Up to $25K/year"),
        ("Amex", "Gold", "flights booked directly", 3.0, "MR", None, None, None),
        ("Amex", "Gold", "other", 1.0, "MR", None, None, None),
        ("Amex", "Gold", "dining credit", 1.0, "MR", 120, "restaurants", "$10/mo dining credit"),
        ("Amex", "Gold", "Uber credit", 1.0, "MR", 120, "Uber", "$10/mo Uber Cash"),
        # Capital One Venture X
        (
            "Capital One",
            "Venture X",
            "hotels/rental cars via Cap1",
            10.0,
            "miles",
            None,
            None,
            None,
        ),
        ("Capital One", "Venture X", "flights via Cap1", 5.0, "miles", None, None, None),
        ("Capital One", "Venture X", "other", 2.0, "miles", None, None, None),
        (
            "Capital One",
            "Venture X",
            "travel credit",
            1.0,
            "miles",
            300,
            "travel via Cap1",
            "$300 annual travel credit",
        ),
        # Citi Strata Premier
        ("Citi", "Strata Premier", "flights", 3.0, "TYP", None, None, None),
        ("Citi", "Strata Premier", "hotels", 3.0, "TYP", None, None, None),
        ("Citi", "Strata Premier", "restaurants", 3.0, "TYP", None, None, None),
        ("Citi", "Strata Premier", "supermarkets", 3.0, "TYP", None, None, None),
        ("Citi", "Strata Premier", "gas stations", 3.0, "TYP", None, None, None),
        ("Citi", "Strata Premier", "other", 1.0, "TYP", None, None, None),
        # Bilt Mastercard
        ("Bilt", "Mastercard", "rent", 1.0, "Bilt", None, None, "Up to 100K points/year on rent"),
        ("Bilt", "Mastercard", "dining", 3.0, "Bilt", None, None, None),
        ("Bilt", "Mastercard", "travel", 2.0, "Bilt", None, None, None),
        ("Bilt", "Mastercard", "other", 1.0, "Bilt", None, None, "Must make 5 transactions/month"),
    ]

    for bank, name, cat, mult, pts, credit, credit_cat, notes in benefits:
        card = session.query(Card).filter(and_(Card.bank == bank, Card.name == name)).first()
        if not card:
            continue

        # Skip if this category already has data (from Offer Optimist credits import)
        existing = (
            session.query(CardBenefit)
            .filter(and_(CardBenefit.card_id == card.id, CardBenefit.category == cat))
            .first()
        )
        if existing:
            continue

        session.add(
            CardBenefit(
                card_id=card.id,
                category=cat,
                multiplier=mult,
                points_type=pts,
                annual_credit=credit,
                credit_category=credit_cat,
                notes=notes,
            )
        )


# ---------------------------------------------------------------------------
# Bank rules (curated — no external source)
# ---------------------------------------------------------------------------


def _seed_bank_rules(session: Session) -> None:
    """Seed issuer-specific application rules."""
    rules = [
        (
            "Chase",
            "5/24",
            "Cannot be approved if you've opened 5+ new cards (any issuer) in the past 24 months",
            "velocity",
            {"window_months": 24, "max_cards": 4},
        ),
        (
            "Chase",
            "One Sapphire Rule",
            "Can only hold one Sapphire product at a time;"
            " cannot get bonus if received a Sapphire bonus in past 48 months",
            "eligibility",
            {"product_family": "Sapphire", "cooldown_months": 48},
        ),
        (
            "Chase",
            "2/30",
            "Maximum 2 Chase card applications in 30 days",
            "velocity",
            {"window_days": 30, "max_apps": 2},
        ),
        (
            "Amex",
            "Lifetime Language",
            "Sign-up bonus available once per lifetime per card product"
            " (with some exceptions for upgrade offers)",
            "eligibility",
            {"lifetime": True},
        ),
        (
            "Amex",
            "1/5 Credit Card Limit",
            "Can only have 5 Amex credit cards at a time (charge cards don't count)",
            "velocity",
            {"max_credit_cards": 5},
        ),
        (
            "Amex",
            "2/90",
            "Cannot be approved for more than 2 credit cards in 90 days",
            "velocity",
            {"window_days": 90, "max_apps": 2},
        ),
        (
            "Amex",
            "NLL Offers",
            "No-lifetime-language targeted offers bypass the once-per-lifetime rule",
            "eligibility",
            {"bypass_lifetime": True},
        ),
        (
            "Citi",
            "8/65",
            "Cannot apply for more than 1 Citi card per 8 days, or 2 per 65 days",
            "velocity",
            {"rules": [{"window_days": 8, "max_apps": 1}, {"window_days": 65, "max_apps": 2}]},
        ),
        (
            "Citi",
            "48-Month Rule",
            "Cannot earn a bonus if you received the same card's bonus in the past 48 months",
            "eligibility",
            {"cooldown_months": 48},
        ),
        (
            "Capital One",
            "Account Limit",
            "Capital One typically limits to 2-3 open cards per person",
            "velocity",
            {"max_cards": 3},
        ),
        (
            "Barclays",
            "6/24",
            "Barclays is very sensitive to new accounts;"
            " 6+ new cards in 24 months often results in denial",
            "velocity",
            {"window_months": 24, "max_cards": 6},
        ),
        (
            "US Bank",
            "0/12 & 0/6",
            "US Bank prefers no new cards in 6-12 months; very inquiry sensitive",
            "velocity",
            {"preferred_clean_months": 12},
        ),
    ]

    for bank, name, desc, rtype, params in rules:
        session.add(
            BankRule(
                bank=bank,
                rule_name=name,
                description=desc,
                rule_type=rtype,
                parameters=params,
            )
        )
