"""
=========================================
CV6 AI Trading OS — News Sentiment Feed
=========================================
Sources (no paid API key needed):
  1. Google News RSS  — latest headlines per symbol
  2. NSE Corporate Announcements — official filings
  3. Economic Times RSS — Indian market news

Output: structured sentiment summary for AI context.
No fake data. If unavailable → returns empty list.
=========================================
"""
from __future__ import annotations

import re
import time
import threading
import xml.etree.ElementTree as ET
from dataclasses import dataclass, field
from typing import List, Optional, Dict
from urllib.parse import quote_plus

import requests
from loguru import logger

_TIMEOUT = 8
_lock    = threading.Lock()
_cache:  Dict[str, any] = {}
_cache_ts: Dict[str, float] = {}
_CACHE_TTL = 600   # 10 minutes per symbol


# ── Positive / negative keyword lists for simple sentiment ────────────────────

_BULLISH_WORDS = {
    "profit", "growth", "surge", "rally", "record", "beats", "strong",
    "upgrade", "outperform", "dividend", "buyback", "expansion", "order",
    "win", "contract", "partnership", "acquisition", "revenue", "earnings beat",
    "positive", "recovery", "milestone", "targets", "raise"
}

_BEARISH_WORDS = {
    "loss", "decline", "fall", "drop", "miss", "downgrade", "underperform",
    "warning", "concern", "debt", "probe", "fraud", "sebi", "penalty",
    "negative", "weak", "cut", "layoff", "slowdown", "default", "write-off",
    "investigation", "charges", "risks", "fall short"
}


@dataclass
class NewsItem:
    title:     str
    source:    str
    date:      str
    url:       str
    sentiment: str = "NEUTRAL"   # BULLISH | BEARISH | NEUTRAL
    score:     int = 0           # +1 / -1 / 0


@dataclass
class NewsSummary:
    symbol:         str
    total_articles: int = 0
    bullish_count:  int = 0
    bearish_count:  int = 0
    neutral_count:  int = 0
    sentiment:      str = "NEUTRAL"   # overall BULLISH | BEARISH | NEUTRAL
    sentiment_score: float = 0.0      # -1.0 to +1.0
    headlines:      List[str] = field(default_factory=list)   # top 5
    key_events:     List[str] = field(default_factory=list)   # NSE announcements
    summary_text:   str = ""          # one-line summary for AI prompt


def _score_headline(title: str) -> tuple[str, int]:
    """Simple keyword-based sentiment scoring."""
    text = title.lower()
    bull = sum(1 for w in _BULLISH_WORDS if w in text)
    bear = sum(1 for w in _BEARISH_WORDS if w in text)
    if bull > bear:
        return "BULLISH", 1
    elif bear > bull:
        return "BEARISH", -1
    return "NEUTRAL", 0


# ── Source 1: Google News RSS ─────────────────────────────────────────────────

def _fetch_google_news(symbol: str, company_name: str = "") -> List[NewsItem]:
    """Fetch from Google News RSS — no API key needed."""
    query = f"{symbol} NSE India stock"
    if company_name:
        query = f"{company_name} stock NSE"
    url = f"https://news.google.com/rss/search?q={quote_plus(query)}&hl=en-IN&gl=IN&ceid=IN:en"
    items = []
    try:
        r = requests.get(url, timeout=_TIMEOUT,
                         headers={"User-Agent": "Mozilla/5.0"})
        r.raise_for_status()
        root = ET.fromstring(r.content)
        channel = root.find("channel")
        if channel is None:
            return []
        for item in list(channel.findall("item"))[:8]:
            title = item.findtext("title", "").strip()
            link  = item.findtext("link",  "").strip()
            pub   = item.findtext("pubDate", "").strip()
            # Strip source from Google News title format: "Headline - Source"
            clean_title = re.sub(r"\s*-\s*[^-]+$", "", title).strip()
            source = re.search(r"-\s*([^-]+)$", title)
            source_name = source.group(1).strip() if source else "News"
            sentiment, score = _score_headline(clean_title)
            items.append(NewsItem(
                title=clean_title, source=source_name,
                date=pub[:16], url=link,
                sentiment=sentiment, score=score
            ))
    except Exception as e:
        logger.warning(f"[News Google] {symbol}: {e}")
    return items


