# app.py
# Demo de Tablero Ejecutivo Data-Driven inspirado en el caso "FC Barcelona: More Than a Club, More Than Data"
# Autor: Julio / Equipo BC
# Ejecuta: streamlit run app.py

import streamlit as st
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import plotly.express as px
import plotly.graph_objects as go
import random

# =========================
# CONFIGURACIÓN BÁSICA
# =========================
st.set_page_config(
    page_title="BC | Executive Data Board",
    page_icon="⚽",
    layout="wide",
    initial_sidebar_state="expanded"
)

# Paleta básica para status
STATUS_COLORS = {
    "ok": "#16a34a",
    "warn": "#f59e0b",
    "risk": "#ef4444",
}

# Semilla reproducible
np.random.seed(42)
random.seed(42)

# =========================
# HELPER FUNCTIONS
# =========================
def kpi_card(label, value, delta=None, help_text=None, border_color="#e5e7eb"):
    """Tarjeta KPI simple con formato consistente."""
    st.markdown(
        f"""
        <div style="border:1px solid {border_color}; border-radius:14px; padding:14px; margin-bottom:10px;">
            <div style="font-size:12px; color:#6b7280; margin-bottom:6px;">{label}</div>
            <div style="display:flex; align-items:baseline; gap:8px;">
                <div style="font-size:28px; font-weight:700;">{value}</div>
                <div style="font-size:13px; color:#6b7280;">{("" if delta is None else delta)}</div>
            </div>
            <div style="font-size:12px; color:#9ca3af; margin-top:6px;">{("" if help_text is None else help_text)}</div>
        </div>
        """,
        unsafe_allow_html=True
    )

def traffic_light(status: str, text: str):
    color = STATUS_COLORS.get(status, "#9ca3af")
    st.markdown(
        f"""
        <div style="display:flex; gap:8px; align-items:center; margin:6px 0;">
            <div style="width:10px; height:10px; border-radius:50%; background:{color};"></div>
            <div style="font-size:14px;">{text}</div>
        </div>
        """,
        unsafe_allow_html=True
    )

def percentage(x, decimals=1):
    return f"{x:.{decimals}f}%"

def currency(x, decimals=0, symbol="€"):
    return f"{symbol}{x:,.{decimals}f}"

def gen_month_range(n_months=18):
    end = datetime.today().replace(day=1)
    months = [end - pd.DateOffset(months=m) for m in range(n_months)][::-1]
    return pd.to_datetime(months)

