import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
from datetime import datetime

# =============================
# FASE 0: Configuración y Datos Dummy
# =============================

st.set_page_config(page_title="Dashboard de Mercado Inmobiliario", layout="wide")

@st.cache_data(show_spinner=False)
def generar_datos_dummy(n_proyectos: int = 80, seed: int = 42):
    np.random.seed(seed)

    # --- Catálogos ---
    tipos = ["Vertical", "Horizontal", "Lote"]
    zonas = ["Zona Norte", "Zona Sur", "Zona Centro", "Zona Oeste"]
    desarrolladoras = ["MiDesarrollo", "HabitaPlus", "Urbania", "Armonia"]
    amenity_pool = [
        "Alberca", "Gimnasio", "Pet Park", "Seguridad 24/7", "Casa Club",
        "Roof Garden", "Área de Juegos", "Coworking", "Cancha Multiusos"
    ]

    # --- Proyectos ---
    ids = np.arange(1, n_proyectos + 1)
    nombres_base = [
        "Torre Caelus", "Residencial Arboleda", "Parque Logístico Norte", "Lomas del Mar",
        "Altavista", "Bahía Cerritos", "Balandra", "Galerna", "Guayacanes",
        "Lacus", "Los Osuna", "Malta Green", "Ocean Hills", "Olympus City",
        "Sonterra", "Altazia", "Maralto", "Cumbres del Pacífico", "Vista Dorada",
        "Porto Viejo", "Costa Nova", "Aria Residences", "Miralta", "Aurora Living",
        "Nova Park", "Punta Palma", "Natura", "Vértice", "Arenas del Sol", "Palmares"
    ]
    nombres = np.random.choice(nombres_base, size=n_proyectos, replace=True)

    tipos_asignados = np.random.choice(tipos, size=n_proyectos, p=[0.45, 0.35, 0.20])
    zonas_asignadas = np.random.choice(zonas, size=n_proyectos)
    estatus_asignados = np.random.choice(["Activo", "Vendido"], size=n_proyectos, p=[0.75, 0.25])

    total_unidades = np.random.randint(20, 201, size=n_proyectos)
    inventario_disponible = np.maximum(0, total_unidades - np.random.randint(0, total_unidades))

    precio_promedio = np.random.randint(3_000_000, 15_000_001, size=n_proyectos)

    m2_construccion = np.where(
        tipos_asignados == "Lote",
        np.nan,
        np.random.randint(80, 251, size=n_proyectos).astype(float)
    )
    m2_terreno = np.random.randint(100, 301, size=n_proyectos).astype(float)

    desarrolladora_asignada = np.random.choice(desarrolladoras, size=n_proyectos, p=[0.25, 0.25, 0.25, 0.25])

    # Amenidades como lista legible
    def sample_amenities():
        k = np.random.randint(2, 6)
        return ", ".join(sorted(np.random.choice(amenity_pool, size=k, replace=False)))

    amenidades = [sample_amenities() for _ in range(n_proyectos)]

    # Coordenadas dummy alrededor de Mazatlán (aprox 23.232, -106.416)
    lat_center, lon_center = 23.232, -106.416
    latitudes = lat_center + np.random.normal(0, 0.05, size=n_proyectos)
    longitudes = lon_center + np.random.normal(0, 0.05, size=n_proyectos)

    df_proyectos = pd.DataFrame({
        "ID_Proyecto": ids,
        "Nombre_Proyecto": nombres + " " + pd.Series(ids).astype(str),
        "Tipo": tipos_asignados,
        "Zona": zonas_asignadas,
        "Estatus": estatus_asignados,
        "Total_Unidades": total_unidades,
        "Inventario_Disponible": inventario_disponible,
        "Precio_Promedio": precio_promedio,
        "M2_Construccion_Prom": m2_construccion,
        "M2_Terreno_Prom": m2_terreno,
        "Desarrolladora": desarrolladora_asignada,
        "Amenidades": amenidades,
        "Lat": latitudes,
        "Lon": longitudes,
    })

    # --- Ventas históricas 24 meses ---
    end = pd.to_datetime(datetime.today().strftime("%Y-%m-01"))
    start = end - pd.DateOffset(months=23)
    fechas = pd.date_range(start, end, freq="MS")

    ventas_rows = []
    for _id in df_proyectos["ID_Proyecto"]:
        # tendencia ligera alcista por proyecto
        trend = np.linspace(0, np.random.uniform(0.1, 0.5), len(fechas))
        base = np.random.randint(0, 6, size=len(fechas))  # 0-5 ventas/mes
        ventas_mes = np.clip(np.round(base + trend).astype(int), 0, 15)

        # precio promedio mensual con pequeña tendencia
        precio_base = np.random.randint(2_800_000, 14_000_000)
        drift = np.linspace(0, np.random.randint(50_000, 250_000), len(fechas))
        ruido = np.random.normal(0, 40_000, size=len(fechas))
        precio_prom_mes = np.maximum(1_500_000, precio_base + drift + ruido)

        for f, v, p in zip(fechas, ventas_mes, precio_prom_mes):
            ventas_rows.append({
                "ID_Proyecto": _id,
                "Fecha": f,
                "Ventas_Mes": int(v),
                "Precio_Promedio_Mes": float(p),
            })

    df_ventas_historico = pd.DataFrame(ventas_rows)
    return df_proyectos, df_ventas_historico


df_proyectos, df_ventas_historico = generar_datos_dummy()

# ==== Campos extra para analítica ====
# Fecha de inicio de ventas simulada por proyecto (entre 6 y 30 meses atrás)
now_ms = pd.to_datetime(datetime.today().strftime("%Y-%m-01"))
meses_atras = np.random.randint(6, 31, size=len(df_proyectos))
df_proyectos["Fecha_Inicio_Ventas"] = [now_ms - pd.DateOffset(months=int(m)) for m in meses_atras]

