"""Market research. Gemini + Google Search finds the next 24h of economic events and the news that moves your pair.
The result is stored as a 'brief'. The deterministic news blackout reads the event times from it."""
import json
import os
import pandas as pd
from . import llm, prompts, schema
from .llm import LLMError, parse_json


def currencies(symbol):
    s = symbol.replace("/", "").replace("-", "").replace("_", "").upper()
    return (s[:3], s[3:6]) if len(s) >= 6 else (s, "")


class Research:
    def __init__(self, router, journal, bc, symbol, env=None):
        self.r, self.j, self.bc, self.symbol = router, journal, bc, symbol
        self.env = env if env is not None else os.environ

    def latest(self):
        row = self.j.latest_research()
        if not row:
            return None
        age = (pd.Timestamp.now(tz="UTC") - pd.Timestamp(row["ts"])).total_seconds() / 60
        b = dict(row["brief"])
        if "search_engine" not in b:
            b["search_engine"] = "serper" if self.env.get("SERPER_API_KEY") else ("tavily" if self.env.get("TAVILY_API_KEY") else "gemini")
        return {**b, "age_min": round(age), "sources": row["sources"]}

    def due(self, now):
        row = self.j.latest_research()
        return row is None or (now - pd.Timestamp(row["ts"])).total_seconds() / 60 >= self.bc.get("research_minutes", 60)

    def refresh(self, now=None):
        now = now or pd.Timestamp.now(tz="UTC")
        a, b = currencies(self.symbol)
        if not a:
            raise LLMError("cannot work out the currencies of this symbol")
        ask = (f"Now: {now.strftime('%A %Y-%m-%d %H:%M')} UTC. Pair: {self.symbol} (currencies {a} and {b}). "
               f"List the events of the next 24 hours and the key news of the last 24 hours for {a} and {b}.")
        brief, sources, provider, model = None, [], "", ""
        serper_key = self.env.get("SERPER_API_KEY")
        tavily_key = self.env.get("TAVILY_API_KEY")
        engine = "gemini"

        if serper_key:
            hits = self._serper(f"{a} {b} forex news economic calendar today", serper_key)
            reply = self.r.chat(prompts.SYS_RESEARCH_FROM_SNIPPETS, ask + "\n\n<untrusted_search_results>\n" +
                                json.dumps(hits)[:6000] + "\n</untrusted_search_results>", json_mode=True,
                                timeout=self.bc.get("research_timeout", 45), max_tokens=1500)
            brief = schema.brief(parse_json(reply.text), now)
            sources = [{"title": h["title"], "uri": h["url"]} for h in hits[:8] if h.get("url")]
            provider, model = reply.provider, reply.model
            engine = "serper"
        elif tavily_key:
            hits = self._tavily(f"{a} {b} forex news economic calendar today", tavily_key)
            reply = self.r.chat(prompts.SYS_RESEARCH_FROM_SNIPPETS, ask + "\n\n<untrusted_search_results>\n" +
                                json.dumps(hits)[:6000] + "\n</untrusted_search_results>", json_mode=True,
                                timeout=self.bc.get("research_timeout", 45), max_tokens=1500)
            brief = schema.brief(parse_json(reply.text), now)
            sources = [{"title": h["title"], "uri": h["url"]} for h in hits[:8] if h.get("url")]
            provider, model = reply.provider, reply.model
            engine = "tavily"
        elif self.r.has("gemini"):
            reply = self.r.chat(prompts.SYS_RESEARCH, ask, json_mode=False, search=True, timeout=self.bc.get("research_timeout", 45),
                                max_tokens=2000, temperature=0.1)
            brief = schema.brief(parse_json(reply.text), now)
            sources, provider, model = reply.sources[:8], reply.provider, reply.model
            engine = "gemini"
        else:
            raise LLMError("research needs SERPER_API_KEY (serper.dev), TAVILY_API_KEY (tavily.com), or GEMINI_API_KEY (Google Search)")
        brief["provider"], brief["model"], brief["search_engine"] = provider, model, engine
        self.j.save_research(now, brief, sources)
        return {**brief, "sources": sources, "age_min": 0}

    @staticmethod
    def _serper(query, key, timeout=15):
        r = llm._http("POST", "https://google.serper.dev/search", {"X-API-KEY": key},
                      {"q": query, "num": 6}, timeout)
        items = (r.get("organic") or []) + (r.get("news") or [])
        return [{"title": (x.get("title") or "")[:120], "url": x.get("link") or x.get("url") or "", "content": (x.get("snippet") or x.get("content") or "")[:400]}
                for x in items[:8] if (x.get("link") or x.get("url"))]

    @staticmethod
    def _tavily(query, key, timeout=15):
        r = llm._http("POST", "https://api.tavily.com/search", {"Authorization": f"Bearer {key}"},
                      {"query": query, "topic": "news", "search_depth": "basic", "max_results": 6, "time_range": "day"}, timeout)
        return [{"title": (x.get("title") or "")[:120], "url": x.get("url"), "content": (x.get("content") or "")[:400]}
                for x in r.get("results", [])]

    def blackout(self, now, before_min=30, after_min=15):
        """A HIGH-impact event for either currency that is about to happen (or just did). Decided by code, not by the AI."""
        row = self.j.latest_research()
        if not row:
            return None
        a, b = currencies(self.symbol)
        for e in row["brief"].get("events", []):
            if e["impact"] != "high" or e["currency"] not in (a, b):
                continue
            mins = (pd.Timestamp(e["time_utc"]) - now).total_seconds() / 60
            if -after_min <= mins <= before_min:
                return e
        return None