# =========================
# DATOS DEMO (SINTÉTICOS)
# =========================
def build_demo_data(n_months=18):
    months = gen_month_range(n_months)
    df = pd.DataFrame({"month": months})

    # Ingresos por áreas (Marketing/Media, Estadio, Socios, Merchandising/BLM, Otros/Transfers)
    df["rev_marketing_media"] = np.random.normal(65, 6, n_months).clip(48, 85) * 1e6
    df["rev_stadium"] = np.random.normal(25, 5, n_months).clip(10, 40) * 1e6
    df["rev_members"] = np.random.normal(10, 1.8, n_months).clip(6, 14) * 1e6
    df["rev_blm"] = np.random.normal(30, 7, n_months).clip(15, 55) * 1e6
    df["rev_other"] = np.random.normal(12, 3, n_months).clip(6, 20) * 1e6
    df["revenue_total"] = df[["rev_marketing_media","rev_stadium","rev_members","rev_blm","rev_other"]].sum(axis=1)

    # Costos (salarios deportivos + no deportivos + OPEX)
    df["cost_sports_salaries"] = np.random.normal(55, 5, n_months).clip(40, 75) * 1e6
    df["cost_non_sports"] = np.random.normal(12, 2.5, n_months).clip(7, 20) * 1e6
    df["cost_opex"] = np.random.normal(10, 2, n_months).clip(6, 18) * 1e6
    df["cost_total"] = df[["cost_sports_salaries","cost_non_sports","cost_opex"]].sum(axis=1)

    # EBITDA y margen
    df["ebitda"] = df["revenue_total"] - df["cost_total"]
    df["margin"] = (df["ebitda"] / df["revenue_total"]) * 100

    # Métricas digitales / fans
    df["fans_contactables"] = np.cumsum(np.random.randint(5_000, 20_000, n_months)) + 1_000_000
    df["site_sessions"] = np.random.normal(2_000_000, 250_000, n_months).astype(int)
    df["app_sessions"] = np.random.normal(800_000, 120_000, n_months).astype(int)
    df["cr_venta_tienda_online"] = np.random.normal(1.8, 0.25, n_months).clip(1.1, 2.8)  # %
    df["aov_ecommerce"] = np.random.normal(84, 9, n_months).clip(55, 110)  # ticket promedio €

    # BLM (ventas por canal)
    df["ventas_tienda_flagship"] = np.random.normal(7, 1.5, n_months).clip(4, 10) * 1e6
    df["ventas_tiendas_lic"] = np.random.normal(5, 1.2, n_months).clip(3, 9) * 1e6
    df["ventas_ecommerce"] = (df["site_sessions"] * (df["cr_venta_tienda_online"]/100) * df["aov_ecommerce"]).astype(float)

    # Estadio (Espai Barça): asistencia, ARPU, tiempos, incidencias
    df["asistencia_media"] = np.random.normal(72_000, 8_000, n_months).clip(40_000, 99_000)
    df["arpu_estadio"] = np.random.normal(28, 5, n_months).clip(16, 45)  # gasto medio por fan €
    df["tiempo_acceso_medio"] = np.random.normal(9.5, 1.8, n_months).clip(5, 16)  # minutos
    df["incidencias_matchday"] = np.random.poisson(18, n_months)

    # Deporte (rendimiento físico agregado): carga, distancias, lesiones
    df["carga_fisica_idx"] = np.random.normal(68, 8, n_months).clip(45, 90)  # índice 0-100
    df["dist_media_equipo_km"] = np.random.normal(108, 7, n_months).clip(90, 124)  # suma km once titular
    df["lesiones_mes"] = np.random.poisson(6, n_months).clip(2, 12)

    # Calidad de datos / gobierno
    df["data_freshness_days"] = np.random.normal(11, 2.5, n_months).clip(6, 18)
    df["porc_fuentes_automatizadas"] = np.linspace(40, 78, n_months)  # % de automatización subiendo

    return df

df = build_demo_data(n_months=18)

# =========================
# SIDEBAR (FILTROS & CONTEXTO)
# =========================
st.sidebar.title("⚙️ Controles")
period = st.sidebar.selectbox(
    "Periodo de análisis",
    options=["Últimos 6 meses", "Últimos 12 meses", "Últimos 18 meses"],
    index=1
)
period_map = {"Últimos 6 meses": 6, "Últimos 12 meses": 12, "Últimos 18 meses": 18}
n = period_map[period]
dfp = df.tail(n).copy()

segmento_fan = st.sidebar.multiselect(
    "Segmentos Fan (demo)",
    ["Local", "Turista", "Socio", "Digital Only"],
    default=["Local", "Turista", "Digital Only"]
)

objetivo_margen = st.sidebar.slider("Objetivo de margen EBITDA (%)", 5.0, 30.0, 18.0, 0.5)
objetivo_freshness = st.sidebar.slider("Objetivo Freshness de datos (días)", 5, 15, 10, 1)
st.sidebar.markdown("---")
st.sidebar.caption("Demo educativo inspirado en el caso. Datos sintéticos.")

# =========================
# HEADER
# =========================
st.markdown("## ⚽ BC | Executive Data Board — *Más que datos, decisiones*")
st.markdown(
    "<div style='color:#6b7280;'>Tablero demo alineado a los objetivos estratégicos: Excelencia Deportiva, Marca Global, Patrimonio (Espai Barça), Implicación Social y Sostenibilidad Financiera.</div>",
    unsafe_allow_html=True,
)

# =========================
# OVERVIEW (KPIs PRINCIPALES)
# =========================
st.markdown("### 📌 Overview Ejecutivo")

c1, c2, c3, c4, c5 = st.columns(5)
with c1:
    kpi_card("Ingresos últimos 12m", currency(dfp["revenue_total"].tail(12).sum()/1e6, 0)+"M",
             delta="YoY demo: +" + percentage(np.random.uniform(4, 12)))