# Dataset simulado de clientes y leads
@st.cache_data(show_spinner=False)
def generar_clientes_leads_dummy(df_proj: pd.DataFrame, n: int = 800, seed: int = 123):
    np.random.seed(seed)
    proyectos = df_proj["Nombre_Proyecto"].values
    zonas_origen = ["Local", "Regional", "Nacional"]
    estados_civ = ["Casado", "Soltero", "Unión Libre"]
    asesores = ["Ana Pérez", "Luis Gómez", "Carlos Ruiz", "María Díaz", "Sofía León"]
    medios = ["Redes Sociales", "Referido", "Lona", "Web", "WhatsApp"]
    motivos = ["Precio Alto", "Ubicación", "Falta de Crédito", "Mejor Opción"]

    tipo_estado = np.random.choice(["Comprador", "No Comprador"], size=n)
    nombre_proy = np.random.choice(proyectos, size=n)

    df = pd.DataFrame({
        "ID_Cliente": np.arange(1, n+1),
        "Nombre_Proyecto": nombre_proy,
        "Tipo_Estado": tipo_estado,
        "Zona_Origen": np.random.choice(zonas_origen, size=n, p=[0.5, 0.3, 0.2]),
        "Edad": np.random.randint(25, 61, size=n),
        "Estado_Civil": np.random.choice(estados_civ, size=n),
        "Num_Hijos": np.random.randint(0, 5, size=n),
        "Meses_Primer_Contacto": np.random.randint(1, 13, size=n),
        "Citas_Agendadas": np.random.randint(0, 6, size=n),
        "Asesor": np.random.choice(asesores, size=n),
        "Enganche_Porcentaje": np.where(tipo_estado=="Comprador", np.random.randint(10, 31, size=n), np.nan),
        "Medio_Contacto": np.random.choice(medios, size=n),
        "Motivo_No_Compra": np.where(tipo_estado=="No Comprador", np.random.choice(motivos, size=n), None),
    })
    return df

df_clientes_leads = generar_clientes_leads_dummy(df_proyectos)

# ================ Utilidades KPI =================

# Catálogo de estados de México (sin Sinaloa)
MEX_ESTADOS_SIN_SINALOA = [
    "Aguascalientes", "Baja California", "Baja California Sur", "Campeche", "Chiapas",
    "Chihuahua", "Ciudad de México", "Coahuila", "Colima", "Durango", "Guanajuato",
    "Guerrero", "Hidalgo", "Jalisco", "Estado de México", "Michoacán", "Morelos",
    "Nayarit", "Nuevo León", "Oaxaca", "Puebla", "Querétaro", "Quintana Roo",
    "San Luis Potosí", "Sonora", "Tabasco", "Tamaulipas", "Tlaxcala", "Veracruz",
    "Yucatán", "Zacatecas"
]


def filtrar_por_tipo(df: pd.DataFrame, tipo: str | None):
    if tipo is None or tipo == "Mercado Total":
        return df.copy()
    return df[df["Tipo"] == tipo].copy()


def kpi_delta(fake_range=("-2.5%", "+3.7%", "+1.2%", "-0.8%", "+2.1%")):
    # Simula un delta tipo texto y color
    delta = np.random.choice(fake_range)
    color = "normal" if not delta.startswith("-") else "inverse"
    return delta, color


def calcular_kpis(df_filtrado: pd.DataFrame, df_hist: pd.DataFrame):
    activos = (df_filtrado["Estatus"] == "Activo").sum()
    inventario = df_filtrado["Inventario_Disponible"].sum()

    ids_filtrados = df_filtrado["ID_Proyecto"].unique().tolist()
    hist_f = df_hist[df_hist["ID_Proyecto"].isin(ids_filtrados)]
    ventas_totales = hist_f["Ventas_Mes"].sum()

    ventas_prom_por_proyecto = ventas_totales / activos if activos > 0 else 0

    # Absorción: promedio últimos 3 meses
    if not hist_f.empty:
        ult_3m = hist_f.sort_values("Fecha").groupby("ID_Proyecto").tail(3)
        prom_3m = ult_3m.groupby("Fecha")["Ventas_Mes"].sum().mean()
    else:
        prom_3m = 0

    meses_agotar = inventario / prom_3m if prom_3m and prom_3m > 0 else np.nan

    precio_prom_final = df_filtrado["Precio_Promedio"].mean()

    # Precio m2 (maneja NaN para Lote en construccion)
    precio_m2_const = (df_filtrado["Precio_Promedio"] / df_filtrado["M2_Construccion_Prom"]).replace([np.inf, -np.inf], np.nan).mean()
    precio_m2_terr = (df_filtrado["Precio_Promedio"] / df_filtrado["M2_Terreno_Prom"]).replace([np.inf, -np.inf], np.nan).mean()

    return {
        "activos": int(activos),
        "inventario": int(inventario),
        "ventas_totales": int(ventas_totales),
        "ventas_prom_por_proyecto": float(ventas_prom_por_proyecto),
        "meses_agotar": float(meses_agotar) if pd.notna(meses_agotar) else None,
        "precio_prom_final": float(precio_prom_final) if pd.notna(precio_prom_final) else None,
        "precio_m2_const": float(precio_m2_const) if pd.notna(precio_m2_const) else None,
        "precio_m2_terr": float(precio_m2_terr) if pd.notna(precio_m2_terr) else None,
    }


# =============================
# Sidebar: Navegación
# =============================
st.sidebar.title("Navegación")
pagina = st.sidebar.radio(
    "Selecciona una sección:",
    ["Resumen de Mercado", "Análisis por Zona", "Benchmark de Proyectos", "Analítica"],
)

