"""
Sniper Alert models for hyper-personalized auction alerts.

This implements "Strategie 4: Alerts nach Maß" from analysis_3.md:
"Der User kann extrem spezifische Filter speichern:
- Informiere mich NUR, wenn eine 4-Letter .com Domain droppt, die kein 'q' oder 'x' enthält.
- Informiere mich, wenn eine .ch Domain droppt, die das Wort 'Immo' enthält."

DATABASE TABLES TO CREATE:
1. sniper_alerts - Saved filter configurations
2. sniper_alert_matches - Matched auctions for each alert
3. sniper_alert_notifications - Sent notifications

Run migrations: alembic upgrade head
"""
from datetime import datetime
from typing import Optional, List
from sqlalchemy import String, DateTime, Float, Integer, Text, ForeignKey, Boolean, JSON
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.database import Base


class SniperAlert(Base):
    """
    Saved filter for hyper-personalized auction alerts.
    
    Users can define very specific criteria and get notified
    when matching domains appear in auctions.
    
    Example filters:
    - "4-letter .com without q or x"
    - ".ch domains containing 'immo'"
    - "Auctions under $100 ending in 1 hour"
    
    From analysis_3.md:
    "Wenn die SMS/Mail kommt, weiß der User: Das ist relevant."
    """
    
    __tablename__ = "sniper_alerts"
    
    id: Mapped[int] = mapped_column(primary_key=True, index=True)
    user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), index=True, nullable=False)
    
    # Alert name
    name: Mapped[str] = mapped_column(String(100), nullable=False)
    description: Mapped[Optional[str]] = mapped_column(String(500), nullable=True)
    
    # Filter criteria (stored as JSON for flexibility)
    # Example: {"tlds": ["com", "io"], "max_length": 4, "exclude_chars": ["q", "x"]}
    filter_criteria: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
    
    # Individual filter fields (for database queries)
    tlds: Mapped[Optional[str]] = mapped_column(String(500), nullable=True)  # Comma-separated: "com,io,ai"
    keywords: Mapped[Optional[str]] = mapped_column(String(500), nullable=True)  # Must contain
    exclude_keywords: Mapped[Optional[str]] = mapped_column(String(500), nullable=True)  # Must not contain
    max_length: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
    min_length: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
    max_price: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
    min_price: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
    max_bids: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)  # Low competition
    ending_within_hours: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)  # Urgency
    platforms: Mapped[Optional[str]] = mapped_column(String(200), nullable=True)  # Comma-separated
    
    # Advanced filters
    no_numbers: Mapped[bool] = mapped_column(Boolean, default=False)
    no_hyphens: Mapped[bool] = mapped_column(Boolean, default=False)
    exclude_chars: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)  # "q,x,z"
    
    # Notification settings
    notify_email: Mapped[bool] = mapped_column(Boolean, default=True)
    notify_sms: Mapped[bool] = mapped_column(Boolean, default=False)  # Tycoon feature
    notify_push: Mapped[bool] = mapped_column(Boolean, default=False)
    
    # Frequency limits
    max_notifications_per_day: Mapped[int] = mapped_column(Integer, default=10)
    cooldown_minutes: Mapped[int] = mapped_column(Integer, default=30)  # Min time between alerts
    
    # Status
    is_active: Mapped[bool] = mapped_column(Boolean, default=True)
    
    # Stats
    matches_count: Mapped[int] = mapped_column(Integer, default=0)
    notifications_sent: Mapped[int] = mapped_column(Integer, default=0)
    last_matched_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
    last_notified_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
    
    # Timestamps
    created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
    updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
    
    # Relationships
    user: Mapped["User"] = relationship("User", back_populates="sniper_alerts")
    matches: Mapped[List["SniperAlertMatch"]] = relationship(
        "SniperAlertMatch", back_populates="alert", cascade="all, delete-orphan"
    )
    
    def __repr__(self) -> str:
        return f"<SniperAlert '{self.name}' (user={self.user_id})>"
    
    def matches_domain(self, domain: str, tld: str, price: float, num_bids: int) -> bool:
        """Check if a domain matches this alert's criteria."""
        name = domain.split('.')[0] if '.' in domain else domain
        
        # TLD filter
        if self.tlds:
            allowed_tlds = [t.strip().lower() for t in self.tlds.split(',')]
            if tld.lower() not in allowed_tlds:
                return False
        
        # Length filters
        if self.max_length and len(name) > self.max_length:
            return False
        if self.min_length and len(name) < self.min_length:
            return False
        
        # Price filters
        if self.max_price and price > self.max_price:
            return False
        if self.min_price and price < self.min_price:
            return False
        
        # Competition filter
        if self.max_bids and num_bids > self.max_bids:
            return False
        
        # Keyword filters
        if self.keywords:
            required = [k.strip().lower() for k in self.keywords.split(',')]
            if not any(kw in name.lower() for kw in required):
                return False
        
        if self.exclude_keywords:
            excluded = [k.strip().lower() for k in self.exclude_keywords.split(',')]
            if any(kw in name.lower() for kw in excluded):
                return False
        
        # Character filters
        if self.no_numbers and any(c.isdigit() for c in name):
            return False
        
        if self.no_hyphens and '-' in name:
            return False
        
        if self.exclude_chars:
            excluded_chars = [c.strip().lower() for c in self.exclude_chars.split(',')]
            if any(c in name.lower() for c in excluded_chars):
                return False
        
        return True


class SniperAlertMatch(Base):
    """
    Record of a domain that matched a sniper alert.
    """
    
    __tablename__ = "sniper_alert_matches"
    
    id: Mapped[int] = mapped_column(primary_key=True, index=True)
    alert_id: Mapped[int] = mapped_column(ForeignKey("sniper_alerts.id"), index=True, nullable=False)
    
    # Matched auction info
    domain: Mapped[str] = mapped_column(String(255), nullable=False)
    platform: Mapped[str] = mapped_column(String(50), nullable=False)
    current_bid: Mapped[float] = mapped_column(Float, nullable=False)
    end_time: Mapped[datetime] = mapped_column(DateTime, nullable=False)
    auction_url: Mapped[Optional[str]] = mapped_column(String(500), nullable=True)
    
    # Status
    notified: Mapped[bool] = mapped_column(Boolean, default=False)
    clicked: Mapped[bool] = mapped_column(Boolean, default=False)
    
    # Timestamps
    matched_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
    notified_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
    
    # Relationships
    alert: Mapped["SniperAlert"] = relationship("SniperAlert", back_populates="matches")
    
    def __repr__(self) -> str:
        return f"<SniperAlertMatch {self.domain} for alert #{self.alert_id}>"

