def search(query: str) -> str:
    """
    使用 DuckDuckGo 执行网页搜索并格式化结果。
    失败时会重试一次。
    """
    for attempt in range(2):
        try:
            with DDGS() as ddgs:
                results = list(ddgs.text(query, max_results=10))
            
            if not results:
                return "No search results found."
            
            formatted = []
            for i, res in enumerate(results, 1):
                title = res.get('title', 'No title').encode('utf-8', 'ignore').decode('utf-8')
                body = res.get('body', 'No summary').encode('utf-8', 'ignore').decode('utf-8')
                href = res.get('href', 'No link')
                formatted.append(f"{i}. {title}\n   {body[:150]}...\n   {href}")
                
            return "Search Results:\n" + "\n\n".join(formatted)
            print(f"Search attempt {attempt  + 1} failed: {e}. Retrying in 2 seconds...")
        except Exception as e:
            time.sleep(2)
            
    return "Search error: Exceeded maximum retries."

# ============================================
# 主要工具函数
# ============================================
def get_stock_price(ticker: str) -> str:
    """获取股票当前价格和变化"""
    time.sleep(1)
    try:
        stock = yf.Ticker(ticker)
        hist = stock.history(period="5d")
        if hist.empty or len(hist) < 2:
            raise ValueError("Historical data is insufficient")
        
        current_price = hist['Close'].iloc[-1]
        prev_close = hist['Close'].iloc[-2]
        
        change = current_price - prev_close
        change_percent = (change / prev_close) * 100
        
        return f"{ticker} Current Price: ${current_price:.2f} | Change: ${change:.2f} ({change_percent:+.2f}%)"
    except Exception as e:
        return f"Error fetching price for {ticker}: {str(e)}. Please use the search function."

def get_company_info(ticker: str) -> str:
    """
    从 Yahoo Finance 获取公司资料信息。
    失败时回退到网页搜索。
    """
    try:
        stock = yf.Ticker(ticker)
        info = stock.info
        
        if not info or 'longName' not in info:
            raise ValueError(f"'{ticker}' information is incomplete, possibly invalid ticker.")
            
        summary = info.get('longBusinessSummary')
        description = (summary[:200] + '...') if summary else 'No description'
        
        result = f"""Company Profile ({ticker}):
- Name: {info.get('longName', 'Unknown')}
- Sector: {info.get('sector', 'Unknown')}
- Industry: {info.get('industry', 'Unknown')}
- Market Cap: ${info.get('marketCap', 0):,.0f}
- Trailing PE: {info.get('trailingPE', 'Unknown')}
- Forward PE: {info.get('forwardPE', 'Unknown')}
- Description: {description}"""
        return result
    except Exception as e:
        print(f"yfinance fetch for '{ticker}' failed: {e}. Falling back to web search.")
        return search(f"{ticker} company profile")

def get_company_news(ticker: str) -> str:
    """
    使用 yfinance 获取公司新闻，增加了重试机制。
    如果多次尝试后仍失败，则回退到通用搜索。
    """
    max_retries = 2
    for attempt in range(max_retries):
        try:
            stock = yf.Ticker(ticker)
            news = stock.news
            
            if news:
                recent_news = news[:5]
                news_list = []
                for i, article in enumerate(recent_news, 1):
                    title = article.get('title', 'No title')
                    publisher = article.get('publisher', 'Unknown source')
                    pub_time = article.get('providerPublishTime', 0)
                    date_str = datetime.fromtimestamp(pub_time).strftime('%Y-%m-%d') if pub_time else 'Unknown date'
                    news_list.append(f"{i}. [{date_str}] {title} ({publisher})")

                return f"Latest News ({ticker}):\n" + "\n".join(news_list)
            
            print(f"Warning: yfinance.news returned empty list for {ticker} (attempt {attempt + 1}/{max_retries})")
            time.sleep(2)
        except Exception as e:
            print(f"Error: Exception fetching news for {ticker} (attempt {attempt + 1}/{max_retries}): {e}")
            time.sleep(2)

    print(f"Info: yfinance.news failed, falling back to search for {ticker}")
    return search(f"{ticker} latest news")

def get_market_sentiment() -> str:
    """
    获取市场情绪指标 - CNN Fear & Greed Index。
    使用多种方法尝试抓取，提供备用方案。
    """
    # 方法1: 尝试抓取CNN JSON API (最可靠)
    try:
        url = "https://production.dataviz.cnn.io/index/fearandgreed/graphdata"
        response = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'}, timeout=10)
        response.raise_for_status()
        data = response.json()
        score = float(data['fear_and_greed']['score'])
        rating = data['fear_and_greed']['rating']
        return f"CNN Fear & Greed Index: {score:.1f} ({rating})"
    except Exception as e:
        print(f"Method 1 (CNN API) failed: {e}. Trying fallback.")
    
    # 方法2: 使用搜索作为备用方案
    try:
        search_result = search("CNN Fear and Greed Index current value today")
        match = re.search(r'(?:Index|Score)[:\s]*(\d+\.?\d*)\s*\((\w+\s?\w*)\)', search_result, re.IGNORECASE)
        if match:
            score = float(match.group(1))
            rating = match.group(2)
            return f"CNN Fear & Greed Index (via search): {score:.1f} ({rating})"
    except Exception as e:
        print(f"Method 2 (Search) failed: {e}")
    
    return "Fear & Greed Index: Unable to fetch. Please check https://money.cnn.com/data/fear-and-greed/ manually."