# =============================
# FASE 1: Resumen General y Segmentación
# =============================
if pagina == "Resumen de Mercado":
    st.title("Dashboard de Mercado Inmobiliario")

    filtro = st.radio(
        "Segmento:",
        ["Mercado Total", "Vertical", "Horizontal", "Lote"],
        horizontal=True,
        index=0,
    )

    df_filtrado = filtrar_por_tipo(df_proyectos, filtro)

    # KPIs
    kpis = calcular_kpis(df_filtrado, df_ventas_historico)
    col1, col2, col3, col4 = st.columns(4)
    col5, col6, col7, col8 = st.columns(4)

    d1, c1 = kpi_delta(); d2, c2 = kpi_delta(); d3, c3 = kpi_delta(); d4, c4 = kpi_delta()
    d5, c5 = kpi_delta(); d6, c6 = kpi_delta(); d7, c7 = kpi_delta(); d8, c8 = kpi_delta()

    col1.metric("Proyectos Activos", f"{kpis['activos']:,}", delta=d1, delta_color=c1)
    col2.metric("Inventario Disponible", f"{kpis['inventario']:,}", delta=d2, delta_color=c2)
    col3.metric("Ventas Totales (Hist)", f"{kpis['ventas_totales']:,}", delta=d3, delta_color=c3)
    col4.metric("Ventas Prom / Proyecto", f"{kpis['ventas_prom_por_proyecto']:.2f}", delta=d4, delta_color=c4)

    col5.metric(
        "Meses para agotar Inventario",
        f"{kpis['meses_agotar']:.1f}" if kpis["meses_agotar"] is not None else "—",
        delta=d5, delta_color=c5,
    )
    col6.metric(
        "Precio Promedio Final",
        f"${kpis['precio_prom_final']:,.0f}" if kpis["precio_prom_final"] is not None else "—",
        delta=d6, delta_color=c6,
    )
    col7.metric(
        "Precio Prom m² Construcción",
        f"${kpis['precio_m2_const']:,.0f}" if kpis["precio_m2_const"] is not None else "—",
        delta=d7, delta_color=c7,
    )
    col8.metric(
        "Precio Prom m² Terreno",
        f"${kpis['precio_m2_terr']:,.0f}" if kpis["precio_m2_terr"] is not None else "—",
        delta=d8, delta_color=c8,
    )

    st.divider()

    # Visualizaciones: mapa + tendencia
    c1, c2 = st.columns(2)
    with c1:
        st.subheader("Ubicación de Proyectos Activos")
        coords = df_filtrado[df_filtrado["Estatus"] == "Activo"][ ["Lat", "Lon"] ].rename(columns={"Lat": "lat", "Lon": "lon"})
        st.map(coords)

    with c2:
        st.subheader("Tendencia de Ventas vs Inventario")
        ids_f = df_filtrado["ID_Proyecto"].unique().tolist()
        hist_f = df_ventas_historico[df_ventas_historico["ID_Proyecto"].isin(ids_f)].copy()

        if not hist_f.empty:
            ventas_mensuales = (
                hist_f.groupby("Fecha")["Ventas_Mes"].sum().reset_index(name="Ventas_Totales")
            )
            inventario_inicial = int(df_filtrado["Inventario_Disponible"].sum())
            ventas_mensuales["Inventario_Restante"] = (
                inventario_inicial - ventas_mensuales["Ventas_Totales"].cumsum()
            ).clip(lower=0)

            df_plot = ventas_mensuales.melt(id_vars="Fecha", value_vars=["Ventas_Totales", "Inventario_Restante"],
                                            var_name="Métrica", value_name="Valor")
            fig = px.line(df_plot, x="Fecha", y="Valor", color="Métrica")
            fig.update_layout(margin=dict(l=0, r=0, t=10, b=0))
            st.plotly_chart(fig, use_container_width=True)
        else:
            st.info("No hay histórico para el filtro seleccionado.")

# =============================
# FASE 2: Análisis por Zona
# =============================
elif pagina == "Análisis por Zona":
    st.header("Análisis por Zona Geográfica")

    zona_seleccionada = st.selectbox(
        "Selecciona una Zona:", options=sorted(df_proyectos["Zona"].unique())
    )

    df_zona = df_proyectos[df_proyectos["Zona"] == zona_seleccionada].copy()
    tab_kpis, tab_vertical, tab_horizontal, tab_lote = st.tabs([
        "Resumen Zona", "Vertical", "Horizontal", "Lote"
    ])

    with tab_kpis:
        st.subheader(f"KPIs de {zona_seleccionada}")
        k = calcular_kpis(df_zona, df_ventas_historico)
        a, b, c, d = st.columns(4)
        e, f, g, h = st.columns(4)
        d1, c1 = kpi_delta(); d2, c2 = kpi_delta(); d3, c3 = kpi_delta(); d4, c4 = kpi_delta()
        d5, c5 = kpi_delta(); d6, c6 = kpi_delta(); d7, c7 = kpi_delta(); d8, c8 = kpi_delta()

        a.metric("Proyectos Activos", f"{k['activos']:,}", delta=d1, delta_color=c1)
        b.metric("Inventario Disponible", f"{k['inventario']:,}", delta=d2, delta_color=c2)
        c.metric("Ventas Totales (Hist)", f"{k['ventas_totales']:,}", delta=d3, delta_color=c3)
        d.metric("Ventas Prom / Proyecto", f"{k['ventas_prom_por_proyecto']:.2f}", delta=d4, delta_color=c4)

        e.metric("Meses para agotar Inventario", f"{k['meses_agotar']:.1f}" if k["meses_agotar"] else "—", delta=d5, delta_color=c5)
        f.metric("Precio Promedio Final", f"${k['precio_prom_final']:,.0f}" if k["precio_prom_final"] else "—", delta=d6, delta_color=c6)
        g.metric("Precio Prom m² Construcción", f"${k['precio_m2_const']:,.0f}" if k["precio_m2_const"] else "—", delta=d7, delta_color=c7)
        h.metric("Precio Prom m² Terreno", f"${k['precio_m2_terr']:,.0f}" if k["precio_m2_terr"] else "—", delta=d8, delta_color=c8)

    def tabla_tipo(df, tipo):
        df_t = df[df["Tipo"] == tipo].copy()
        if df_t.empty:
            st.info(f"No hay proyectos {tipo.lower()} en {zona_seleccionada}.")
            return
        st.dataframe(
            df_t[[
                "Nombre_Proyecto", "Tipo", "Estatus", "Inventario_Disponible",
                "Precio_Promedio", "M2_Construccion_Prom", "M2_Terreno_Prom", "Desarrolladora"
            ]].sort_values("Inventario_Disponible", ascending=False),
            use_container_width=True,
        )
        inv = int(df_t["Inventario_Disponible"].sum())
        st.metric(f"Inventario {tipo} en Zona", f"{inv:,}")

    with tab_vertical:
        st.subheader(f"Proyectos Verticales en {zona_seleccionada}")
        tabla_tipo(df_zona, "Vertical")

    with tab_horizontal:
        st.subheader(f"Proyectos Horizontales en {zona_seleccionada}")
        tabla_tipo(df_zona, "Horizontal")

    with tab_lote:
        st.subheader(f"Proyectos de Lote en {zona_seleccionada}")
        tabla_tipo(df_zona, "Lote")

