"""
Database module for Hyperliquid Monitor - Fixed Version 2

This version includes:
- Correct side mapping (A=SELL, B=BUY)
- Proper address handling (from parameter, not fill dict)
- Thread-safe operations
- Optimized indexes for better performance
- Powerful query methods for analysis
"""

import sqlite3
import threading
from datetime import datetime
from pathlib import Path
from typing import Dict, Optional, List, Tuple

def init_database(db_path: Optional[str] = None) -> str:
    """
    Initialize a new database for the Hyperliquid monitor or validate an existing one.
    
    Args:
        db_path: Optional path to the database. If None, creates a default 'trades.db'
                in the current directory.
    
    Returns:
        str: The absolute path to the initialized database
        
    Raises:
        sqlite3.Error: If there's an error creating or accessing the database
        ValueError: If the provided path is invalid
    """
    if db_path is None:
        db_path = "trades.db"
    
    # Convert to Path object for easier manipulation
    db_path = Path(db_path).resolve()
    
    # Create parent directories if they don't exist
    db_path.parent.mkdir(parents=True, exist_ok=True)
    
    try:
        # Create and test the database connection
        conn = sqlite3.connect(str(db_path))
        cursor = conn.cursor()
        
        # Create the fills table
        cursor.execute('''
        CREATE TABLE IF NOT EXISTS fills (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            timestamp DATETIME,
            address TEXT,
            coin TEXT,
            side TEXT,
            size REAL,
            price REAL,
            direction TEXT,
            tx_hash TEXT,
            fee REAL,
            fee_token TEXT,
            start_position REAL,
            closed_pnl REAL,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP
        )
        ''')
        
        # Create indexes for better query performance
        cursor.execute('''
        CREATE INDEX IF NOT EXISTS idx_fills_address ON fills(address)
        ''')
        cursor.execute('''
        CREATE INDEX IF NOT EXISTS idx_fills_timestamp ON fills(timestamp)
        ''')
        cursor.execute('''
        CREATE INDEX IF NOT EXISTS idx_fills_coin ON fills(coin)
        ''')
        cursor.execute('''
        CREATE INDEX IF NOT EXISTS idx_fills_side ON fills(side)
        ''')
        
        conn.commit()
        conn.close()
        
        return str(db_path)
        
    except sqlite3.Error as e:
        raise sqlite3.Error(f"Failed to initialize database at {db_path}: {str(e)}")
    except Exception as e:
        raise ValueError(f"Error creating database at {db_path}: {str(e)}")

