"""
Streamlit dashboard para gestión de proyectos Vertical / Horizontal / Lote
- Permite subir un archivo Excel (.xlsx o .xlsm) con hojas: Vertical, Horizontal, Lote, Ventas (y otras).
- Implementa la lógica solicitada para el estado "Disponible".

Cómo usar:
1. Instala dependencias: pip install streamlit pandas openpyxl
2. Ejecuta: streamlit run streamlit_dashboard.py

Nota: los archivos .xlsm (con macros) se leerán pero las macros NO se ejecutarán ni se extraerán. Si necesitas trabajar con el código VBA o ejecutar macros, hay que añadir herramientas específicas (por ejemplo, xlwings o extractores de VBA) en una fase posterior.

Autor: Generado por ChatGPT
"""

import streamlit as st
import pandas as pd
from io import BytesIO

st.set_page_config(page_title="Dashboard Inventario - Vertical/Horizontal/Lote", layout="wide")

st.title("Dashboard interactivo de inventario")
st.markdown("Usa el **sidebar** para subir el archivo, elegir la sección y navegar entre vistas.")
st.markdown("Sube tu archivo Excel (.xlsx o .xlsm) con las hojas 'Vertical', 'Horizontal', 'Lote' y 'Ventas' y explora el inventario disponible por tipo de desarrollo.")

# -----------------------------------------------------------------------------
# Helper functions
# -----------------------------------------------------------------------------
@st.cache_data
def read_excel_sheets(uploaded_file_bytes, filename: str):
    """Lee todas las hojas de un archivo Excel. Soporta .xlsx y .xlsm (usando openpyxl).
    uploaded_file_bytes: bytes del archivo subido
    filename: nombre del archivo para detectar la extensión
    """
    # Elegir engine: openpyxl funciona para xlsx/xlsm
    engine = 'openpyxl'
    # pandas acepta un file-like object
    xls = pd.read_excel(BytesIO(uploaded_file_bytes), sheet_name=None, engine=engine)
    return xls


def ensure_df(df):
    # Si df es None, devolvemos dataframe vacío
    if df is None:
        return pd.DataFrame()
    return df.copy()


def ensure_fecha_datetime(df):
    if 'Fecha' in df.columns:
        df = df.copy()
        df['Fecha'] = pd.to_datetime(df['Fecha'], errors='coerce')
    return df


def process_disponible_tab(df_main, df_ventas):
    """
    Ejecuta la lógica solicitada cuando el usuario elige 'Disponible'.
    Devuelve (ultimos_registros, inventario_total)
    """
    # 1. Filtrar proyectos disponibles en la hoja principal
    proyectos_disponibles_df = df_main[df_main['Estatus_venta'] == 'Disponible'] if not df_main.empty and 'Estatus_venta' in df_main.columns else pd.DataFrame()
    lista_nombres_disponibles = proyectos_disponibles_df['Nombre_desarrollo'].unique().tolist() if not proyectos_disponibles_df.empty and 'Nombre_desarrollo' in proyectos_disponibles_df.columns else []

    if len(lista_nombres_disponibles) == 0:
        return pd.DataFrame(), 0

    # 2. Filtrar df_ventas para esos proyectos
    ventas_filtered = df_ventas[df_ventas['Nombre_desarrollo'].isin(lista_nombres_disponibles)] if not df_ventas.empty and 'Nombre_desarrollo' in df_ventas.columns else pd.DataFrame()

    if ventas_filtered.empty:
        return pd.DataFrame(), 0

    # 3. Asegurar 'Fecha' como datetime y obtener último registro por Nombre_desarrollo
    ventas_filtered = ensure_fecha_datetime(ventas_filtered)
    # Orden descendente por Fecha y tomar el primer registro por Nombre_desarrollo (último por fecha)
    ventas_filtered = ventas_filtered.sort_values(by='Fecha', ascending=False)
    ultimos_registros = ventas_filtered.drop_duplicates(subset=['Nombre_desarrollo'], keep='first')

    # 4. Calcular inventario total
    inventario_col = 'Inventario_disponible'
    if inventario_col not in ultimos_registros.columns:
        inventario_total = 0
    else:
        inventario_total = ultimos_registros[inventario_col].fillna(0).sum()

    # Ordenar columnas de salida mínimas si existen
    cols_to_show = [c for c in ['Nombre_desarrollo', 'Inventario_disponible', 'Fecha'] if c in ultimos_registros.columns]
    ultimos_registros_out = ultimos_registros[cols_to_show].sort_values(by='Nombre_desarrollo') if not ultimos_registros.empty else pd.DataFrame()

    return ultimos_registros_out, inventario_total


