"""Historical FOMO event tracking and pattern matching."""

import json
from datetime import datetime, timedelta
from pathlib import Path
from typing import Any

from pydantic import BaseModel
from rich.console import Console
from rich.table import Table

from ..config import Config, get_config
from ..providers.coingecko import CoinGeckoProvider


class FOMOEvent(BaseModel):
    """A historical FOMO event."""

    date: datetime
    symbol: str
    price_at_event: float
    ath_proximity: float  # 0-100, how close to ATH
    price_change_24h: float
    price_change_7d: float
    # Outcomes
    price_after_7d: float | None = None
    price_after_30d: float | None = None
    price_after_90d: float | None = None
    change_after_7d: float | None = None
    change_after_30d: float | None = None
    change_after_90d: float | None = None


class HistoryTracker:
    """Track and analyze historical FOMO events."""

    # Thresholds for detecting a FOMO event
    FOMO_THRESHOLDS = {
        "ath_proximity": 90,  # Within 10% of ATH
        "price_change_24h": 15,  # 15%+ gain in 24h
        "price_change_7d": 40,  # 40%+ gain in 7d
    }

    def __init__(self):
        """Initialize history tracker."""
        self.config = get_config()
        self.data_dir = Config.get_data_dir()
        self.history_file = self.data_dir / "history.json"
        self.console = Console()

    async def get_fomo_events(
        self, symbol: str, days: int = 90
    ) -> list[FOMOEvent]:
        """Get historical FOMO events for a symbol."""
        # Try to load cached events
        cached = self._load_cached_events(symbol)
        if cached:
            return cached

        # Fetch historical data and detect FOMO events
        events = await self._detect_historical_events(symbol, days)

        # Cache the results
        if events:
            self._cache_events(symbol, events)

        return events

    async def _detect_historical_events(
        self, symbol: str, days: int
    ) -> list[FOMOEvent]:
        """Detect FOMO events from historical price data."""
        events = []

        coingecko_key = self.config.get_api_key("coingecko")
        async with CoinGeckoProvider(coingecko_key) as provider:
            # Get historical OHLC data
            try:
                ohlc_data = await provider.fetch_ohlc(symbol, days=days)
            except Exception:
                return []

            if not ohlc_data or len(ohlc_data) < 7:
                return []

            # Find ATH in the period
            closes = [candle[4] for candle in ohlc_data if len(candle) >= 5]
            if not closes:
                return []

            period_ath = max(closes)

            # Analyze each day for FOMO conditions
            for i in range(7, len(ohlc_data)):
                candle = ohlc_data[i]
                if len(candle) < 5:
                    continue

                timestamp, open_price, high, low, close = candle[:5]
                date = datetime.fromtimestamp(timestamp / 1000)

                # Calculate metrics
                ath_proximity = (close / period_ath) * 100

                # 24h change (compare with previous candle)
                prev_close = ohlc_data[i - 1][4] if len(ohlc_data[i - 1]) >= 5 else close
                change_24h = ((close - prev_close) / prev_close) * 100 if prev_close > 0 else 0

                # 7d change
                week_ago_close = ohlc_data[i - 7][4] if len(ohlc_data[i - 7]) >= 5 else close
                change_7d = ((close - week_ago_close) / week_ago_close) * 100 if week_ago_close > 0 else 0

                # Check if this qualifies as a FOMO event
                is_fomo = (
                    ath_proximity >= self.FOMO_THRESHOLDS["ath_proximity"]
                    or change_24h >= self.FOMO_THRESHOLDS["price_change_24h"]
                    or change_7d >= self.FOMO_THRESHOLDS["price_change_7d"]
                )

                if is_fomo:
                    # Calculate outcomes
                    outcomes = self._calculate_outcomes(ohlc_data, i, close)

                    event = FOMOEvent(
                        date=date,
                        symbol=symbol.upper(),
                        price_at_event=close,
                        ath_proximity=ath_proximity,
                        price_change_24h=change_24h,
                        price_change_7d=change_7d,
                        **outcomes,
                    )
                    events.append(event)

        # Deduplicate events (keep only one per week)
        events = self._deduplicate_events(events)

        return events

    def _calculate_outcomes(
        self, ohlc_data: list, current_index: int, current_price: float
    ) -> dict[str, float | None]:
        """Calculate price outcomes after the event."""
        outcomes: dict[str, float | None] = {
            "price_after_7d": None,
            "price_after_30d": None,
            "price_after_90d": None,
            "change_after_7d": None,
            "change_after_30d": None,
            "change_after_90d": None,
        }

        # 7 days after
        if current_index + 7 < len(ohlc_data):
            price_7d = ohlc_data[current_index + 7][4]
            outcomes["price_after_7d"] = price_7d
            outcomes["change_after_7d"] = ((price_7d - current_price) / current_price) * 100

        # 30 days after
        if current_index + 30 < len(ohlc_data):
            price_30d = ohlc_data[current_index + 30][4]
            outcomes["price_after_30d"] = price_30d
            outcomes["change_after_30d"] = ((price_30d - current_price) / current_price) * 100

        # 90 days after (if we have that much data)
        if current_index + 90 < len(ohlc_data):
            price_90d = ohlc_data[current_index + 90][4]
            outcomes["price_after_90d"] = price_90d
            outcomes["change_after_90d"] = ((price_90d - current_price) / current_price) * 100

        return outcomes

    def _deduplicate_events(self, events: list[FOMOEvent]) -> list[FOMOEvent]:
        """Keep only one event per week."""
        if not events:
            return []

        deduplicated = []
        last_date = None

        for event in sorted(events, key=lambda e: e.date):
            if last_date is None or (event.date - last_date).days >= 7:
                deduplicated.append(event)
                last_date = event.date

        return deduplicated

    def _load_cached_events(self, symbol: str) -> list[FOMOEvent] | None:
        """Load cached events from file."""
        if not self.history_file.exists():
            return None

        try:
            with open(self.history_file, "r") as f:
                data = json.load(f)

            symbol_data = data.get(symbol.upper())
            if not symbol_data:
                return None

            # Check if cache is fresh (less than 24 hours old)
            cached_at = datetime.fromisoformat(symbol_data.get("cached_at", "2000-01-01"))
            if datetime.now() - cached_at > timedelta(hours=24):
                return None

            events = [
                FOMOEvent(**event)
                for event in symbol_data.get("events", [])
            ]
            return events

        except (json.JSONDecodeError, Exception):
            return None

    def _cache_events(self, symbol: str, events: list[FOMOEvent]) -> None:
        """Cache events to file."""
        # Load existing data
        data = {}
        if self.history_file.exists():
            try:
                with open(self.history_file, "r") as f:
                    data = json.load(f)
            except (json.JSONDecodeError, Exception):
                pass

        # Add new events
        data[symbol.upper()] = {
            "cached_at": datetime.now().isoformat(),
            "events": [event.model_dump(mode="json") for event in events],
        }

        # Save
        with open(self.history_file, "w") as f:
            json.dump(data, f, indent=2, default=str)

    def display_events(self, symbol: str, events: list[FOMOEvent]) -> None:
        """Display historical FOMO events."""
        if not events:
            self.console.print(f"[dim]No FOMO events found for {symbol}[/dim]")
            return

        self.console.print(f"\n[bold]Historical FOMO Events for {symbol.upper()}[/bold]\n")

        table = Table(show_header=True)
        table.add_column("Date", style="cyan")
        table.add_column("Price", justify="right")
        table.add_column("ATH%", justify="right")
        table.add_column("24h%", justify="right")
        table.add_column("7d%", justify="right")
        table.add_column("After 7d", justify="right")
        table.add_column("After 30d", justify="right")

        for event in events[-10:]:  # Show last 10 events
            change_7d_style = "red" if event.change_after_7d and event.change_after_7d < 0 else "green"
            change_30d_style = "red" if event.change_after_30d and event.change_after_30d < 0 else "green"

            table.add_row(
                event.date.strftime("%Y-%m-%d"),
                f"${event.price_at_event:,.2f}",
                f"{event.ath_proximity:.0f}%",
                f"{event.price_change_24h:+.1f}%",
                f"{event.price_change_7d:+.1f}%",
                f"[{change_7d_style}]{event.change_after_7d:+.1f}%[/{change_7d_style}]" if event.change_after_7d else "-",
                f"[{change_30d_style}]{event.change_after_30d:+.1f}%[/{change_30d_style}]" if event.change_after_30d else "-",
            )

        self.console.print(table)

        # Summary statistics
        self._display_summary(events)

    def _display_summary(self, events: list[FOMOEvent]) -> None:
        """Display summary of FOMO event outcomes."""
        # Calculate statistics
        changes_7d = [e.change_after_7d for e in events if e.change_after_7d is not None]
        changes_30d = [e.change_after_30d for e in events if e.change_after_30d is not None]

        if changes_7d:
            avg_7d = sum(changes_7d) / len(changes_7d)
            negative_7d = sum(1 for c in changes_7d if c < 0)
            pct_negative_7d = (negative_7d / len(changes_7d)) * 100

            self.console.print(f"\n[bold]Summary:[/bold]")
            self.console.print(f"  7-day outcomes: {pct_negative_7d:.0f}% ended lower (avg: {avg_7d:+.1f}%)")

        if changes_30d:
            avg_30d = sum(changes_30d) / len(changes_30d)
            negative_30d = sum(1 for c in changes_30d if c < 0)
            pct_negative_30d = (negative_30d / len(changes_30d)) * 100
            self.console.print(f"  30-day outcomes: {pct_negative_30d:.0f}% ended lower (avg: {avg_30d:+.1f}%)")

        self.console.print(
            "\n[dim]Note: Past performance does not guarantee future results.[/dim]\n"
        )

    def find_similar_conditions(
        self, current_score: float, events: list[FOMOEvent]
    ) -> list[FOMOEvent]:
        """Find historical events with similar FOMO conditions."""
        # Find events where conditions were similar to current
        similar = []
        for event in events:
            # Calculate a rough similarity score based on ATH proximity and changes
            event_intensity = (
                event.ath_proximity * 0.5 +
                min(event.price_change_24h, 50) +
                min(event.price_change_7d / 2, 50)
            ) / 1.5

            # If within 20 points of current score
            if abs(event_intensity - current_score) < 20:
                similar.append(event)

        return similar