# ── Source 2: NSE Announcements ───────────────────────────────────────────────

def _fetch_nse_announcements(symbol: str) -> List[str]:
    """Fetch official NSE corporate announcements."""
    try:
        from app.market.nse_data import get_announcements
        ann = get_announcements(symbol, limit=5)
        return [f"[NSE] {a['date']}: {a['subject'][:120]}" for a in ann if a.get("subject")]
    except Exception as e:
        logger.warning(f"[News NSE Announce] {symbol}: {e}")
        return []


# ── Source 3: Economic Times RSS ─────────────────────────────────────────────

def _fetch_et_news(symbol: str) -> List[NewsItem]:
    """Fetch from Economic Times Markets RSS."""
    url = "https://economictimes.indiatimes.com/markets/stocks/rssfeeds/2146842.cms"
    items = []
    try:
        r = requests.get(url, timeout=_TIMEOUT,
                         headers={"User-Agent": "Mozilla/5.0"})
        r.raise_for_status()
        root = ET.fromstring(r.content)
        channel = root.find("channel")
        if channel is None:
            return []
        sym_upper = symbol.upper()
        for item in list(channel.findall("item"))[:20]:
            title = item.findtext("title", "").strip()
            # Only include if headline mentions the symbol
            if sym_upper not in title.upper():
                continue
            link  = item.findtext("link", "").strip()
            pub   = item.findtext("pubDate", "")[:16]
            sentiment, score = _score_headline(title)
            items.append(NewsItem(
                title=title, source="Economic Times",
                date=pub, url=link,
                sentiment=sentiment, score=score
            ))
    except Exception as e:
        logger.warning(f"[News ET] {symbol}: {e}")
    return items


# ── Main: Get news summary for a symbol ──────────────────────────────────────

def get_news_summary(symbol: str, company_name: str = "") -> NewsSummary:
    """
    Returns a complete news sentiment summary for the symbol.
    Cached for 10 minutes per symbol.
    """
    cache_key = symbol.upper()
    now = time.time()

    with _lock:
        if cache_key in _cache and (now - _cache_ts.get(cache_key, 0)) < _CACHE_TTL:
            return _cache[cache_key]

    summary = NewsSummary(symbol=symbol.upper())

    # Fetch from all sources
    google_items = _fetch_google_news(symbol, company_name)
    et_items     = _fetch_et_news(symbol)
    all_items    = google_items + et_items

    # Aggregate sentiment
    for item in all_items:
        summary.total_articles += 1
        if item.sentiment == "BULLISH":
            summary.bullish_count += 1
        elif item.sentiment == "BEARISH":
            summary.bearish_count += 1
        else:
            summary.neutral_count += 1

    # Overall sentiment
    if summary.total_articles > 0:
        net = summary.bullish_count - summary.bearish_count
        summary.sentiment_score = round(net / summary.total_articles, 2)
        if summary.sentiment_score > 0.2:
            summary.sentiment = "BULLISH"
        elif summary.sentiment_score < -0.2:
            summary.sentiment = "BEARISH"
        else:
            summary.sentiment = "NEUTRAL"

    # Top headlines (most extreme sentiment first)
    sorted_items = sorted(all_items, key=lambda x: abs(x.score), reverse=True)
    summary.headlines = [i.title for i in sorted_items[:5]]

    # NSE announcements
    summary.key_events = _fetch_nse_announcements(symbol)

    # One-line summary for AI
    b = summary.bullish_count
    be= summary.bearish_count
    n = summary.neutral_count
    if summary.total_articles == 0:
        summary.summary_text = "No recent news found"
    else:
        summary.summary_text = (
            f"{summary.total_articles} articles: {b} bullish, {be} bearish, {n} neutral. "
            f"Overall: {summary.sentiment} (score {summary.sentiment_score:+.2f})"
        )

    with _lock:
        _cache[cache_key]    = summary
        _cache_ts[cache_key] = now

    return summary


# ── Singleton ─────────────────────────────────────────────────────────────────
news_feed = type("NewsFeed", (), {
    "get": staticmethod(get_news_summary)
})()