# =============================
# FASE 3: Benchmark de Proyectos
# =============================
elif pagina == "Benchmark de Proyectos":
    st.header("Herramienta de Benchmark Competitivo")
    st.info("Selecciona tu proyecto y luego los competidores con los que deseas compararlo.")

    col1, col2 = st.columns([1, 2])

    with col1:
        opciones_mi = df_proyectos[df_proyectos["Desarrolladora"] == "MiDesarrollo"]["Nombre_Proyecto"].unique()
        if len(opciones_mi) == 0:
            st.warning("No hay proyectos de 'MiDesarrollo' en los datos simulados.")
            st.stop()
        mi_proyecto = st.selectbox("1. Elige tu Proyecto:", options=sorted(opciones_mi))

    with col2:
        opciones_comp = df_proyectos[df_proyectos["Desarrolladora"] != "MiDesarrollo"]["Nombre_Proyecto"].unique()
        competidores = st.multiselect("2. Elige Competidores:", options=sorted(opciones_comp))

    lista_comparacion = [mi_proyecto] + competidores if mi_proyecto else competidores

    if len(lista_comparacion) == 0:
        st.stop()

    df_comparacion = df_proyectos[df_proyectos["Nombre_Proyecto"].isin(lista_comparacion)].copy()
    ids_comp = df_comparacion["ID_Proyecto"].unique().tolist()
    df_ventas_comparacion = df_ventas_historico[df_ventas_historico["ID_Proyecto"].isin(ids_comp)].copy()

    # --- Gráfico de barras: ritmo de ventas (promedio mensual por proyecto) ---
    ritmo = (
        df_ventas_comparacion.groupby("ID_Proyecto")["Ventas_Mes"].mean()
        .reset_index(name="Ventas_Prom_Mes")
        .merge(df_comparacion[["ID_Proyecto", "Nombre_Proyecto"]], on="ID_Proyecto", how="left")
    )

    st.subheader("Comparativa de Métricas Principales")
    if not ritmo.empty:
        fig_bar = px.bar(ritmo.sort_values("Ventas_Prom_Mes", ascending=False),
                         x="Nombre_Proyecto", y="Ventas_Prom_Mes",
                         labels={"Nombre_Proyecto": "Proyecto", "Ventas_Prom_Mes": "Ventas Prom/mes"})
        fig_bar.update_layout(margin=dict(l=0, r=0, t=10, b=0))
        st.plotly_chart(fig_bar, use_container_width=True)
    else:
        st.info("Sin histórico para los proyectos seleccionados.")

    # --- Tabla de métricas clave ---
    st.subheader("Ficha comparativa")
    cols_show = [
        "Nombre_Proyecto", "Tipo", "Inventario_Disponible", "Precio_Promedio",
        "M2_Construccion_Prom", "M2_Terreno_Prom", "Desarrolladora"
    ]
    st.dataframe(df_comparacion[cols_show].sort_values("Inventario_Disponible", ascending=False), use_container_width=True)

    # --- Detalle por proyecto (expanders) ---
    st.subheader("Análisis Detallado (Amenidades y Más)")
    for proyecto_nombre in lista_comparacion:
        datos = df_comparacion[df_comparacion["Nombre_Proyecto"] == proyecto_nombre]
        if datos.empty:
            continue
        row = datos.iloc[0]
        with st.expander(f"Ver detalle de: {proyecto_nombre}"):
            st.write(f"**Desarrolladora:** {row['Desarrolladora']}")
            st.write(f"**Amenidades:** {row['Amenidades']}")
            st.write(f"**Unidades Totales:** {int(row['Total_Unidades'])}")
            # Cálculo seguro de precios m²
            precio_m2_const = np.nan
            if pd.notna(row["M2_Construccion_Prom"]) and row["M2_Construccion_Prom"] > 0:
                precio_m2_const = row["Precio_Promedio"]/row["M2_Construccion_Prom"]
            precio_m2_terr = row["Precio_Promedio"]/row["M2_Terreno_Prom"] if row["M2_Terreno_Prom"] > 0 else np.nan

            st.write(f"**Precio m² Const:** {precio_m2_const:,.2f}" if pd.notna(precio_m2_const) else "**Precio m² Const:** —")
            st.write(f"**Precio m² Terreno:** {precio_m2_terr:,.2f}" if pd.notna(precio_m2_terr) else "**Precio m² Terreno:** —")

