"""
Página de paper trading (simulación).
Misma lógica que cartera.py pero con modo='simulacion' y capital virtual configurable.
"""
import pandas as pd
import plotly.graph_objects as go
import yfinance as yf
import dash
from dash import dcc, html, Input, Output, State, callback, dash_table, no_update
import dash_bootstrap_components as dbc

from database import (
    añadir_transaccion, eliminar_transaccion,
    get_transacciones, get_posiciones,
)
from activos import ACTIVOS
from database import get_tickers_extra

dash.register_page(__name__, path="/simulacion", name="Simulación", order=3)

MODO = "simulacion"


def opciones_empresa():
    extra = {r["nombre"]: r["ticker"] for r in get_tickers_extra()}
    todos = {**ACTIVOS, **extra}
    return [{"label": k.replace("_", " "), "value": k} for k in sorted(todos)]


def precio_actual(ticker):
    t = yf.Ticker(ticker)
    try:
        df = t.history(period="2d")
        if not df.empty:
            return float(df["Close"].iloc[-1])
    except Exception:
        pass
    try:
        p = t.fast_info.last_price
        if p and p > 0:
            return float(p)
    except Exception:
        pass
    return None


def construir_tabla_posiciones():
    extra = {r["nombre"]: r["ticker"] for r in get_tickers_extra()}
    todos = {**ACTIVOS, **extra}
    posiciones = get_posiciones(MODO)
    filas = []
    total_invertido = 0
    total_valor = 0

    for pos in posiciones:
        ticker_sym = todos.get(pos["empresa"], "")
        pv = precio_actual(ticker_sym) if ticker_sym else None
        cantidad = pos["cantidad_neta"]
        coste_total = pos["coste_total"]
        precio_medio = coste_total / cantidad if cantidad else 0
        valor_actual = pv * cantidad if pv else None
        pl = (valor_actual - coste_total) if valor_actual is not None else None
        pl_pct = (pl / coste_total * 100) if (pl is not None and coste_total) else None

        total_invertido += coste_total
        if valor_actual:
            total_valor += valor_actual

        filas.append({
            "Empresa": pos["empresa"].replace("_", " "),
            "Cantidad": f"{cantidad:.0f}",
            "Precio medio": f"{precio_medio:.3f} €",
            "Precio actual": f"{pv:.3f} €" if pv else "—",
            "Valor actual": f"{valor_actual:,.2f} €" if valor_actual else "—",
            "P&L €": f"{pl:+,.2f} €" if pl is not None else "—",
            "P&L %": f"{pl_pct:+.2f}%" if pl_pct is not None else "—",
        })

    return filas, total_invertido, total_valor


layout = dbc.Container(fluid=True, children=[
    dbc.Row(className="mt-3 mb-3", children=[
        dbc.Col(html.H4("Simulación (Paper Trading)"), width="auto"),
        dbc.Col(dbc.Badge("Virtual", color="warning", text_color="dark", className="ms-2 align-self-center"), width="auto"),
    ]),

    dbc.Alert(
        "Esta cartera es completamente virtual. Las operaciones no tienen efecto real.",
        color="warning", className="mb-3",
    ),

    dbc.Row(id="sim-resumen", className="mb-4 g-3"),

    html.H5("Posiciones abiertas"),
    html.Div(id="sim-tabla-posiciones", className="mb-4"),

    dbc.Card(className="mb-4", children=[
        dbc.CardHeader(html.H6("Registrar operación simulada", className="mb-0")),
        dbc.CardBody([
            dbc.Row(className="g-2", children=[
                dbc.Col([dbc.Label("Empresa"), dcc.Dropdown(id="sim-empresa", options=opciones_empresa(), clearable=False)], md=3),
                dbc.Col([dbc.Label("Tipo"), dcc.Dropdown(id="sim-tipo", options=[{"label": "Compra", "value": "compra"}, {"label": "Venta", "value": "venta"}], value="compra", clearable=False)], md=1),
                dbc.Col([dbc.Label("Fecha"), dbc.Input(id="sim-fecha", type="date", value=pd.Timestamp.today().strftime("%Y-%m-%d"))], md=2),
                dbc.Col([dbc.Label("Cantidad"), dbc.Input(id="sim-cantidad", type="number", min=0, placeholder="100")], md=1),
                dbc.Col([dbc.Label("Precio (€)"), dbc.Input(id="sim-precio", type="number", min=0, step=0.001, placeholder="5.23")], md=2),
                dbc.Col([dbc.Label("Comisión (€)"), dbc.Input(id="sim-comision", type="number", min=0, value=0, step=0.01)], md=1),
                dbc.Col([dbc.Label("Notas"), dbc.Input(id="sim-notas", type="text", placeholder="Opcional")], md=2),
            ]),
            dbc.Row(className="mt-3", children=[
                dbc.Col(dbc.Button("Registrar", id="sim-btn-add", color="warning", n_clicks=0), width="auto"),
                dbc.Col(html.Span(id="sim-msg", className="text-muted ms-3 align-self-center")),
            ]),
        ]),
    ]),

    html.H5("Historial de operaciones simuladas"),
    html.Div(id="sim-tabla-historial", className="mb-4"),

    html.H5("Evolución de la simulación vs IBEX 35"),
    dcc.Graph(id="sim-grafica-evolucion", style={"height": "45vh"}),

    dcc.Store(id="sim-refresh", data=0),
    dcc.Interval(id="sim-interval", interval=5*60*1000, n_intervals=0),
])