with c2:
    kpi_card("Margen EBITDA prom.", percentage(dfp["margin"].mean()),
             delta=("Obj: " + percentage(objetivo_margen)))
with c3:
    kpi_card("Fans contactables CRM", f"{int(dfp['fans_contactables'].iloc[-1]):,}",
             delta="Δ mensual: +" + f"{int(dfp['fans_contactables'].iloc[-1]-dfp['fans_contactables'].iloc[-2]):,}")
with c4:
    kpi_card("ARPU Estadio (matchday)", currency(dfp["arpu_estadio"].mean(), 2),
             help_text="Gasto medio por fan")
with c5:
    kpi_card("% Fuentes Automatizadas", percentage(dfp["porc_fuentes_automatizadas"].iloc[-1]),
             delta="Meta 2021: 100% (caso)")

# Alertas rápidas
st.markdown("##### Estado rápido")
cols = st.columns(4)
with cols[0]:
    status_margen = "ok" if dfp["margin"].mean() >= objetivo_margen else "warn"
    traffic_light(status_margen, f"Margen EBITDA objetivo {percentage(objetivo_margen)} | actual {percentage(dfp['margin'].mean())}")
with cols[1]:
    status_fresh = "ok" if dfp["data_freshness_days"].mean() <= objetivo_freshness else "risk"
    traffic_light(status_fresh, f"Freshness datos objetivo {objetivo_freshness}d | actual {dfp['data_freshness_days'].mean():.1f}d")
with cols[2]:
    status_asistencia = "ok" if dfp["asistencia_media"].mean() >= 65000 else "warn"
    traffic_light(status_asistencia, f"Asistencia media {int(dfp['asistencia_media'].mean()):,} (umbral 65k)")
with cols[3]:
    status_lesiones = "ok" if dfp["lesiones_mes"].mean() <= 7 else "warn"
    traffic_light(status_lesiones, f"Lesiones/mes {dfp['lesiones_mes'].mean():.1f} (umbral 7)")

st.markdown("---")

# =========================
# PESTAÑAS PRINCIPALES
# =========================
tabs = st.tabs([
    "📈 Financials",
    "📣 Fan & Digital",
    "🛍️ Merchandising (BLM)",
    "🏟️ Espai Barça",
    "⚽ Deporte & Rendimiento",
    "🔒 Datos & Gobierno",
    "🧪 What-If Simulator"
])

# -------------------------
# TAB 1: FINANCIALS
# -------------------------
with tabs[0]:
    st.subheader("📈 Financials — ingresos, costos y márgenes")

    # Serie de ingresos por área
    df_area = dfp.melt(id_vars="month", value_vars=[
        "rev_marketing_media","rev_stadium","rev_members","rev_blm","rev_other"
    ], var_name="area", value_name="revenue")

    fig_rev = px.area(
        df_area, x="month", y="revenue", color="area",
        title="Ingresos por área (mensual)",
        labels={"month":"Mes","revenue":"Ingresos (€)","area":"Área"}
    )
    fig_rev.update_yaxes(tickprefix="€")
    st.plotly_chart(fig_rev, use_container_width=True)

    # EBITDA trend
    fig_eb = go.Figure()
    fig_eb.add_trace(go.Bar(
        x=dfp["month"], y=dfp["ebitda"]/1e6, name="EBITDA (M€)"
    ))
    fig_eb.add_trace(go.Scatter(
        x=dfp["month"], y=dfp["margin"], name="Margen EBITDA (%)", yaxis="y2", mode="lines+markers"
    ))
    fig_eb.update_layout(
        title="EBITDA y Margen",
        xaxis_title="Mes",
        yaxis=dict(title="EBITDA (M€)"),
        yaxis2=dict(title="Margen (%)", overlaying="y", side="right")
    )
    st.plotly_chart(fig_eb, use_container_width=True)

    c1, c2, c3 = st.columns(3)
    with c1:
        kpi_card("Coste Salarial Deportivo (prom.)", currency(dfp["cost_sports_salaries"].mean(),0),
                 help_text="Incluye traspasos (demo)")
    with c2:
        kpi_card("OPEX (prom.)", currency(dfp["cost_opex"].mean(),0))
    with c3:
        kpi_card("Revenue/Matchday (prom.)", currency(dfp["rev_stadium"].mean(),0))