# =============================
# NUEVA SECCIÓN: Analítica
# =============================
elif pagina == "Analítica":
    st.header("Analítica de Proyecto (Geo + Históricos + Clientes)")

    # Selector de proyecto base
    proy_sel = st.selectbox(
        "Selecciona un proyecto:", options=sorted(df_proyectos["Nombre_Proyecto"].unique())
    )
    row = df_proyectos[df_proyectos["Nombre_Proyecto"] == proy_sel].iloc[0]
    pid = int(row["ID_Proyecto"])
    hist = df_ventas_historico[df_ventas_historico["ID_Proyecto"] == pid].sort_values("Fecha").copy()

    tab_info, tab_hist, tab_clientes, tab_forecast = st.tabs([
        "Información Completa", "Historial de Ventas", "Análisis de Clientes", "Estrategia y Forecast"
    ])

    # ---------- 6.1 Visualización Geográfica y Entorno ----------
    with tab_info:
        st.subheader("Ubicación y Entorno")
        lat0, lon0 = float(row["Lat"]), float(row["Lon"])
        # Puntos de interés cercanos (simulados a ~1-3 km)
        rng = np.random.default_rng(2025)
        poi = pd.DataFrame({
            "Nombre": ["Escuela A", "Centro Comercial B", "Hospital C"],
            "lat": lat0 + rng.normal(0, 0.015, 3),
            "lon": lon0 + rng.normal(0, 0.015, 3),
            "Tipo": ["Escuela", "Comercial", "Hospital"],
        })
        proyecto_df = pd.DataFrame({"Nombre": [proy_sel], "lat": [lat0], "lon": [lon0], "Tipo": ["Proyecto"]})
        mapa_df = pd.concat([proyecto_df, poi], ignore_index=True)

        fig_map = px.scatter_mapbox(
            mapa_df, lat="lat", lon="lon", color="Tipo", hover_name="Nombre", zoom=12,
            height=420, size=np.where(mapa_df["Tipo"]=="Proyecto", 18, 10)
        )
        fig_map.update_layout(mapbox_style="open-street-map", margin=dict(l=0, r=0, t=0, b=0))
        st.plotly_chart(fig_map, use_container_width=True)

        st.markdown("**Cercano a:** Colegios, Centros Comerciales, Vías Rápidas, Hospitales")

        with st.expander("Ficha Técnica del Proyecto"):
            cols = st.columns(3)
            cols[0].metric("Tipo", row["Tipo"])
            cols[1].metric("Zona", row["Zona"])
            cols[2].metric("Estatus", row["Estatus"])
            st.write(f"**Desarrolladora:** {row['Desarrolladora']}")
            st.write(f"**Amenidades:** {row['Amenidades']}")
            st.write(f"**Unidades Totales:** {int(row['Total_Unidades'])}")
            pm2c = (row["Precio_Promedio"]/row["M2_Construccion_Prom"]) if pd.notna(row["M2_Construccion_Prom"]) else np.nan
            pm2t = (row["Precio_Promedio"]/row["M2_Terreno_Prom"]) if row["M2_Terreno_Prom"]>0 else np.nan
            st.write(f"**Precio Promedio:** ${row['Precio_Promedio']:,.0f}")
            st.write(f"**Precio m² Const:** {pm2c:,.2f}" if pd.notna(pm2c) else "**Precio m² Const:** —")
            st.write(f"**Precio m² Terreno:** {pm2t:,.2f}" if pd.notna(pm2t) else "**Precio m² Terreno:** —")

    # ---------- 6.2 KPIs Históricos y Crecimiento ----------
    with tab_hist:
        st.subheader("KPIs Históricos")
        ini = pd.to_datetime(row["Fecha_Inicio_Ventas"]).to_period("M").to_timestamp()
        meses_en_venta = max(1, (now_ms.year - ini.year) * 12 + (now_ms.month - ini.month))
        ventas_tot = int(hist["Ventas_Mes"].sum())
        prom_mensual = ventas_tot / meses_en_venta if meses_en_venta>0 else 0
        inv_actual = max(0, int(row["Inventario_Disponible"]) - ventas_tot)
        prom_ult_6 = hist.tail(6)["Ventas_Mes"].mean() if len(hist) >= 1 else 0
        meses_inv = (inv_actual / prom_ult_6) if prom_ult_6 and prom_ult_6>0 else np.nan

        c1,c2,c3,c4 = st.columns(4)
        c5,c6 = st.columns(2)
        c1.metric("Inicio de Ventas", ini.strftime("%Y-%m-01"))
        c2.metric("Meses en Venta", f"{meses_en_venta}")
        c3.metric("Ventas Totales", f"{ventas_tot:,}", delta="+15% vs año prev.")
        c4.metric("Ventas Mensuales Prom.", f"{prom_mensual:.2f}")
        c5.metric("Inventario Actual", f"{inv_actual:,}")
        c6.metric("Meses de Inventario", f"{meses_inv:.1f}" if pd.notna(meses_inv) else "—")

        if not hist.empty:
            fig_v = px.bar(hist, x="Fecha", y="Ventas_Mes", title="Ventas Mensuales")
            fig_v.update_layout(margin=dict(l=0,r=0,t=40,b=0))
            st.plotly_chart(fig_v, use_container_width=True)

            hist["Growth_%"] = hist["Ventas_Mes"].pct_change().replace([np.inf, -np.inf], np.nan)*100
            fig_g = px.line(hist, x="Fecha", y="Growth_%", title="Crecimiento % Mensual")
            fig_g.update_layout(margin=dict(l=0,r=0,t=40,b=0))
            st.plotly_chart(fig_g, use_container_width=True)
        else:
            st.info("Sin histórico para este proyecto.")

    # ---------- 7. Análisis de Clientes (compradores desde ventas) ----------
    with tab_clientes:
        st.subheader("Análisis de Clientes y Funnel de Campaña")

        # 1) Número de clientes = ventas totales
        ventas_tot = int(hist["Ventas_Mes"].sum())
        if ventas_tot == 0:
            st.info("Este proyecto no tiene ventas históricas para simular clientes.")
        else:
            # 1. Datos básicos de clientes (uno por venta)
            n = ventas_tot
            rng = np.random.default_rng(12345)

            edades = rng.integers(25, 66, size=n)
            origen = rng.choice(MEX_ESTADOS_SIN_SINALOA, size=n)
            estado_civil = rng.choice(["Casado", "Soltero", "Unión Libre"], size=n, p=[0.5,0.35,0.15])
            tiene_hijos = rng.choice(["Sí", "No"], size=n, p=[0.6, 0.4])
            num_hijos = np.where(tiene_hijos=="Sí", rng.integers(1,5,size=n), 0)

            # 2. Medio de contacto
            medios = ["TikTok", "Instagram", "Facebook", "Anuncios Publicitarios", "Eventos"]
            medio_contacto = rng.choice(medios, size=n, p=[0.25,0.2,0.25,0.2,0.1])

            # 3. Compra (m² y precio por m² en miles)
            m2 = rng.integers(50, 151, size=n)
            rangos = rng.choice(["100-110","121-130","131-140","141-150",">=150"], size=n, p=[0.25,0.25,0.2,0.2,0.1])
            def precio_m2_por_rango(r):
                if r=="100-110": return rng.integers(100,111)
                if r=="121-130": return rng.integers(121,131)
                if r=="131-140": return rng.integers(131,141)
                if r=="141-150": return rng.integers(141,151)
                return rng.integers(150,171)  # >=150
            precio_m2_miles = np.array([precio_m2_por_rango(r) for r in rangos])
            precio_final = m2 * (precio_m2_miles*1000)

            # 4. Asesores + interacciones
            asesores = np.array(["Asesor A","Asesor B","Asesor C","Asesor D"])
            asesor_asignado = rng.choice(asesores, size=n)
            llamadas_totales = rng.integers(2, 9, size=n)
            llamadas_primer_contacto = np.minimum(llamadas_totales, rng.integers(1, 4, size=n))
            llamadas_para_cita = np.minimum(llamadas_totales-llamadas_primer_contacto, rng.integers(1, 4, size=n))
            citas_antes_apartar = rng.integers(1, 4, size=n)

            # Fechas para calcular tiempo de cierre
            fechas_contacto = pd.to_datetime(rng.choice(hist["Fecha"].values, size=n))
            dias_a_cita = rng.integers(1, 21, size=n)
            dias_a_apartar = dias_a_cita + rng.integers(7, 61, size=n)
            fecha_cita = fechas_contacto + pd.to_timedelta(dias_a_cita, unit="D")
            fecha_apartado = fechas_contacto + pd.to_timedelta(dias_a_apartar, unit="D")
            tiempo_cierre_dias = (fecha_apartado - fechas_contacto).days

            df_clientes = pd.DataFrame({
                "ID_Cliente": np.arange(1, n+1),
                "Proyecto": proy_sel,
                "Edad": edades,
                "Genero": rng.choice(["Hombre","Mujer"], size=n, p=[0.55,0.45]),
                "Origen": origen,
                "Estado_Civil": estado_civil,
                "Tiene_Hijos": tiene_hijos,
                "Num_Hijos": num_hijos,
                "Medio_Contacto": medio_contacto,
                "m2": m2,
                "Rango_Precio_m2_k": rangos,
                "Precio_m2_k": precio_m2_miles,
                "Precio_Final": precio_final,
                "Asesor": asesor_asignado,
                "Llamadas_Totales": llamadas_totales,
                "Llamadas_Primer_Contacto": llamadas_primer_contacto,
                "Llamadas_Para_Cita": llamadas_para_cita,
                "Citas_Antes_Apartar": citas_antes_apartar,
                "Fecha_Primer_Contacto": fechas_contacto,
                "Fecha_Primera_Cita": fecha_cita,
                "Fecha_Apartado": fecha_apartado,
                "Tiempo_Cierre_Dias": tiempo_cierre_dias,
            })

            # Indicadores Clave al inicio
            st.subheader("Indicadores Clave")

            # --- Construimos embudo primero para usar en KPIs (leads y ventas por canal) ---
            medios = ["TikTok", "Instagram", "Facebook", "Anuncios Publicitarios", "Eventos"]
            factor_leads = rng.integers(5, 12)
            distrib = rng.random(len(medios)); distrib /= distrib.sum()
            leads_por_medio = (distrib * (ventas_tot * factor_leads)).round().astype(int)
            contact_rate = rng.uniform(0.45, 0.7, size=len(medios))
            appt_rate = rng.uniform(0.35, 0.6, size=len(medios))
            close_rate = rng.uniform(0.18, 0.35, size=len(medios))
            df_funnel = pd.DataFrame({"Medio": medios, "Leads": leads_por_medio})
            df_funnel["Contactados"] = np.round(df_funnel["Leads"] * contact_rate).astype(int)
            df_funnel["Citados"] = np.minimum(df_funnel["Contactados"], np.round(df_funnel["Contactados"] * appt_rate).astype(int))
            df_funnel["Ventas_Base"] = np.minimum(df_funnel["Citados"], np.round(df_funnel["Citados"] * close_rate).astype(int))
            scale = ventas_tot / max(1, df_funnel["Ventas_Base"].sum())
            df_funnel["Ventas"] = np.floor(df_funnel["Ventas_Base"] * scale).astype(int)
            diff = ventas_tot - df_funnel["Ventas"].sum()
            if diff > 0:
                idxs = df_funnel.sort_values("Ventas_Base", ascending=False).index[:diff]
                df_funnel.loc[idxs, "Ventas"] += 1

            # --- KPIs ---
            edad_prom = df_clientes["Edad"].mean()
            genero_counts = df_clientes["Genero"].value_counts(normalize=True)
            pct_h = float(genero_counts.get("Hombre", 0)*100)
            pct_m = float(genero_counts.get("Mujer", 0)*100)
            top3_estados = ", ".join(df_clientes["Origen"].value_counts().head(3).index.tolist())
            top3_leads = ", ".join(df_funnel.sort_values("Leads", ascending=False).head(3)["Medio"].tolist())
            top3_ventas = ", ".join(df_funnel.sort_values("Ventas", ascending=False).head(3)["Medio"].tolist())
            prom_llamadas_para_cita = df_clientes["Llamadas_Para_Cita"].mean()
            prom_citas_apartar = df_clientes["Citas_Antes_Apartar"].mean()

            k1,k2,k3 = st.columns(3)
            k1.metric("Edad Promedio", f"{edad_prom:.1f} años")
            k2.metric("Distribución Género", f"{pct_h:.0f}% H / {pct_m:.0f}% M")
            k3.metric("Promedio Llamadas para Cita", f"{prom_llamadas_para_cita:.2f}")

            k4,k5 = st.columns(2)
            k4.metric("Promedio Citas para Apartar", f"{prom_citas_apartar:.2f}")
            k5.metric("Clientes (ventas simuladas)", f"{n}")

            st.markdown(f"**Top 3 Estados (Origen):** {top3_estados}")
            st.markdown(f"**Top 3 Canales por Leads:** {top3_leads}")
            st.markdown(f"**Top 3 Canales por Ventas:** {top3_ventas}")

            # 5. Eficiencia de asesores
            st.subheader("Eficiencia de Asesores")
            ventas_por_asesor = df_clientes.groupby("Asesor")["ID_Cliente"].count().rename("Ventas").reset_index()
            # Aproxima clientes asignados = ventas + leads no cerrados estimados (20-60% adicional aleatorio por asesor)
            asignados_aprox = (ventas_por_asesor["Ventas"] * rng.uniform(1.2, 1.6, size=len(ventas_por_asesor))).round().astype(int)
            ventas_por_asesor["Clientes_Asignados_Aprox"] = asignados_aprox
            ventas_por_asesor["Eficiencia"] = ventas_por_asesor["Ventas"] / ventas_por_asesor["Clientes_Asignados_Aprox"]

            interacciones_prom = df_clientes.groupby("Asesor").agg(
                Llamadas_para_agendar_cita_prom=("Llamadas_Para_Cita","mean"),
                Citas_para_apartar_prom=("Citas_Antes_Apartar","mean"),
                Tiempo_cierre_prom_dias=("Tiempo_Cierre_Dias","mean")
            ).reset_index()
            tabla_asesores = ventas_por_asesor.merge(interacciones_prom, on="Asesor", how="left")
            st.dataframe(tabla_asesores, use_container_width=True)
            st.plotly_chart(px.bar(ventas_por_asesor, x="Asesor", y="Ventas", title="Ventas por Asesor"), use_container_width=True)

            # 6. Embudo por campaña (funnel)
            st.subheader("Embudo de Conversión por Campaña")
            # Simula leads por medio a partir de ventas
            factor_leads = rng.integers(5, 12)  # leads totales ≈ factor * ventas
            distrib = rng.random(len(medios)); distrib /= distrib.sum()
            leads_por_medio = (distrib * (ventas_tot * factor_leads)).round().astype(int)

            # Tasas de etapas (por medio)
            contact_rate = rng.uniform(0.45, 0.7, size=len(medios))
            appt_rate = rng.uniform(0.35, 0.6, size=len(medios))
            close_rate = rng.uniform(0.18, 0.35, size=len(medios))

            df_funnel = pd.DataFrame({
                "Medio": medios,
                "Leads": leads_por_medio,
            })
            df_funnel["Contactados"] = np.round(df_funnel["Leads"] * contact_rate).astype(int)
            df_funnel["Citados"] = np.minimum(
                df_funnel["Contactados"],
                np.round(df_funnel["Contactados"] * appt_rate).astype(int)
            )
            df_funnel["Ventas_Base"] = np.minimum(
                df_funnel["Citados"],
                np.round(df_funnel["Citados"] * close_rate).astype(int)
            )
            # Ajusta para que la suma de ventas por medio = ventas_tot
            scale = ventas_tot / max(1, df_funnel["Ventas_Base"].sum())
            df_funnel["Ventas"] = np.floor(df_funnel["Ventas_Base"] * scale).astype(int)
            diff = ventas_tot - df_funnel["Ventas"].sum()
            if diff > 0:
                # reparte la diferencia a los medios con más 'Ventas_Base'
                idxs = df_funnel.sort_values("Ventas_Base", ascending=False).index[:diff]
                df_funnel.loc[idxs, "Ventas"] += 1

            # Tasas de conversión por etapa
            df_funnel["Conv_Lead→Contacto_%"] = (df_funnel["Contactados"]/df_funnel["Leads"].replace(0,np.nan))*100
            df_funnel["Conv_Contacto→Cita_%"] = (df_funnel["Citados"]/df_funnel["Contactados"].replace(0,np.nan))*100
            df_funnel["Conv_Cita→Venta_%"] = (df_funnel["Ventas"]/df_funnel["Citados"].replace(0,np.nan))*100
            df_funnel = df_funnel.fillna(0)

            st.dataframe(df_funnel[["Medio","Leads","Contactados","Citados","Ventas",
                                    "Conv_Lead→Contacto_%","Conv_Contacto→Cita_%","Conv_Cita→Venta_%"]], use_container_width=True)

            col1,col2 = st.columns(2)
            col1.plotly_chart(px.funnel(df_funnel, y="Medio", x="Leads", title="Leads por Medio"), use_container_width=True)
            col2.plotly_chart(px.bar(df_funnel, x="Medio", y=["Leads","Contactados","Citados","Ventas"], barmode="group",
                                     title="Etapas del Funnel por Medio"), use_container_width=True)

            # KPIs de compradores
            st.subheader("Perfil del Comprador")
            a,b,c = st.columns(3)
            a.metric("Edad Promedio", f"{df_clientes['Edad'].mean():.1f}")
            b.metric("% con Hijos", f"{(df_clientes['Tiene_Hijos'].eq('Sí').mean()*100):.1f}%")
            c.metric("Ticket Promedio", f"${df_clientes['Precio_Final'].mean():,.0f}")

            c1,c2 = st.columns(2)
            c1.plotly_chart(px.histogram(df_clientes, x="Edad", nbins=12, title="Distribución de Edad"), use_container_width=True)
            c2.plotly_chart(px.pie(df_clientes, names="Origen", title="Origen (Estados)"), use_container_width=True)

    # ---------- 8. Estrategia y Forecast ----------
    with tab_forecast:
        st.subheader("Forecast y Diagnóstico de Eficiencia")
        if not hist.empty:
            base = hist.tail(3)["Ventas_Mes"].mean() if len(hist)>=3 else hist["Ventas_Mes"].mean()
            future_dates = pd.date_range(hist["Fecha"].max() + pd.offsets.MonthBegin(1), periods=6, freq="MS")
            forecast_vals = np.maximum(0, np.round(base + np.linspace(0, 1, 6) + np.random.normal(0, 0.5, 6))).astype(int)

            hist_plot = hist[["Fecha", "Ventas_Mes"]].rename(columns={"Ventas_Mes":"Ventas"})
            fc_plot = pd.DataFrame({"Fecha": future_dates, "Ventas": forecast_vals})
            hist_plot["Serie"] = "Histórico"; fc_plot["Serie"] = "Forecast"
            df_fc = pd.concat([hist_plot, fc_plot])
            fig_fc = px.line(df_fc, x="Fecha", y="Ventas", color="Serie", title="Ventas: Histórico + Forecast 6M")
            fig_fc.update_layout(margin=dict(l=0,r=0,t=40,b=0))
            st.plotly_chart(fig_fc, use_container_width=True)
        else:
            st.info("Sin histórico para calcular forecast.")

        st.subheader("Simulación de Impacto de Precio → Conversión")
        grid = pd.DataFrame({
            "ΔPrecio_%": [-10, -5, 0, 5, 10],
            "Tasa_Conversión_%": [18, 15, 12, 10, 8]
        })
        st.dataframe(grid, use_container_width=True)
        st.plotly_chart(px.line(grid, x="ΔPrecio_%", y="Tasa_Conversión_%", markers=True,
                                title="Curva Precio vs Conversión (Dummy)"), use_container_width=True)

        st.subheader("Diagnóstico")
        # Diagnóstico rápido usando motivos de no compra de df_funnel no aplica aquí directamente; mostramos guía general
        st.info("Usa el embudo por campaña y la eficiencia por asesor para orientar acciones: precio, producto, capacitación y medios.")

