"""Price monitoring system — track items over time with alerts."""

from __future__ import annotations

import time
from dataclasses import dataclass, field
from typing import Any

from agent_scout.db.database import Database
from agent_scout.db.models import PriceSnapshot
from agent_scout.events import Event, EventBus, EventType


@dataclass
class PriceAlert:
    """A configured price alert for an item."""

    item_name: str
    target_price: float
    currency: str = "USD"
    alert_type: str = "below"  # "below" or "above"
    active: bool = True
    triggered: bool = False
    created_at: float = field(default_factory=time.time)


@dataclass
class TrackedItem:
    """An item being tracked for price changes."""

    name: str
    check_interval_seconds: int = 3600  # default: 1 hour
    last_checked: float = 0.0
    alerts: list[PriceAlert] = field(default_factory=list)
    sources: list[str] = field(default_factory=list)


@dataclass
class PriceChange:
    """Detected price change for an item."""

    item_name: str
    source: str
    old_price: float
    new_price: float
    currency: str
    change_pct: float
    is_drop: bool


class PriceMonitor:
    """Monitors tracked items for price changes and triggers alerts.

    Works with the Database layer to store/retrieve price history
    and emits events when significant changes are detected.
    """

    def __init__(
        self,
        db: Database | None = None,
        event_bus: EventBus | None = None,
        drop_threshold_pct: float = 5.0,
    ) -> None:
        self._db = db
        self._bus = event_bus
        self._drop_threshold_pct = drop_threshold_pct
        self._tracked_items: dict[str, TrackedItem] = {}

    def track(
        self,
        item_name: str,
        check_interval_seconds: int = 3600,
        sources: list[str] | None = None,
    ) -> TrackedItem:
        """Start tracking an item."""
        item = TrackedItem(
            name=item_name,
            check_interval_seconds=check_interval_seconds,
            sources=sources or [],
        )
        self._tracked_items[item_name] = item
        return item

    def untrack(self, item_name: str) -> bool:
        """Stop tracking an item."""
        return self._tracked_items.pop(item_name, None) is not None

    def add_alert(
        self,
        item_name: str,
        target_price: float,
        currency: str = "USD",
        alert_type: str = "below",
    ) -> PriceAlert | None:
        """Add a price alert to a tracked item."""
        item = self._tracked_items.get(item_name)
        if not item:
            return None
        alert = PriceAlert(
            item_name=item_name,
            target_price=target_price,
            currency=currency,
            alert_type=alert_type,
        )
        item.alerts.append(alert)
        return alert

    def get_tracked_items(self) -> list[TrackedItem]:
        """Return all tracked items."""
        return list(self._tracked_items.values())

    def items_due_for_check(self) -> list[TrackedItem]:
        """Return items whose check interval has elapsed."""
        now = time.time()
        return [
            item
            for item in self._tracked_items.values()
            if now - item.last_checked >= item.check_interval_seconds
        ]

    async def record_price(
        self,
        item_name: str,
        price: float,
        source: str,
        currency: str = "USD",
        url: str = "",
        metadata: dict[str, Any] | None = None,
    ) -> list[PriceChange]:
        """Record a new price and check for changes/alerts."""
        changes: list[PriceChange] = []

        # Store in DB if available
        if self._db:
            snapshot = PriceSnapshot(
                item_name=item_name,
                price=price,
                source=source,
                currency=currency,
                url=url,
                metadata=metadata or {},
            )
            await self._db.store_price(snapshot)

        # Update last checked time
        item = self._tracked_items.get(item_name)
        if item:
            item.last_checked = time.time()

        # Detect change vs previous price
        change = await self._detect_change(item_name, price, source, currency)
        if change:
            changes.append(change)

        # Check alerts
        await self._check_alerts(item_name, price, currency)

        return changes

    async def get_price_trend(
        self,
        item_name: str,
        limit: int = 30,
    ) -> dict[str, Any]:
        """Get price trend data for an item."""
        if not self._db:
            return {"item_name": item_name, "history": [], "trend": "unknown"}

        history = await self._db.get_price_history(item_name, limit=limit)
        if not history:
            return {"item_name": item_name, "history": [], "trend": "unknown"}

        prices = [h.price for h in reversed(history)]  # chronological

        trend = "stable"
        if len(prices) >= 2:
            first_half = sum(prices[: len(prices) // 2]) / max(len(prices) // 2, 1)
            second_half = sum(prices[len(prices) // 2 :]) / max(len(prices) - len(prices) // 2, 1)
            if second_half < first_half * 0.95:
                trend = "declining"
            elif second_half > first_half * 1.05:
                trend = "rising"

        return {
            "item_name": item_name,
            "history": [
                {
                    "price": h.price,
                    "source": h.source,
                    "currency": h.currency,
                    "timestamp": h.captured_at,
                }
                for h in history
            ],
            "trend": trend,
            "current_price": prices[-1] if prices else None,
            "min_price": min(prices) if prices else None,
            "max_price": max(prices) if prices else None,
            "avg_price": sum(prices) / len(prices) if prices else None,
        }

    async def _detect_change(
        self,
        item_name: str,
        new_price: float,
        source: str,
        currency: str,
    ) -> PriceChange | None:
        """Detect if a price has changed significantly."""
        if not self._db:
            return None

        history = await self._db.get_price_history(item_name, limit=2)
        # Need at least 2 entries (the one we just stored + the previous one)
        if len(history) < 2:
            return None

        old_price = history[1].price  # previous entry (history is newest-first)
        if old_price == 0:
            return None

        change_pct = ((new_price - old_price) / old_price) * 100
        is_drop = new_price < old_price

        if abs(change_pct) < self._drop_threshold_pct:
            return None

        change = PriceChange(
            item_name=item_name,
            source=source,
            old_price=old_price,
            new_price=new_price,
            currency=currency,
            change_pct=change_pct,
            is_drop=is_drop,
        )

        if self._bus and is_drop:
            await self._bus.emit(
                Event(
                    event_type=EventType.COST_UPDATE,
                    data={
                        "type": "price_drop",
                        "item_name": item_name,
                        "old_price": old_price,
                        "new_price": new_price,
                        "change_pct": change_pct,
                        "source": source,
                    },
                )
            )

        return change

    async def _check_alerts(
        self,
        item_name: str,
        price: float,
        currency: str,
    ) -> None:
        """Check if any alerts should trigger for this price."""
        item = self._tracked_items.get(item_name)
        if not item:
            return

        for alert in item.alerts:
            if not alert.active or alert.triggered:
                continue
            if alert.currency != currency:
                continue

            is_below = alert.alert_type == "below" and price <= alert.target_price
            is_above = alert.alert_type == "above" and price >= alert.target_price
            triggered = is_below or is_above

            if triggered:
                alert.triggered = True
                if self._bus:
                    await self._bus.emit(
                        Event(
                            event_type=EventType.BUDGET_WARNING,
                            data={
                                "type": "price_alert",
                                "item_name": item_name,
                                "price": price,
                                "target_price": alert.target_price,
                                "alert_type": alert.alert_type,
                                "currency": currency,
                            },
                        )
                    )