# -------------------------
# TAB 2: FAN & DIGITAL
# -------------------------
with tabs[1]:
    st.subheader("📣 Fan & Digital — CRM, engagement y monetización")

    c1, c2, c3, c4 = st.columns(4)
    with c1:
        kpi_card("Fans contactables CRM", f"{int(dfp['fans_contactables'].iloc[-1]):,}")
    with c2:
        kpi_card("Sessions Web", f"{int(dfp['site_sessions'].iloc[-1]):,}")
    with c3:
        kpi_card("Sessions App", f"{int(dfp['app_sessions'].iloc[-1]):,}")
    with c4:
        kpi_card("CR eCommerce", percentage(dfp["cr_venta_tienda_online"].iloc[-1]))

    fig_sessions = px.line(
        dfp, x="month", y=["site_sessions","app_sessions"],
        title="Tendencia de sesiones (Web/App)",
        labels={"value":"Sesiones","variable":"Canal","month":"Mes"}
    )
    st.plotly_chart(fig_sessions, use_container_width=True)

    # Funil eCommerce simplificado (demo)
    st.markdown("**Funnel eCommerce (demo)**")
    funnel = pd.DataFrame({
        "etapa": ["Visitas", "Vistas Producto", "Add-to-Cart", "Checkout", "Pagos Exitosos"],
        "valor": [100000, 42000, 19000, 9000, 5000]
    })
    fig_fun = px.funnel(funnel, x="valor", y="etapa", title="Embudo de conversión estimado")
    st.plotly_chart(fig_fun, use_container_width=True)

    st.markdown("**Ideas de acción (playbook):**")
    st.markdown("- Personalización con IA por segmento y país\n- Activación de cross-sell post-compra\n- Campañas a ‘fans silenciosos’ (no interactúan en 90 días)\n- A/B test en checkout y envíos a China/USA")

# -------------------------
# TAB 3: MERCHANDISING (BLM)
# -------------------------
with tabs[2]:
    st.subheader("🛍️ BLM — ventas por canal y ticket promedio")

    c1, c2, c3 = st.columns(3)
    with c1:
        kpi_card("Ventas Flagship (último mes)", currency(dfp["ventas_tienda_flagship"].iloc[-1],0))
    with c2:
        kpi_card("Ventas Lic. Terceros (último mes)", currency(dfp["ventas_tiendas_lic"].iloc[-1],0))
    with c3:
        kpi_card("Ventas eCommerce (último mes)", currency(dfp["ventas_ecommerce"].iloc[-1],0))

    df_blm = dfp[["month","ventas_tienda_flagship","ventas_tiendas_lic","ventas_ecommerce"]].melt(
        id_vars="month", var_name="canal", value_name="ventas"
    )
    fig_blm = px.line(df_blm, x="month", y="ventas", color="canal", title="Ventas por canal")
    fig_blm.update_yaxes(tickprefix="€")
    st.plotly_chart(fig_blm, use_container_width=True)

    st.markdown("**Acciones sugeridas:**")
    st.markdown("- Omnicanalidad (Click&Collect, devoluciones cruzadas)\n- Integración inventario real-time\n- Lanzamientos ‘drop’ por país y calendario deportivo")

# -------------------------
# TAB 4: ESPAI BARÇA
# -------------------------
with tabs[3]:
    st.subheader("🏟️ Espai Barça — experiencia y monetización del estadio")

    c1, c2, c3, c4 = st.columns(4)
    with c1:
        kpi_card("Asistencia media", f"{int(dfp['asistencia_media'].mean()):,}")
    with c2:
        kpi_card("ARPU Estadio", currency(dfp["arpu_estadio"].mean(),2))
    with c3:
        kpi_card("Tiempo acceso medio", f"{dfp['tiempo_acceso_medio'].mean():.1f} min")
    with c4:
        kpi_card("Incidencias por partido", f"{dfp['incidencias_matchday'].mean():.1f}")

    fig_att = px.bar(dfp, x="month", y="asistencia_media", title="Asistencia media por partido")
    st.plotly_chart(fig_att, use_container_width=True)

    fig_access = px.line(dfp, x="month", y="tiempo_acceso_medio", title="Tiempo medio de acceso (min)")
    st.plotly_chart(fig_access, use_container_width=True)

    st.markdown("**Playbook matchday (demo):**")
    st.markdown("- Beacons/WiFi analytics para mapas de calor\n- Pre-orden móvil y ‘fast lanes’\n- Gamificación antes/durante/pospartido\n- AR guiada para orientación en el estadio")

