import os
import logging
import smtplib
import ssl
from email.mime.text import MIMEText
import numpy as np
import yfinance as yf
import pandas as pd
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger

from activos import ACTIVOS, FECHA_INICIO_HISTORICO
from database import get_tickers_extra
from config import EMAIL_ORIGEN, EMAIL_APP_PASSWORD, EMAIL_DESTINO, SMTP_HOST, SMTP_PORT, ALERTA_UMBRAL

DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
logger = logging.getLogger(__name__)


def csv_path(nombre):
    return os.path.join(DATA_DIR, f"historico_{nombre}.csv")


def descargar_ticker(nombre, ticker):
    """Descarga incremental: solo los días que faltan desde el último registro."""
    ruta = csv_path(nombre)
    try:
        if os.path.exists(ruta):
            df_prev = pd.read_csv(ruta, index_col=0, skiprows=[1, 2], header=0)
            df_prev.index = pd.to_datetime(df_prev.index, errors="coerce")
            df_prev = df_prev[df_prev.index.notna()]
            if not df_prev.empty:
                ultima = df_prev.index.max()
                inicio = (ultima + pd.Timedelta(days=1)).strftime("%Y-%m-%d")
            else:
                inicio = FECHA_INICIO_HISTORICO
        else:
            inicio = FECHA_INICIO_HISTORICO

        df_nuevo = yf.download(ticker, start=inicio, progress=False)
        if df_nuevo.empty:
            logger.info("Sin datos nuevos para %s (%s)", nombre, ticker)
            return

        if os.path.exists(ruta):
            df_nuevo.to_csv(ruta, mode="a", header=False)
        else:
            df_nuevo.to_csv(ruta)

        logger.info("Actualizado %s (%s): %d filas nuevas", nombre, ticker, len(df_nuevo))

    except Exception:
        logger.exception("Error descargando %s (%s)", nombre, ticker)


def calcular_indicador_ultima_sesion(nombre):
    """Devuelve el valor del Índ.Vol. (×100) de la última sesión, o None si no se puede calcular."""
    ruta = csv_path(nombre)
    if not os.path.exists(ruta):
        return None
    try:
        df = pd.read_csv(ruta, index_col=0, skiprows=[1, 2], header=0)
        df.index = pd.to_datetime(df.index, errors="coerce")
        df = df[df.index.notna()].sort_index()
        for col in df.columns:
            df[col] = pd.to_numeric(df[col], errors="coerce")

        col = "Adj Close" if "Adj Close" in df.columns else "Close"
        if "Volume" not in df.columns or len(df) < 14:
            return None

        s = df[col].dropna()
        vol = df["Volume"].replace(0, np.nan)
        sma14 = s.rolling(14, min_periods=14).mean()
        v_med14 = vol.rolling(14, min_periods=14).mean()
        denominador = sma14 * v_med14
        ind = (s * vol) / denominador.replace(0, np.nan) * 100

        last = ind.dropna()
        return float(last.iloc[-1]) if not last.empty else None
    except Exception:
        logger.exception("Error calculando indicador para %s", nombre)
        return None


def enviar_email_alertas(empresas_alerta):
    """Envía un correo listando las empresas con Índ.Vol. > ALERTA_UMBRAL."""
    if not empresas_alerta:
        return
    if not EMAIL_APP_PASSWORD or EMAIL_APP_PASSWORD.startswith("X"):
        logger.warning("App Password de Yahoo no configurada; no se envía alerta.")
        return

    lineas = [f"  • {nombre.replace('_', ' ')}: {valor:.1f}" for nombre, valor in empresas_alerta]
    cuerpo = (
        f"Alerta de Índ.Vol. — {pd.Timestamp.now().strftime('%d/%m/%Y')}\n\n"
        f"Las siguientes empresas superan el umbral de {ALERTA_UMBRAL}:\n\n"
        + "\n".join(lineas)
        + "\n\nMensaje automático generado por BolsaWeb."
    )

    msg = MIMEText(cuerpo, "plain", "utf-8")
    msg["Subject"] = f"[BolsaWeb] Alerta Índ.Vol. > {ALERTA_UMBRAL} — {len(empresas_alerta)} empresa(s)"
    msg["From"] = EMAIL_ORIGEN
    msg["To"] = EMAIL_DESTINO

    try:
        ctx = ssl.create_default_context()
        with smtplib.SMTP_SSL(SMTP_HOST, 465, context=ctx, timeout=15) as smtp:
            smtp.login(EMAIL_ORIGEN, EMAIL_APP_PASSWORD)
            smtp.sendmail(EMAIL_ORIGEN, [EMAIL_DESTINO], msg.as_string())
        logger.info("Alerta enviada a %s con %d empresa(s)", EMAIL_DESTINO, len(empresas_alerta))
    except Exception:
        logger.exception("Error enviando alerta por correo")


def descargar_todos():
    logger.info("=== Inicio descarga automática ===")
    todos = dict(ACTIVOS)
    for row in get_tickers_extra():
        todos[row["nombre"]] = row["ticker"]

    for nombre, ticker in todos.items():
        descargar_ticker(nombre, ticker)
    logger.info("=== Descarga automática finalizada ===")

    # Comprueba indicador y envía alertas
    empresas_alerta = []
    for nombre in todos:
        val = calcular_indicador_ultima_sesion(nombre)
        if val is not None and val > ALERTA_UMBRAL:
            empresas_alerta.append((nombre, val))
            logger.info("Alerta: %s Índ.Vol.=%.1f > %s", nombre, val, ALERTA_UMBRAL)

    if empresas_alerta:
        enviar_email_alertas(empresas_alerta)
    else:
        logger.info("Sin empresas que superen el umbral de alerta (%s)", ALERTA_UMBRAL)


def iniciar_scheduler():
    scheduler = BackgroundScheduler(timezone="Europe/Madrid")
    # Lunes a viernes a las 18:45 (tras el cierre del mercado español)
    scheduler.add_job(descargar_todos, CronTrigger(day_of_week="mon-fri", hour=18, minute=45))
    scheduler.start()
    logger.info("Scheduler iniciado: descarga diaria L-V a las 18:45")
    return scheduler