def get_economic_events() -> str:
    """搜索当前月份的主要美国经济事件。"""
    now = datetime.now()
    query = f"major upcoming US economic events {now.strftime('%B %Y')} (FOMC, CPI, jobs report)"
    raw_search = search(query)
    
    events_found = re.findall(r'(?:FOMC|CPI|Non-Farm|Jobs Report|Unemployment Rate).*?(?:on|date)?[\s:]*(\w+\s+\d{1,2})', raw_search, re.IGNORECASE)
    
    if events_found:
        result = f"📅 Major Economic Events for {now.strftime('%B %Y')}:\n"
        # 使用字典去重并保留顺序
        unique_events = list(dict.fromkeys(events_found))
        for event in unique_events:
            result += f"  - {event}\n"
        return result
    
    return "Unable to parse specific event dates from search results. Please check a financial calendar for details."

def get_performance_comparison(tickers: dict) -> str:
    """
    比较字典中股票代码的年初至今和1年期表现。
    tickers 格式: {"Display Name": "TICKER"}
    """
    data = {}
    for name, ticker in tickers.items():
        time.sleep(1)
        try:
            stock = yf.Ticker(ticker)
            hist = stock.history(period="2y")
            if hist.empty:
                print(f"Warning: No historical data for {ticker}")
                continue
            
            end_price = hist['Close'].iloc[-1]
            
            # YTD Performance
            current_year_data = hist[hist.index.year == datetime.now().year]
            if current_year_data.empty:
                perf_ytd = float('nan') # Not enough data
            else:
                current_year_start_price = current_year_data['Close'].iloc[0]
                perf_ytd = ((end_price - current_year_start_price) / current_year_start_price) * 100
            
            # 1-Year Performance
            if len(hist) >= 252:
                one_year_ago_price = hist['Close'].iloc[-252]
                perf_1y = ((end_price - one_year_ago_price) / one_year_ago_price) * 100
            else:
                perf_1y = float('nan') # Not enough data

            data[name] = {"Current": f"{end_price:,.2f}", "YTD": f"{perf_ytd:+.2f}%", "1-Year": f"{perf_1y:+.2f}%"}
        except Exception as e:
            print(f"Error processing performance for '{ticker}': {e}")
            data[name] = {"Current": "N/A", "YTD": "N/A", "1-Year": "N/A"}
    
    if not data:
        return "Unable to fetch performance data."
            
    header = f"{'Ticker':<25} {'Current Price':<15} {'YTD %':<12} {'1-Year %':<12}\n" + "-" * 67 + "\n"
    rows = [f"{name:<25} {metrics['Current']:<15} {metrics['YTD']:<12} {metrics['1-Year']:<12}" for name, metrics in data.items()]
    return "Performance Comparison:\n\n" + header + "\n".join(rows)

def analyze_historical_drawdowns(ticker: str = "^IXIC") -> str:
    """计算并报告过去10年的前3大历史回撤。"""
    try:
        stock = yf.Ticker(ticker)
        hist = stock.history(period="10y")
        if hist.empty:
            return f"No historical data for {ticker}."
            
        hist['cummax'] = hist['Close'].cummax()
        hist['drawdown'] = (hist['Close'] - hist['cummax']) / hist['cummax']
        
        drawdowns, in_drawdown = [], False
        start_date, peak_val, min_val = None, 0, 0
        
        for i in range(len(hist)):
            current_dd = hist['drawdown'].iloc[i]
            if current_dd < 0 and not in_drawdown:
                in_drawdown = True
                start_date = hist.index[i-1] if i > 0 else hist.index[i]
                peak_val = hist['Close'].iloc[i-1] if i > 0 else hist['Close'].iloc[i]
                min_val = hist['Close'].iloc[i]
            elif current_dd < 0 and in_drawdown and hist['Close'].iloc[i] < min_val:
                min_val = hist['Close'].iloc[i]
            elif current_dd == 0 and in_drawdown:
                in_drawdown = False
                drawdowns.append({
                    "start": start_date, "end": hist.index[i],
                    "drawdown": (min_val - peak_val) / peak_val,
                    "recovery_days": (hist.index[i] - start_date).days
                })
        
        if in_drawdown:
            drawdowns.append({
                "start": start_date, "end": None,
                "drawdown": (min_val - peak_val) / peak_val, "recovery_days": "Ongoing"
            })
            
        if not drawdowns:
            return f"No significant drawdowns found for {ticker}."
            
        top_3 = sorted(drawdowns, key=lambda x: x['drawdown'])[:3]
        
        result = [f"Top 3 Historical Drawdowns for {ticker}:\n"]
        for i, dd in enumerate(top_3, 1):
            end_str = dd['end'].strftime('%Y-%m-%d') if dd['end'] else 'Ongoing'
            result.append(
                f"{i}. {dd['start'].year}  Event: Max Drawdown {dd['drawdown']:.2%} | "
                f"Recovery: {dd['recovery_days']} days ({dd['start'].strftime('%Y-%m-%d')} to {end_str})"
            )
        return "\n".join(result)
    except Exception as e:
        return f"Historical analysis error: {e}."

def get_current_datetime() -> str:
    """返回当前日期和时间"""
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")