# Footer sutil
st.write("")
st.caption("Data 100% simulada para fines demostrativos • Construido con Streamlit + Plotly")
#     with tab_forecast:
#         st.subheader("Forecast y Diagnóstico de Eficiencia")
#         # Forecast 6 meses: promedio últimos 3 meses + ruido leve
#         if not hist.empty:
#             base = hist.tail(3)["Ventas_Mes"].mean() if len(hist)>=3 else hist["Ventas_Mes"].mean()
#             future_dates = pd.date_range(hist["Fecha"].max() + pd.offsets.MonthBegin(1), periods=6, freq="MS")
#             forecast_vals = np.maximum(0, np.round(base + np.linspace(0, 1, 6) + np.random.normal(0, 0.5, 6))).astype(int)

#             hist_plot = hist[["Fecha", "Ventas_Mes"]].rename(columns={"Ventas_Mes":"Ventas"})
#             fc_plot = pd.DataFrame({"Fecha": future_dates, "Ventas": forecast_vals})
#             hist_plot["Serie"] = "Histórico"; fc_plot["Serie"] = "Forecast"
#             df_fc = pd.concat([hist_plot, fc_plot])
#             fig_fc = px.line(df_fc, x="Fecha", y="Ventas", color="Serie", title="Ventas: Histórico + Forecast 6M")
#             fig_fc.update_layout(margin=dict(l=0,r=0,t=40,b=0))
#             st.plotly_chart(fig_fc, use_container_width=True)
#         else:
#             st.info("Sin histórico para calcular forecast.")

