#!/usr/bin/env python3
import os
import json
import requests
from datetime import datetime, timedelta, timezone
from dotenv import load_dotenv
from collections import Counter
import re

load_dotenv()

KALSHI_API_KEY = os.getenv('KALSHI_API_KEY')
KALSHI_BASE_URL = 'https://api.elections.kalshi.com/trade-api/v2'

def fetch_polymarket_markets():
    """Fetch active markets from Polymarket"""
    url = "https://gamma-api.polymarket.com/markets"
    try:
        # Get markets ending in next 2 days
        now = datetime.now(timezone.utc)
        seven_days = now + timedelta(days=2)
        
        response = requests.get(url, params={
            'closed': 'false', 
            'limit': 500,
            'end_date_max': seven_days.isoformat()
        }, timeout=60)
        response.raise_for_status()
        markets = response.json()
        
        processed = []
        for m in markets:
            if not m.get('active') or m.get('closed'):
                continue
            
            end_date_str = m.get('endDate')
            if not end_date_str:
                continue
            
            # Get yes prices (Polymarket is always yes/no binary)
            yes_bid = None
            yes_ask = None
            
            # Try to get from market data first
            if m.get('bestBid'):
                yes_bid = float(m.get('bestBid'))
            if m.get('bestAsk'):
                yes_ask = float(m.get('bestAsk'))
            
            # If not available, try orderbook API
            if (yes_bid is None or yes_ask is None):
                token_id = m.get('clobTokenIds', [''])[0] if m.get('clobTokenIds') else None
                if token_id:
                    try:
                        ob_url = f"https://clob.polymarket.com/book?token_id={token_id}"
                        ob_resp = requests.get(ob_url, timeout=5)
                        if ob_resp.ok:
                            book = ob_resp.json()
                            bids = book.get('bids', [])
                            asks = book.get('asks', [])
                            if yes_bid is None and bids:
                                yes_bid = float(bids[0]['price'])
                            if yes_ask is None and asks:
                                yes_ask = float(asks[0]['price'])
                    except:
                        pass
            
            # Calculate no prices (no_bid = 1 - yes_ask, no_ask = 1 - yes_bid)
            no_bid = (1 - yes_ask) if yes_ask is not None else None
            no_ask = (1 - yes_bid) if yes_bid is not None else None
            
            # Build proper URL - remove numeric suffix from slug if present
            slug = m.get('slug', '')
            if slug:
                # Remove -XXX suffix from grouped markets
                import re
                slug_clean = re.sub(r'-\d+$', '', slug)
                url = f"https://polymarket.com/event/{slug_clean}"
            else:
                url = f"https://polymarket.com/market/{m.get('id', '')}"
            
            processed.append({
                'platform': 'Polymarket',
                'id': m.get('id'),
                'question': m.get('question', ''),
                'end_date': m.get('endDate'),
                'volume': float(m.get('volume', 0)),
                'yes_bid': yes_bid,
                'yes_ask': yes_ask,
                'no_bid': no_bid,
                'no_ask': no_ask,
                'url': url
            })
        
        return processed
    except Exception as e:
        print(f"Error fetching Polymarket: {e}")
        return []

def fetch_kalshi_markets():
    """Fetch active markets from Kalshi"""
    headers = {'Authorization': f'Bearer {KALSHI_API_KEY}'}
    
    try:
        # Get markets closing in next 2 days
        now = datetime.now(timezone.utc)
        thirty_days = now + timedelta(days=2)
        
        markets_url = f"{KALSHI_BASE_URL}/markets"
        params = {
            'limit': 200,
            'min_close_ts': int(now.timestamp()),
            'max_close_ts': int(thirty_days.timestamp())
        }
        
        print(f"Kalshi API filter: min={int(now.timestamp())} max={int(thirty_days.timestamp())}")
        print(f"Date range: {now.strftime('%Y-%m-%d %H:%M')} to {thirty_days.strftime('%Y-%m-%d %H:%M')} UTC")
        
        all_markets = []
        cursor = None
        page = 0
        
        # Paginate through results - get many pages to find sports games
        for _ in range(50):  # Max 50 pages = 10000 markets
            page += 1
            if cursor:
                params['cursor'] = cursor
            
            response = requests.get(markets_url, headers=headers, params=params, timeout=30)
            response.raise_for_status()
            data = response.json()
            
            markets = data.get('markets', [])
            all_markets.extend(markets)
            
            cursor = data.get('cursor')
            if not cursor:
                break
        
        print(f"Fetched {len(all_markets)} total markets from Kalshi API")
        

        
        processed = []
        for m in all_markets:
            # Accept both 'active' and 'open' status
            if m.get('status') not in ['active', 'open']:
                continue
                
            close_time_str = m.get('close_time')
            if not close_time_str:
                continue
            

            
            ticker = m.get('ticker')
            event_ticker = m.get('event_ticker', '')
            
            # Skip parlay markets (they have mve_selected_legs)
            if m.get('mve_selected_legs'):
                continue
            
            # Get yes and no prices
            yes_bid = float(m.get('yes_bid_dollars', 0)) if m.get('yes_bid_dollars') else None
            yes_ask = float(m.get('yes_ask_dollars', 0)) if m.get('yes_ask_dollars') else None
            no_bid = float(m.get('no_bid_dollars', 0)) if m.get('no_bid_dollars') else None
            no_ask = float(m.get('no_ask_dollars', 0)) if m.get('no_ask_dollars') else None
            
            # Skip markets with no liquidity
            if not yes_bid and not yes_ask and not no_bid and not no_ask:
                continue
            
            # Build URL using event_ticker
            url = f"https://kalshi.com/markets/{event_ticker}" if event_ticker else f"https://kalshi.com/markets/{ticker}"
            
            processed.append({
                'platform': 'Kalshi',
                'id': ticker,
                'question': m.get('title', ''),
                'end_date': m.get('close_time'),
                'volume': float(m.get('volume', 0)),
                'yes_bid': yes_bid,
                'yes_ask': yes_ask,
                'no_bid': no_bid,
                'no_ask': no_ask,
                'url': url
            })
        
        return processed
    except Exception as e:
        print(f"Error fetching Kalshi: {e}")
        return []