@callback(
    Output("sim-refresh", "data"),
    Output("sim-msg", "children"),
    Input("sim-btn-add", "n_clicks"),
    State("sim-empresa", "value"),
    State("sim-tipo", "value"),
    State("sim-fecha", "value"),
    State("sim-cantidad", "value"),
    State("sim-precio", "value"),
    State("sim-comision", "value"),
    State("sim-notas", "value"),
    State("sim-refresh", "data"),
    prevent_initial_call=True,
)
def registrar_simulacion(_, empresa, tipo, fecha, cantidad, precio, comision, notas, refresh):
    if not all([empresa, tipo, fecha, cantidad, precio]):
        return no_update, "Rellena todos los campos obligatorios."
    extra = {r["nombre"]: r["ticker"] for r in get_tickers_extra()}
    todos = {**ACTIVOS, **extra}
    ticker = todos.get(empresa, empresa)
    añadir_transaccion(MODO, empresa, ticker, tipo, fecha, float(cantidad), float(precio), float(comision or 0), notas or "")
    return (refresh or 0) + 1, f"✓ {tipo.capitalize()} simulada registrada."


@callback(
    Output("sim-tabla-posiciones", "children"),
    Output("sim-resumen", "children"),
    Output("sim-grafica-evolucion", "figure"),
    Output("sim-tabla-historial", "children"),
    Input("sim-refresh", "data"),
    Input("sim-interval", "n_intervals"),
)
def actualizar_vista(refresh, _interval):
    filas, total_inv, total_val = construir_tabla_posiciones()
    pl_total = total_val - total_inv
    pl_pct = pl_total / total_inv * 100 if total_inv else 0
    color = "#2E7D32" if pl_total >= 0 else "#C62828"

    tarjetas = [
        _tarjeta("Capital empleado", f"{total_inv:,.2f} €", "#E65100"),
        _tarjeta("Valor actual", f"{total_val:,.2f} €", "#E65100"),
        _tarjeta("P&L simulado", f"{pl_total:+,.2f} €  ({pl_pct:+.2f}%)", color),
    ]

    tabla_pos = dash_table.DataTable(
        data=filas,
        columns=[{"name": c, "id": c} for c in filas[0]] if filas else [],
        style_cell={"textAlign": "right", "padding": "6px 12px", "fontFamily": "monospace"},
        style_header={"fontWeight": "bold", "backgroundColor": "#fff3e0"},
        style_data_conditional=[
            {"if": {"filter_query": '{P&L €} contains "+"'}, "color": "#2E7D32"},
            {"if": {"filter_query": '{P&L €} contains "-"'}, "color": "#C62828"},
        ],
        page_size=20,
    ) if filas else html.P("Sin posiciones abiertas.", className="text-muted")

    # Historial
    trans = get_transacciones(MODO)
    tabla_hist = dash_table.DataTable(
        data=[{
            "Fecha": t["fecha"], "Empresa": t["empresa"].replace("_", " "),
            "Tipo": t["tipo"].capitalize(), "Cantidad": t["cantidad"],
            "Precio": f"{t['precio']:.3f} €", "Comisión": f"{t['comision']:.2f} €",
            "ID": t["id"],
        } for t in trans],
        columns=[{"name": c, "id": c} for c in ["Fecha", "Empresa", "Tipo", "Cantidad", "Precio", "Comisión"]],
        style_cell={"textAlign": "right", "padding": "5px 10px"},
        style_header={"fontWeight": "bold", "backgroundColor": "#fff3e0"},
        page_size=10,
    ) if trans else html.P("Sin operaciones registradas.", className="text-muted")

    try:
        fig = _grafica_evolucion_vs_ibex()
    except Exception:
        fig = go.Figure()
        fig.update_layout(template="plotly_white", title="Sin datos de evolución")

    return tabla_pos, tarjetas, fig, tabla_hist