# -----------------------------------------------------------------------------
# Sidebar: carga y navegación
# -----------------------------------------------------------------------------
with st.sidebar:
    st.header("Carga de archivo")
    uploaded_file = st.file_uploader("Sube el archivo Excel (.xlsx o .xlsm)", type=['xlsx', 'xlsm'], accept_multiple_files=False, key="uploader")

    sheets = {}
    if uploaded_file is not None:
        try:
            uploaded_bytes = uploaded_file.read()
            filename = uploaded_file.name
            sheets = read_excel_sheets(uploaded_bytes, filename)
            st.success("Cargado: " + filename)
            if filename.lower().endswith('.xlsm'):
                st.info("Archivo .xlsm: las macros NO se ejecutan, solo se leen los datos.")
        except Exception as e:
            st.error(f"Error leyendo el archivo Excel: {e}")

    st.header("Menú")
seccion = st.radio(
    "Sección",
    options=["Análisis del mercado", "Análisis por desarrollo"],
    index=0,
    key="nav_seccion"
)

# Controles dependientes de la sección seleccionada
if seccion == "Análisis del mercado":
    st.header("📊 Análisis del mercado")
    estatus_sel = st.radio("Estatus", options=['Disponible', 'Vendido'], index=0, horizontal=True, key="nav_estatus")
    dataset_sel = None
else:
    st.header("🏗️ Análisis por desarrollo")
    estatus_sel = None
    dataset_opciones = ["Vertical", "Horizontal", "Lote"]
    dataset_sel = st.selectbox("Conjunto de datos", options=dataset_opciones, index=0, key="nav_dataset")

# Cargar DataFrames relevantes (si existen)
df_vertical = ensure_df(sheets.get('Vertical'))
df_horizontal = ensure_df(sheets.get('Horizontal'))
df_lote = ensure_df(sheets.get('Lote'))
df_ventas = ensure_df(sheets.get('Ventas'))

# Asegurar Fecha en df_ventas
df_ventas = ensure_fecha_datetime(df_ventas)

# Helper para elegir df principal por nombre
DF_MAP = {
    'Vertical': lambda: ensure_df(sheets.get('Vertical')),
    'Horizontal': lambda: ensure_df(sheets.get('Horizontal')),
    'Lote': lambda: ensure_df(sheets.get('Lote')),
}

# Elegir df_main solo para análisis por desarrollo
if seccion == "Análisis por desarrollo" and dataset_sel is not None:
    df_main = DF_MAP.get(dataset_sel, lambda: pd.DataFrame())()
else:
    df_main = pd.DataFrame()

# -----------------------------------------------------------------------------
# Funciones de render para cada vista
# -----------------------------------------------------------------------------


def render_market_analysis(df_main, df_ventas, dataset_name, estatus):
    st.subheader(f"Análisis del mercado · {dataset_name}")

    if df_main.empty:
        st.warning(f"No se encontró la hoja o está vacía: {dataset_name}. Sube un archivo con la hoja '{dataset_name}'.")
        return

    with st.expander("Vista previa de la hoja (primeras filas)"):
        st.dataframe(df_main.head(), use_container_width=True)

    if estatus == 'Disponible':
        ultimos_registros, inventario_total = process_disponible_tab(df_main, df_ventas)

        if ultimos_registros.empty:
            st.info("No hay registros de ventas recientes para los proyectos disponibles o no coinciden los nombres con la hoja 'Ventas'.")
            return

        try:
            inventario_display = int(inventario_total) if float(inventario_total).is_integer() else round(inventario_total, 2)
        except Exception:
            inventario_display = inventario_total

        st.metric(label="Inventario Total Disponible", value=inventario_display)
        st.markdown("**Detalle por proyecto (último registro disponible)**")
        st.dataframe(ultimos_registros, use_container_width=True)

        csv = ultimos_registros.to_csv(index=False).encode('utf-8')
        st.download_button(
            label="Descargar detalle (CSV)",
            data=csv,
            file_name=f"ultimos_registros_{dataset_name}.csv",
            mime='text/csv',
            key=f"download_{dataset_name.lower()}"
        )
    else:
        st.info("La lógica para 'Vendido' está pendiente y se implementará en una siguiente fase.")