# -------------------------
# TAB 5: DEPORTE & RENDIMIENTO
# -------------------------
with tabs[4]:
    st.subheader("⚽ Rendimiento deportivo — de la intuición al dato")

    c1, c2, c3 = st.columns(3)
    with c1:
        kpi_card("Índice Carga Física", f"{dfp['carga_fisica_idx'].mean():.1f}/100")
    with c2:
        kpi_card("Distancia total equipo", f"{dfp['dist_media_equipo_km'].mean():.1f} km")
    with c3:
        kpi_card("Lesiones/mes", f"{dfp['lesiones_mes'].mean():.1f}")

    fig_load = px.line(dfp, x="month", y=["carga_fisica_idx","dist_media_equipo_km"],
                       title="Carga física y distancia recorrida")
    st.plotly_chart(fig_load, use_container_width=True)

    fig_inj = px.area(dfp, x="month", y="lesiones_mes", title="Lesiones por mes (incidencia)")
    st.plotly_chart(fig_inj, use_container_width=True)

    st.markdown("**Ideas de acción:**")
    st.markdown("- Umbrales personalizados por jugador (zonas rojas)\n- Días de recuperación guiados por datos\n- Feedback staff-datos post-entreno en tiempo real")

# -------------------------
# TAB 6: DATOS & GOBIERNO
# -------------------------
with tabs[5]:
    st.subheader("🔒 Gobierno de Datos — calidad, disponibilidad y uso")

    c1, c2, c3 = st.columns(3)
    with c1:
        kpi_card("Freshness (días)", f"{dfp['data_freshness_days'].mean():.1f}",
                 delta=f"Objetivo ≤ {objetivo_freshness}d")
    with c2:
        kpi_card("% automatización", percentage(dfp["porc_fuentes_automatizadas"].iloc[-1]))
    with c3:
        kpi_card("# dashboards activos (demo)", f"{random.randint(90, 130)}",
                 help_text="Ejecutivo + Comité + Áreas")

    st.markdown("**Checklist de gobierno (demo):**")
    colA, colB = st.columns(2)
    with colA:
        traffic_light("ok", "Catálogo de datos corporativo unificado")
        traffic_light("ok", "Diccionario de KPIs (definiciones únicas)")
        traffic_light("warn", "SLA de ingestión mensual (meta 10 días)")
        traffic_light("warn", "Trazabilidad extremo-a-extremo (parcial)")
    with colB:
        traffic_light("ok", "Accesos RBAC y cumplimiento GDPR")
        traffic_light("ok", "Plataformas transversales (evitar silos)")
        traffic_light("warn", "Alertas de calidad (outliers, nulos)")
        traffic_light("ok", "Comité de datos interáreas")

    # Trend de freshness
    fig_fr = px.line(dfp, x="month", y="data_freshness_days", title="Tendencia Freshness (días)")
    st.plotly_chart(fig_fr, use_container_width=True)

    st.info(
        "Próximos pasos sugeridos: 1) orquestación ETL/ELT con SLA ≤ 10 días; "
        "2) validaciones de calidad automáticas; 3) métricas de uso de dashboards por área; "
        "4) ownership claro por dominio (Deporte, BLM, Estadio, Digital, Finanzas)."
    )