class TradeDatabase:
    def __init__(self, db_path: str):
        """Initialize the database connection and create tables if they don't exist."""
        self.db_path = init_database(db_path)
        self._local = threading.local()
        
    @property
    def conn(self) -> sqlite3.Connection:
        if not hasattr(self._local, 'conn'):
            self._local.conn = sqlite3.connect(self.db_path)
        return self._local.conn

    def store_fill(self, fill: Dict, address: str) -> None:
        """
        Store a fill in the database.
        
        Args:
            fill: Fill data from API
            address: The address that made the trade (NOT from fill dict)
        """
        cursor = self.conn.cursor()
        timestamp = datetime.fromtimestamp(int(fill.get("time", 0)) / 1000)
        
        # FIXED: Correct side mapping (A = Ask = SELL, B = Bid = BUY)
        side = "SELL" if fill.get("side") == "A" else "BUY"
        
        cursor.execute('''
        INSERT INTO fills (
            timestamp, address, coin, side, size, price, direction, tx_hash, 
            fee, fee_token, start_position, closed_pnl
        )
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        ''', (
            timestamp,
            address,  # FIXED: Use address parameter instead of fill.get("address")
            fill.get("coin", "Unknown"),
            side,
            float(fill.get("sz", 0)),
            float(fill.get("px", 0)),
            fill.get("dir", "Unknown"),
            fill.get("hash", "Unknown"),
            float(fill.get("fee", 0)),
            fill.get("feeToken", "Unknown"),
            float(fill.get("startPosition", 0)),
            float(fill.get("closedPnl", 0))
        ))
        
        self.conn.commit()
    
    def get_fills_by_address(self, address: str, limit: Optional[int] = None) -> List[Tuple]:
        """
        Retrieve fills for a specific address.
        
        Args:
            address: The address to query
            limit: Maximum number of results to return
            
        Returns:
            List of fill records as tuples
        """
        cursor = self.conn.cursor()
        query = '''
        SELECT * FROM fills 
        WHERE address = ? 
        ORDER BY timestamp DESC
        '''
        if limit:
            query += f' LIMIT {limit}'
        
        cursor.execute(query, (address,))
        return cursor.fetchall()
    
    def get_fills_by_coin(self, coin: str, address: Optional[str] = None, limit: Optional[int] = None) -> List[Tuple]:
        """
        Retrieve fills for a specific coin, optionally filtered by address.
        
        Args:
            coin: The coin to query
            address: Optional address filter
            limit: Maximum number of results to return
            
        Returns:
            List of fill records as tuples
        """
        cursor = self.conn.cursor()
        
        if address:
            query = 'SELECT * FROM fills WHERE coin = ? AND address = ? ORDER BY timestamp DESC'
            params = (coin, address)
        else:
            query = 'SELECT * FROM fills WHERE coin = ? ORDER BY timestamp DESC'
            params = (coin,)
        
        if limit:
            query += f' LIMIT {limit}'
        
        cursor.execute(query, params)
        return cursor.fetchall()
    
    def get_address_statistics(self, address: str) -> Dict:
        """
        Get trading statistics for an address.
        
        Args:
            address: The address to query
            
        Returns:
            Dictionary with statistics
        """
        cursor = self.conn.cursor()
        
        # Total fills
        cursor.execute('SELECT COUNT(*) FROM fills WHERE address = ?', (address,))
        total_fills = cursor.fetchone()[0]
        
        # Buy vs Sell counts
        cursor.execute('SELECT COUNT(*) FROM fills WHERE address = ? AND side = ?', (address, 'BUY'))
        buy_count = cursor.fetchone()[0]
        cursor.execute('SELECT COUNT(*) FROM fills WHERE address = ? AND side = ?', (address, 'SELL'))
        sell_count = cursor.fetchone()[0]
        
        # Total volume by side
        cursor.execute('SELECT COALESCE(SUM(size * price), 0) FROM fills WHERE address = ? AND side = ?', (address, 'BUY'))
        buy_volume = cursor.fetchone()[0]
        cursor.execute('SELECT COALESCE(SUM(size * price), 0) FROM fills WHERE address = ? AND side = ?', (address, 'SELL'))
        sell_volume = cursor.fetchone()[0]
        
        # Total PnL
        cursor.execute('SELECT COALESCE(SUM(closed_pnl), 0) FROM fills WHERE address = ?', (address,))
        total_pnl = cursor.fetchone()[0]
        
        # Total fees
        cursor.execute('SELECT COALESCE(SUM(fee), 0) FROM fills WHERE address = ?', (address,))
        total_fees = cursor.fetchone()[0]
        
        # Most traded coins
        cursor.execute('''
        SELECT coin, COUNT(*) as trade_count 
        FROM fills 
        WHERE address = ? 
        GROUP BY coin 
        ORDER BY trade_count DESC 
        LIMIT 10
        ''', (address,))
        top_coins = cursor.fetchall()
        
        # First and last trade timestamps
        cursor.execute('SELECT MIN(timestamp), MAX(timestamp) FROM fills WHERE address = ?', (address,))
        first_trade, last_trade = cursor.fetchone()
        
        return {
            'address': address,
            'total_fills': total_fills,
            'buy_count': buy_count,
            'sell_count': sell_count,
            'buy_volume': buy_volume,
            'sell_volume': sell_volume,
            'total_volume': buy_volume + sell_volume,
            'total_pnl': total_pnl,
            'total_fees': total_fees,
            'top_coins': top_coins,
            'first_trade': first_trade,
            'last_trade': last_trade
        }
    
    def get_recent_activity(self, minutes: int = 60, address: Optional[str] = None) -> List[Tuple]:
        """
        Get recent trading activity.
        
        Args:
            minutes: Number of minutes to look back
            address: Optional address filter
            
        Returns:
            List of recent fill records
        """
        cursor = self.conn.cursor()
        cutoff_time = datetime.now().timestamp() - (minutes * 60)
        cutoff_datetime = datetime.fromtimestamp(cutoff_time)
        
        if address:
            query = 'SELECT * FROM fills WHERE address = ? AND timestamp >= ? ORDER BY timestamp DESC'
            params = (address, cutoff_datetime)
        else:
            query = 'SELECT * FROM fills WHERE timestamp >= ? ORDER BY timestamp DESC'
            params = (cutoff_datetime,)
        
        cursor.execute(query, params)
        return cursor.fetchall()
    
    def get_coin_summary(self, address: str, coin: str) -> Dict:
        """
        Get summary statistics for a specific coin traded by an address.
        
        Args:
            address: The address to query
            coin: The coin to analyze
            
        Returns:
            Dictionary with coin-specific statistics
        """
        cursor = self.conn.cursor()
        
        # Total trades
        cursor.execute('SELECT COUNT(*) FROM fills WHERE address = ? AND coin = ?', (address, coin))
        total_trades = cursor.fetchone()[0]
        
        # Buy vs Sell
        cursor.execute('SELECT COUNT(*) FROM fills WHERE address = ? AND coin = ? AND side = ?', (address, coin, 'BUY'))
        buy_count = cursor.fetchone()[0]
        cursor.execute('SELECT COUNT(*) FROM fills WHERE address = ? AND coin = ? AND side = ?', (address, coin, 'SELL'))
        sell_count = cursor.fetchone()[0]
        
        # Total sizes
        cursor.execute('SELECT COALESCE(SUM(size), 0) FROM fills WHERE address = ? AND coin = ? AND side = ?', (address, coin, 'BUY'))
        total_bought = cursor.fetchone()[0]
        cursor.execute('SELECT COALESCE(SUM(size), 0) FROM fills WHERE address = ? AND coin = ? AND side = ?', (address, coin, 'SELL'))
        total_sold = cursor.fetchone()[0]
        
        # Average prices
        cursor.execute('SELECT AVG(price) FROM fills WHERE address = ? AND coin = ? AND side = ?', (address, coin, 'BUY'))
        avg_buy_price = cursor.fetchone()[0] or 0
        cursor.execute('SELECT AVG(price) FROM fills WHERE address = ? AND coin = ? AND side = ?', (address, coin, 'SELL'))
        avg_sell_price = cursor.fetchone()[0] or 0
        
        # PnL for this coin
        cursor.execute('SELECT COALESCE(SUM(closed_pnl), 0) FROM fills WHERE address = ? AND coin = ?', (address, coin))
        coin_pnl = cursor.fetchone()[0]
        
        return {
            'address': address,
            'coin': coin,
            'total_trades': total_trades,
            'buy_count': buy_count,
            'sell_count': sell_count,
            'total_bought': total_bought,
            'total_sold': total_sold,
            'net_position': total_bought - total_sold,
            'avg_buy_price': avg_buy_price,
            'avg_sell_price': avg_sell_price,
            'total_pnl': coin_pnl
        }

    def close(self) -> None:
        """Close the database connection."""
        if hasattr(self._local, 'conn'):
            self._local.conn.close()
            delattr(self._local, 'conn')


# Alias for backward compatibility
Database = TradeDatabase
