import json
import logging
import time
import os
from functools import wraps
import csv

# Directorio base del proyecto (donde están los .py)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))

def log_trade_to_csv(trade_data, csv_file=None):
    if csv_file is None:
        csv_file = os.path.join(BASE_DIR, "trades_history.csv")
        
    file_exists = os.path.exists(csv_file)
    fields = ["timestamp", "symbol", "side", "amount", "price", "cost", "notes", "pnl", "pnl_pct"]
    
    with open(csv_file, mode='a', newline='') as f:
        writer = csv.DictWriter(f, fieldnames=fields)
        if not file_exists:
            writer.writeheader()
            
        row = {f: trade_data.get(f, "") for f in fields}
        if not row["timestamp"]:
            row["timestamp"] = time.strftime('%Y-%m-%d %H:%M:%S')
            
        writer.writerow(row)

def setup_logger(name="BinanceBot", log_file=None):
    """Configura el sistema de logging para consola y archivo.
    Usa ruta absoluta para que funcione correctamente desde cron."""
    if log_file is None:
        log_file = os.path.join(BASE_DIR, "bot.log")

    logger = logging.getLogger(name)
    logger.setLevel(logging.ERROR)
    if not logger.handlers:
        formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
        
        # Consola
        ch = logging.StreamHandler()
        ch.setFormatter(formatter)
        logger.addHandler(ch)
        
        # Archivo (ruta absoluta)
        fh = logging.FileHandler(log_file)
        fh.setFormatter(formatter)
        logger.addHandler(fh)
    return logger

def retry_api(max_retries=3, backoff_factor=1):
    """
    Decorador para reintentar llamadas a la API si fallan por errores de red
    o rate limits, introduciendo un retraso (backoff) progresivo.
    """
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            import ccxt  # Import lazy - las excepciones están en el namespace raíz
            retries = 0
            while retries < max_retries:
                try:
                    return func(*args, **kwargs)
                except (ccxt.NetworkError, ccxt.RateLimitExceeded) as e:
                    retries += 1
                    sleep_time = backoff_factor * (2 ** (retries - 1))
                    logging.warning(f"Error en {func.__name__}: {e}. Reintento {retries}/{max_retries} en {sleep_time}s...")
                    time.sleep(sleep_time)
                except Exception as e:
                    logging.error(f"Error crítico en {func.__name__}: {e}")
                    raise e
            raise Exception(f"Fallo persistente en {func.__name__} después de {max_retries} reintentos.")
        return wrapper
    return decorator

class StateManager:
    """Maneja el estado local del bot usando un archivo JSON para persistencia."""
    def __init__(self, filepath=None):
        if filepath is None:
            filepath = os.path.join(BASE_DIR, "state.json")
        self.filepath = filepath
        self.state = self.load()

    def load(self):
        if os.path.exists(self.filepath):
            with open(self.filepath, 'r') as f:
                return json.load(f)
        return {"open_position": None}

    def save(self):
        with open(self.filepath, 'w') as f:
            json.dump(self.state, f, indent=4)

    def set_position(self, data):
        self.state["open_position"] = data
        self.save()

    def clear_position(self):
        self.state["open_position"] = None
        self.save()

    def get_position(self):
        return self.state.get("open_position")

    def set_cooldown(self, symbol, minutes=90):
        """Bloquea re-entrada en un símbolo durante X minutos tras un SL."""
        cooldowns = self.state.get("cooldowns", {})
        cooldowns[symbol] = time.time() + (minutes * 60)
        self.state["cooldowns"] = cooldowns
        self.save()
        logging.info(f"[COOLDOWN] {symbol} bloqueado por {minutes} min.")

    def is_on_cooldown(self, symbol):
        """Retorna True si el símbolo está en período de cooldown."""
        cooldowns = self.state.get("cooldowns", {})
        expiry = cooldowns.get(symbol, 0)
        if time.time() < expiry:
            remaining = int((expiry - time.time()) / 60)
            logging.info(f"[COOLDOWN] {symbol} en cooldown. Faltan ~{remaining} min.")
            return True
        return False

    def clean_expired_cooldowns(self):
        """Limpia cooldowns expirados del estado para no acumular basura."""
        cooldowns = self.state.get("cooldowns", {})
        now = time.time()
        self.state["cooldowns"] = {s: exp for s, exp in cooldowns.items() if exp > now}
        self.save()