# -------------------------
# TAB 7: WHAT-IF SIMULATOR
# -------------------------
with tabs[6]:
    st.subheader("🧪 What-If Simulator — del tablero a la decisión")

    st.markdown("Ajusta palancas y observa el impacto estimado en margen y EBITDA (modelo simplificado).")

    c1, c2, c3 = st.columns(3)
    with c1:
        delta_cr = st.slider("Δ CR eCommerce (puntos porcentuales)", -1.0, 2.5, 0.5, 0.1)
    with c2:
        delta_arpu = st.slider("Δ ARPU Estadio (€)", -5.0, 10.0, 2.0, 0.5)
    with c3:
        delta_salary = st.slider("Δ Salarial Deportivo (%)", -10.0, 10.0, -2.0, 0.5)

    # Copia de trabajo
    sim = dfp.copy()

    # Impacto eCommerce: recalcula ventas_ecommerce con CR ajustado
    base_cr = sim["cr_venta_tienda_online"].iloc[-1]
    sim_cr = (sim["cr_venta_tienda_online"] + delta_cr).clip(0.5, 5.0)
    sim["ventas_ecommerce_sim"] = (sim["site_sessions"] * (sim_cr/100) * sim["aov_ecommerce"])

    # Impacto ARPU estadio
    sim["arpu_estadio_sim"] = (sim["arpu_estadio"] + delta_arpu).clip(0, None)
    # Asumimos 2 partidos/mes en promedio (demo)
    partidos_mes = 2
    rev_stadium_delta = (sim["arpu_estadio_sim"] - sim["arpu_estadio"]) * sim["asistencia_media"] * partidos_mes
    sim["rev_stadium_sim"] = sim["rev_stadium"] + rev_stadium_delta

    # Impacto salarios deportivos
    sim["cost_sports_salaries_sim"] = sim["cost_sports_salaries"] * (1 + (delta_salary/100.0))

    # Reconstrucción de revenue total sim
    sim["revenue_total_sim"] = (
        sim["rev_marketing_media"] + sim["rev_members"] + sim["rev_other"] +
        sim["ventas_tienda_flagship"] + sim["ventas_tiendas_lic"] + sim["ventas_ecommerce_sim"] +
        (sim["rev_stadium_sim"] - sim["rev_stadium"])  # sustituimos el estadio
    )

    # Reconstrucción de costos y EBITDA sim
    sim["cost_total_sim"] = sim["cost_non_sports"] + sim["cost_opex"] + sim["cost_sports_salaries_sim"]
    sim["ebitda_sim"] = sim["revenue_total_sim"] - sim["cost_total_sim"]
    sim["margin_sim"] = (sim["ebitda_sim"] / sim["revenue_total_sim"]) * 100

    c1, c2, c3 = st.columns(3)
    with c1:
        kpi_card("EBITDA actual (12m)", currency(dfp["ebitda"].tail(12).sum()/1e6,0)+"M")
    with c2:
        kpi_card("EBITDA sim (12m)", currency(sim["ebitda_sim"].tail(12).sum()/1e6,0)+"M")
    with c3:
        diff = sim["ebitda_sim"].tail(12).sum() - dfp["ebitda"].tail(12).sum()
        color = "🟢" if diff >= 0 else "🔴"
        kpi_card("Δ EBITDA (12m)", f"{color} {currency(diff/1e6,0)}M")

    fig_sim = go.Figure()
    fig_sim.add_trace(go.Scatter(x=dfp["month"], y=dfp["margin"], mode="lines+markers", name="Margen actual"))
    fig_sim.add_trace(go.Scatter(x=sim["month"], y=sim["margin_sim"], mode="lines+markers", name="Margen sim"))
    fig_sim.update_layout(title="Margen: actual vs escenario simulado", xaxis_title="Mes", yaxis_title="Margen (%)")
    st.plotly_chart(fig_sim, use_container_width=True)

    st.markdown("**Interpretación ejecutiva (demo):**")
    st.markdown(
        f"- +{delta_cr:.1f} pp en CR eCommerce y +€{delta_arpu:.1f} en ARPU matchday incrementan EBITDA "
        f"≈ {currency(diff/1e6,0)}M (12m), aun con variación salarial de {delta_salary:.1f}%.\n"
        "- Recomendación: priorizar palancas de alto ROI medible (eCommerce y ARPU), acompañadas de disciplina salarial."
    )

# =========================
# FOOTER
# =========================
st.markdown("---")
st.caption("© Demo educativo — Inspirado en el caso FC Barcelona (IESE). Este tablero utiliza datos sintéticos con fines formativos.")