def render_project_analysis(df_main, df_ventas, sheets, dataset_name):
    st.subheader(f"Análisis por desarrollo · {dataset_name}")

    if df_main.empty:
        st.warning(f"No se encontró la hoja o está vacía: {dataset_name}. Sube un archivo con la hoja '{dataset_name}'.")
        return

    # Selector de proyecto
    lista_proyectos = list(df_main['Nombre_desarrollo'].dropna().unique()) if 'Nombre_desarrollo' in df_main.columns else []
    lista_proyectos.insert(0, "Seleccione un proyecto")

    proyecto_seleccionado = st.selectbox(
        "Elige un proyecto para analizar:",
        options=lista_proyectos,
        key=f"select_proyecto_{dataset_name.lower()}"
    )

    if proyecto_seleccionado == "Seleccione un proyecto":
        st.info("Selecciona un proyecto para ver el detalle.")
        return

    # Filtrar DataFrames relevantes
    info_general = df_main[df_main['Nombre_desarrollo'] == proyecto_seleccionado]

    historial_ventas = df_ventas[df_ventas['Nombre_desarrollo'] == proyecto_seleccionado].copy() if not df_ventas.empty and 'Nombre_desarrollo' in df_ventas.columns else pd.DataFrame()
    historial_ventas = ensure_fecha_datetime(historial_ventas).sort_values(by='Fecha') if not historial_ventas.empty else historial_ventas

    # Hojas auxiliares por dataset
    precios_sheet_name = 'Precios'  # común
    prototipos_sheet_map = {
        'Vertical': 'Info_prototipos_vertical',
        'Horizontal': 'Info_prototipos_horizontal',
        'Lote': 'Info_prototipos_lote',
    }

    df_precios_local = ensure_df(sheets.get(precios_sheet_name))
    df_precios_local = ensure_fecha_datetime(df_precios_local)
    historial_precios = df_precios_local[df_precios_local['Nombre_desarrollo'] == proyecto_seleccionado].sort_values(by='Fecha') if not df_precios_local.empty and 'Nombre_desarrollo' in df_precios_local.columns else pd.DataFrame()

    df_prototipos_local = ensure_df(sheets.get(prototipos_sheet_map.get(dataset_name)))
    info_prototipos = df_prototipos_local[df_prototipos_local['Nombre_desarrollo'] == proyecto_seleccionado] if not df_prototipos_local.empty and 'Nombre_desarrollo' in df_prototipos_local.columns else pd.DataFrame()

    # --- Resumen rápido ---
    st.write("### Resumen General del Proyecto")
    if not info_general.empty:
        datos = info_general.iloc[0]
        col1, col2, col3, col4 = st.columns(4)

        zona = datos['Zona'] if 'Zona' in info_general.columns else "-"
        desarrolladora = datos['Desarrolladora'] if 'Desarrolladora' in info_general.columns else "-"
        estatus_venta = datos['Estatus_venta'] if 'Estatus_venta' in info_general.columns else "-"
        deptos_planeados = datos['Departamentos_planeados'] if 'Departamentos_planeados' in info_general.columns else "-"

        col1.metric("Zona", zona)
        col2.metric("Desarrolladora", desarrolladora)
        col3.metric("Estatus Venta", estatus_venta)
        col4.metric("Deptos. Planeados", deptos_planeados)
    else:
        st.warning("No hay información general disponible para este proyecto.")

    # --- Pestañas de detalle ---
    tab_ventas, tab_precios, tab_prototipos, tab_info = st.tabs([
        "Historial de Ventas",
        "Historial de Precios",
        "Prototipos",
        "Información Completa"
    ])

    with tab_ventas:
        st.write("#### Evolución de Ventas e Inventario")
        if not historial_ventas.empty and 'Fecha' in historial_ventas.columns:
            df_plot = historial_ventas.set_index('Fecha')
            cols_to_plot = [c for c in ['Ventas_acumuladas', 'Inventario_disponible'] if c in df_plot.columns]
            if len(cols_to_plot) > 0:
                st.line_chart(df_plot[cols_to_plot])
            else:
                st.warning("Faltan columnas 'Ventas_acumuladas' o 'Inventario_disponible' en el historial de ventas.")
        else:
            st.warning("No hay datos de ventas registrados para este proyecto.")

    with tab_precios:
        st.write("#### Evolución de Precios por Prototipo")
        if not historial_precios.empty and 'Fecha' in historial_precios.columns:
            if 'Nombre_prototipos' in historial_precios.columns and 'Precio' in historial_precios.columns:
                pivot = historial_precios.pivot_table(index='Fecha', columns='Nombre_prototipos', values='Precio')
                st.line_chart(pivot)
            elif 'Precio' in historial_precios.columns:
                st.line_chart(historial_precios.set_index('Fecha')['Precio'])
            else:
                st.warning("La hoja de precios no contiene la columna 'Precio'.")
        else:
            st.warning("No hay datos de precios registrados para este proyecto.")

    with tab_prototipos:
        st.write("#### Información de Prototipos")
        if not info_prototipos.empty:
            st.dataframe(info_prototipos, use_container_width=True)
        else:
            st.info("No hay información de prototipos para este proyecto.")

    with tab_info:
        st.write("#### Ficha Técnica Completa")
        if not info_general.empty:
            st.dataframe(info_general.T, use_container_width=True)
        else:
            st.info("No hay información general a mostrar.")

# -----------------------------------------------------------------------------
# Render según navegación del sidebar
# -----------------------------------------------------------------------------
if seccion == "Análisis del mercado":
    # Mostrar tabs para Vertical / Horizontal / Lote
    tab_v, tab_h, tab_l = st.tabs(["Vertical", "Horizontal", "Lote"])
    with tab_v:
        render_market_analysis(ensure_df(sheets.get('Vertical')), df_ventas, 'Vertical', estatus_sel)
    with tab_h:
        render_market_analysis(ensure_df(sheets.get('Horizontal')), df_ventas, 'Horizontal', estatus_sel)
    with tab_l:
        render_market_analysis(ensure_df(sheets.get('Lote')), df_ventas, 'Lote', estatus_sel)
else:
    render_project_analysis(df_main, df_ventas, sheets, dataset_sel)