def normalize_text(text):
    """Normalize text for comparison"""
    text = text.lower()
    # Remove punctuation except spaces
    text = re.sub(r'[^a-z0-9\s]', ' ', text)
    # Remove extra spaces
    text = ' '.join(text.split())
    return text

def text_similarity(text1, text2):
    """Calculate similarity between two texts using character-level matching"""
    from difflib import SequenceMatcher
    
    # Normalize both texts
    norm1 = normalize_text(text1)
    norm2 = normalize_text(text2)
    
    # Use SequenceMatcher for character-level similarity
    return SequenceMatcher(None, norm1, norm2).ratio()

def match_markets(poly_markets, kalshi_markets):
    """Match markets between platforms based on keyword similarity"""
    matches = []
    used_kalshi = set()
    
    print(f"Matching {len(poly_markets)} Polymarket markets against {len(kalshi_markets)} Kalshi markets...")
    
    for i, pm in enumerate(poly_markets):
        if i % 50 == 0:
            print(f"  Processed {i}/{len(poly_markets)} markets...")
        
        best_match = None
        best_score = 0.50  # 50% text similarity threshold
        
        for km in kalshi_markets:
            if km['id'] in used_kalshi:
                continue
            
            score = text_similarity(pm['question'], km['question'])
            if score > best_score:
                best_score = score
                best_match = km
        
        if best_match:
            used_kalshi.add(best_match['id'])
            
            # Calculate arbitrage opportunities (buy yes on one, buy no on other)
            arb_opportunity = None
            
            # Strategy 1: Buy YES on Polymarket, Buy NO on Kalshi
            if pm['yes_ask'] and best_match['no_ask']:
                cost = pm['yes_ask'] + best_match['no_ask']
                profit = 1 - cost
                if profit > 0.01:  # At least 1% profit
                    arb_opportunity = {
                        'type': 'Buy YES on Polymarket, Buy NO on Kalshi',
                        'profit': profit,
                        'poly_side': 'YES',
                        'poly_price': pm['yes_ask'],
                        'kalshi_side': 'NO',
                        'kalshi_price': best_match['no_ask']
                    }
            
            # Strategy 2: Buy NO on Polymarket, Buy YES on Kalshi
            if pm['no_ask'] and best_match['yes_ask']:
                cost = pm['no_ask'] + best_match['yes_ask']
                profit = 1 - cost
                if profit > 0.01:
                    if not arb_opportunity or profit > arb_opportunity['profit']:
                        arb_opportunity = {
                            'type': 'Buy NO on Polymarket, Buy YES on Kalshi',
                            'profit': profit,
                            'poly_side': 'NO',
                            'poly_price': pm['no_ask'],
                            'kalshi_side': 'YES',
                            'kalshi_price': best_match['yes_ask']
                        }
            
            matches.append({
                'polymarket': pm,
                'kalshi': best_match,
                'similarity': best_score,
                'arbitrage': arb_opportunity
            })
    
    return matches

def main():
    print("Note: Arbitrage strategy buys YES on one platform and NO on the other.")
    print("This guarantees profit regardless of outcome.\n")
    print("Fetching Polymarket markets...")
    poly_markets = fetch_polymarket_markets()
    print(f"Found {len(poly_markets)} Polymarket markets")
    
    print("\nFetching Kalshi markets...")
    kalshi_markets = fetch_kalshi_markets()
    print(f"Found {len(kalshi_markets)} Kalshi markets")
    
    # Save raw market data for debugging
    with open('raw_markets.json', 'w') as f:
        json.dump({
            'polymarket': [{'id': m['id'], 'question': m['question']} for m in poly_markets[:50]],
            'kalshi': [{'id': m['id'], 'question': m['question']} for m in kalshi_markets[:50]]
        }, f, indent=2)
    print("\nSaved sample questions to raw_markets.json")
    
    print("\nMatching markets...")
    matches = match_markets(poly_markets, kalshi_markets)
    print(f"Found {len(matches)} potential matches")
    
    # Count arbitrage opportunities
    arb_count = sum(1 for m in matches if m['arbitrage'])
    print(f"Found {arb_count} arbitrage opportunities")
    
    # Save results
    output = {
        'timestamp': datetime.now().isoformat(),
        'polymarket_count': len(poly_markets),
        'kalshi_count': len(kalshi_markets),
        'matches': matches,
        'polymarket_markets': poly_markets,
        'kalshi_markets': kalshi_markets
    }
    
    with open('arbitrage_data.json', 'w') as f:
        json.dump(output, f, indent=2)
    
    print("\nData saved to arbitrage_data.json")
    
    if arb_count > 0:
        print("\n=== ARBITRAGE OPPORTUNITIES ===")
        for m in matches:
            if m['arbitrage']:
                arb = m['arbitrage']
                print(f"\n{m['polymarket']['question'][:80]}...")
                print(f"  {arb['type']}")
                print(f"  Profit: ${arb['profit']:.3f} ({arb['profit']*100:.1f}%)")
                print(f"  Polymarket {arb['poly_side']} @ ${arb['poly_price']:.3f}")
                print(f"  Kalshi {arb['kalshi_side']} @ ${arb['kalshi_price']:.3f}")
                print(f"  Total cost: ${arb['poly_price'] + arb['kalshi_price']:.3f}")

if __name__ == '__main__':
    main()
