import streamlit as st
import pandas as pd
import os
import joblib
import numpy as np
import math
import folium
import json
import base64
from io import BytesIO
from pathlib import Path
from PIL import Image
from streamlit_folium import st_folium

# --- UTILS ---
@st.cache_data
def get_image_base64(img_path):
    img = Image.open(img_path).convert("RGBA")
    w, h = img.size
    buffered = BytesIO()
    img.save(buffered, format="PNG")
    img_str = base64.b64encode(buffered.getvalue()).decode("ascii")
    return f"data:image/png;base64,{img_str}", w, h

def calculate_error(z_real, x_real, y_real, z_pred, x_pred, y_pred):
    z_err = abs(z_real - z_pred)
    dist_err = math.sqrt((x_real - x_pred)**2 + (y_real - y_pred)**2)
    return z_err, dist_err

def show_test_inference(cfg):
    st.subheader("🧪 Test Inferenza Offline")
    
    MODEL_DIR = Path("/data/model")
    TEST_SAMPLES_DIR = Path("/data/train/testsamples")
    MAPS_DIR = Path("/data/maps")

    available_models = sorted([m.name for m in MODEL_DIR.glob("model_camp_*.joblib")], reverse=True)
    test_files = sorted([f.name for f in TEST_SAMPLES_DIR.glob("*.csv")])

    if not available_models or not test_files:
        st.warning("Verifica la presenza di modelli e campioni di test.")
        return

    col1, col2 = st.columns(2)
    selected_model = col1.selectbox("🎯 Seleziona Modello:", available_models)
    selected_test = col2.selectbox("📄 Seleziona Fingerprint:", test_files)

    if "test_results" not in st.session_state:
        st.session_state.test_results = None

    if st.button("🚀 ESEGUI TEST DI PRECISIONE", type="primary", use_container_width=True):
        try:
            m_pkg = joblib.load(MODEL_DIR / selected_model)
            delim = cfg.get('paths', {}).get('csv_delimiter', ';')
            df_test = pd.read_csv(TEST_SAMPLES_DIR / selected_test, sep=delim)
            row = df_test.iloc[0]
            
            z_real, x_real, y_real = int(round(float(row['z']))), float(row['x']), float(row['y'])
            gws = m_pkg['gateways_order']
            fill = m_pkg.get('nan_fill', -110.0)

            # --- DEBUG VETTORE INPUT ---
            raw_vals = []
            for gw in gws:
                val = row.get(gw)
                raw_vals.append(float(val) if val is not None and not pd.isna(val) else fill)
            
            X_in = np.array([raw_vals])
            
            # Visualizzazione Debug in UI per l'operatore
            with st.expander("🔍 Analisi Vettore di Input (Fingerprint vs Modello)"):
                debug_df = pd.DataFrame({
                    "Gateway MAC": gws,
                    "RSSI Letto": [row.get(gw, "NON TROVATO") for gw in gws],
                    "RSSI Finale (Input AI)": raw_vals
                })
                st.dataframe(debug_df)
                match_count = np.sum(X_in[0] > fill)
                st.write(f"**Gateway Corrispondenti:** {match_count} su {len(gws)}")
                if match_count == 0:
                    st.error("ERRORE: Il file di test non contiene nessuno dei Gateway usati per l'addestramento!")

            # Predizione
            z_pred = int(m_pkg['floor_clf'].predict(X_in)[0])
            x_pred, y_pred = -1.0, -1.0
            if z_pred in m_pkg['xy_by_floor']:
                xy = m_pkg['xy_by_floor'][z_pred].predict(X_in)[0]
                x_pred, y_pred = float(xy[0]), float(xy[1])

            z_err, dist_err = calculate_error(z_real, x_real, y_real, z_pred, x_pred, y_pred)
            st.session_state.test_results = {
                "z_real": z_real, "x_real": x_real, "y_real": y_real,
                "z_pred": z_pred, "x_pred": x_pred, "y_pred": y_pred,
                "z_err": z_err, "dist_err": dist_err, "model_used": selected_model
            }
        except Exception as e:
            st.error(f"Errore durante l'inferenza: {e}")

    # --- VISUALIZZAZIONE GRAFICA DEI RISULTATI ---
    if st.session_state.test_results:
        res = st.session_state.test_results
        
        c_info1, c_info2 = st.columns(2)
        with c_info1:
            st.info(f"📍 **Test Reale** | Piano: {res['z_real']} | X: **{res['x_real']}** | Y: **{res['y_real']}**")
        with c_info2:
            st.success(f"🔮 **Predizione** | Piano: {res['z_pred']} | X: **{round(res['x_pred'],1)}** | Y: **{round(res['y_pred'],1)}**")

        st.markdown(f"""
        <div style="background-color: #f8f9fa; padding: 12px; border-radius: 8px; border-left: 5px solid #00bcd4; margin: 10px 0;">
            <h5 style="margin:0 0 8px 0;">📍 Legenda Mappa</h5>
            <span style="color: #00bcd4; font-weight: bold;">● PUNTO DI TEST:</span> Posizione reale del rilievo.<br>
            <span style="color: #ff9800; font-weight: bold;">● PUNTO PREDETTO:</span> Posizione calcolata dal modello AI.<br>
            <span style="color: #ffeb3b; font-weight: bold;">--- LINEA GIALLA:</span> Scostamento di <b>{round(res['dist_err'], 2)} px</b>.
        </div>
        """, unsafe_allow_html=True)

        img_filename = f"floor_{res['z_pred']}.png"
        meta_filename = f"meta_{res['z_pred']}.json"
        img_p = MAPS_DIR / img_filename
        meta_p = MAPS_DIR / meta_filename

        if img_p.exists() and meta_p.exists():
            with open(meta_p, "r") as f: meta = json.load(f)
            img_data, w, h = get_image_base64(img_p)
            bounds = [[0, 0], [h, w]]
            
            m = folium.Map(location=[h/2, w/2], crs="Simple", tiles=None, zoom_start=0, attribution_control=False)
            m.fit_bounds(bounds)
            m.options.update({"minZoom": -5, "maxZoom": 5, "maxBounds": bounds, "zoomSnap": 0.25})
            folium.raster_layers.ImageOverlay(image=img_data, bounds=bounds).add_to(m)

            if meta.get("calibrated"):
                def to_px(mx, my):
                    px = (mx * meta["pixel_ratio"]) + meta["origin"][0]
                    py = meta["origin"][1] - (my * meta["pixel_ratio"])
                    return [py, px]

                p_real = to_px(res['x_real'], res['y_real'])
                p_pred = to_px(res['x_pred'], res['y_pred'])

                # Marker Posizione Reale
                folium.CircleMarker(
                    location=p_real, radius=11, color="#00838f", fill=True, 
                    fill_color="#00bcd4", fill_opacity=0.85, tooltip="PUNTO DI TEST (REALE)"
                ).add_to(m)
                
                # Marker Predizione
                if res['x_pred'] != -1.0:
                    folium.CircleMarker(
                        location=p_pred, radius=11, color="#e65100", fill=True, 
                        fill_color="#ff9800", fill_opacity=0.85,
                        tooltip=f"PREDIZIONE (Modello: {res['model_used']})"
                    ).add_to(m)
                    
                    # Linea di errore (Gialla tratteggiata)
                    folium.PolyLine(
                        locations=[p_real, p_pred], color="#ffeb3b", 
                        weight=4, opacity=0.7, dash_array='8'
                    ).add_to(m)

            st_folium(m, height=700, width=None, key=f"test_map_final", use_container_width=True)