def _tarjeta(titulo, valor, color):
    return dbc.Col(dbc.Card(dbc.CardBody([
        html.P(titulo, className="text-muted mb-1 small"),
        html.H5(valor, style={"color": color}),
    ])), md=4)


def _grafica_evolucion_vs_ibex():
    import os
    DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data")
    extra = {r["nombre"]: r["ticker"] for r in get_tickers_extra()}
    todos = {**ACTIVOS, **extra}
    trans = get_transacciones(MODO)

    fig = go.Figure()

    if not trans:
        fig.update_layout(template="plotly_white", title="Sin datos de simulación")
        return fig

    df_t = pd.DataFrame(trans)
    df_t["fecha"] = pd.to_datetime(df_t["fecha"])
    df_t = df_t.sort_values("fecha")

    empresas = df_t["empresa"].unique()
    precios_hist = {}
    for emp in empresas:
        ruta = os.path.join(DATA_DIR, f"historico_{emp}.csv")
        if os.path.exists(ruta):
            try:
                dh = pd.read_csv(ruta, index_col=0, skiprows=[1, 2], header=0)
                dh.index = pd.to_datetime(dh.index, errors="coerce")
                col = "Adj Close" if "Adj Close" in dh.columns else "Close"
                s = pd.to_numeric(dh[col], errors="coerce").dropna()
                precios_hist[emp] = s.groupby(level=0).last()
            except Exception:
                pass

    if not precios_hist:
        fig.update_layout(template="plotly_white", title="Sin históricos disponibles")
        return fig

    idx_global = sorted(set().union(*[s.index for s in precios_hist.values()]))
    idx_global = [d for d in idx_global if d >= df_t["fecha"].min()]
    valor_serie = []

    for fecha in idx_global:
        ops_hasta = df_t[df_t["fecha"] <= fecha]
        valor_dia = 0
        for emp, grp in ops_hasta.groupby("empresa"):
            cantidad = (
                grp[grp["tipo"] == "compra"]["cantidad"].sum()
                - grp[grp["tipo"] == "venta"]["cantidad"].sum()
            )
            if cantidad > 0 and emp in precios_hist:
                serie = precios_hist[emp]
                idx_disp = serie.index[serie.index <= fecha]
                if len(idx_disp):
                    valor_dia += cantidad * float(serie[idx_disp[-1]])
        valor_serie.append(valor_dia)

    coste_inicial = df_t[df_t["tipo"] == "compra"].apply(
        lambda r: r["cantidad"] * r["precio"] + r["comision"], axis=1
    ).sum()

    # Normalizar a base 100
    if valor_serie and valor_serie[0]:
        sim_norm = [v / coste_inicial * 100 for v in valor_serie]
    else:
        sim_norm = valor_serie

    fig.add_trace(go.Scatter(
        x=idx_global, y=sim_norm,
        name="Simulación (base 100)",
        line=dict(color="#E65100", width=2),
        fill="tozeroy", fillcolor="rgba(230,81,0,0.07)",
    ))

    # IBEX normalizado
    ruta_ibex = os.path.join(DATA_DIR, "historico_IBEX_35.csv")
    if os.path.exists(ruta_ibex):
        try:
            dh = pd.read_csv(ruta_ibex, index_col=0, skiprows=[1, 2], header=0)
            dh.index = pd.to_datetime(dh.index, errors="coerce")
            col = "Adj Close" if "Adj Close" in dh.columns else "Close"
            ibex = pd.to_numeric(dh[col], errors="coerce").dropna()
            ibex = ibex[ibex.index >= df_t["fecha"].min()]
            if not ibex.empty:
                ibex_norm = ibex / ibex.iloc[0] * 100
                fig.add_trace(go.Scatter(
                    x=ibex_norm.index, y=ibex_norm.values,
                    name="IBEX 35 (base 100)",
                    line=dict(color="#1565C0", width=1.5, dash="dash"),
                ))
        except Exception:
            pass

    fig.update_layout(
        template="plotly_white",
        hovermode="x unified",
        yaxis_title="Rendimiento (base 100)",
        margin=dict(l=40, r=20, t=30, b=30),
    )
    return fig