#         # Simulación impacto precio -> conversión
#         st.subheader("Simulación de Impacto de Precio → Conversión")
#         grid = pd.DataFrame({
#             "ΔPrecio_%": [-10, -5, 0, 5, 10],
#             "Tasa_Conversión_%": [18, 15, 12, 10, 8]  # relación hipotética inversa
#         })
#         st.dataframe(grid, use_container_width=True)
#         st.plotly_chart(px.line(grid, x="ΔPrecio_%", y="Tasa_Conversión_%", markers=True,
#                                 title="Curva Precio vs Conversión (Dummy)"), use_container_width=True)

#         # Diagnóstico de eficiencia
#         st.subheader("Diagnóstico")
#         df_cl = df_clientes_leads[df_clientes_leads["Nombre_Proyecto"] == proy_sel]
#         if not df_cl.empty:
#             perdidos = df_cl[df_cl["Tipo_Estado"]=="No Comprador"]
#             if not perdidos.empty:
#                 motivo_top = perdidos["Motivo_No_Compra"].value_counts(normalize=True).idxmax()
#                 pct_top = perdidos["Motivo_No_Compra"].value_counts(normalize=True).max()*100
#                 if motivo_top in ["Precio Alto", "Mejor Opción"] and pct_top>=35:
#                     st.error(f"🚨 **Acción en Precio/Producto:** El {pct_top:.0f}% de leads se pierde por '{motivo_top}'. Revisar precio por m² o propuesta de valor.")

#             # Eficiencia de asesor
#             conv_por_asesor = df_cl.assign(es_comp=df_cl["Tipo_Estado"]=="Comprador").groupby("Asesor")["es_comp"].mean().sort_values(ascending=False)*100
#             if not conv_por_asesor.empty:
#                 top_asesor = conv_por_asesor.index[0]
#                 top_rate = conv_por_asesor.iloc[0]
#                 st.plotly_chart(px.bar(conv_por_asesor.reset_index(), x="Asesor", y="es_comp",
#                                        labels={"es_comp":"Tasa de Cierre %"}, title="Tasa de Cierre por Asesor"), use_container_width=True)
#                 st.success(f"🏆 **Asesor Top:** {top_asesor} con tasa de cierre {top_rate:.1f}%.")

#             # Eficiencia de campaña (CPA simulado)
#             medios = df_cl["Medio_Contacto"].value_counts()
#             if not medios.empty:
#                 # CPA hipotético inverso a cierres
#                 cierres_por_medio = df_cl[df_cl["Tipo_Estado"]=="Comprador"]["Medio_Contacto"].value_counts()
#                 df_cpa = pd.DataFrame({"Medio": medios.index, "Leads": medios.values})
#                 df_cpa["Cierres"] = df_cpa["Medio"].map(cierres_por_medio).fillna(0)
#                 # costo simulado por lead
#                 costo_lead = {"Redes Sociales": 80, "Referido": 20, "Lona": 50, "Web": 60, "WhatsApp": 30}
#                 df_cpa["Costo_Lead"] = df_cpa["Medio"].map(costo_lead)
#                 df_cpa["CPA"] = np.where(df_cpa["Cierres"]>0, (df_cpa["Leads"]*df_cpa["Costo_Lead"]) / df_cpa["Cierres"], np.nan)
#                 colA, colB = st.columns(2)
#                 colA.plotly_chart(px.bar(df_cpa, x="Medio", y="CPA", title="CPA Simulado por Medio"), use_container_width=True)
#                 colB.plotly_chart(px.bar(df_cpa, x="Medio", y="Cierres", title="Cierres por Medio"), use_container_width=True)
#                 if (df_cpa["Medio"]=="Redes Sociales").any():
#                     st.warning("⚠️ **Revisión de Marketing:** Redes Sociales genera muchos leads, pero revisa su calidad frente a 'Referido'.")

# # Footer sutil
# st.write("")
# st.caption("Data 100% simulada para fines demostrativos • Construido con Streamlit + Plotly")
