#!/usr/bin/env python
# -*- coding: utf-8 -*-

import geopandas as gpd
import unicodedata
import dash
from dash import Dash, dcc, html
from dash.dependencies import Input, Output, State
import plotly.graph_objects as go

# ============================================================
# CONFIG GENERAL
# ============================================================

# Si lo usas como script (.py), deja True.
# Si lo importas en Jupyter para usar fig.show(), pon False.
RUN_DASH = True

# Centro y zoom del mapa
lat = -42.034899
lon = -72.644567
zoom = 5

# Patrones para detectar especies objetivo dentro de la lista ESPECIES
species_patterns = {
    "CHORITO": ["CHORITO"],
    "OSTION DEL NORTE": ["OSTION DEL NORTE"],
    "SALMON DEL ATLANTICO": ["SALMON DEL ATLANTICO"],
    "SALMON PLATEADO O COHO": ["SALMON PLATEADO O COHO", "SALMON COHO", "COHO"],
    "SALMON REY": ["SALMON REY", "KING SALMON"]
}

# Colores por especie_target
color_map = {
    "CHORITO": "rgb(75, 154, 108)",            # verde
    "OSTION DEL NORTE": "rgb(220, 20, 60)",    # 🔴 rojo (antes naranjo)
    "SALMON DEL ATLANTICO": "rgb(70, 130, 180)",   # azul (coherente)
    "SALMON PLATEADO O COHO": "rgb(123, 104, 238)", # violeta suave
    "SALMON REY": "rgb(255, 140, 0)"               # naranja → distinto de ostión
}

# ============================================================
# FUNCIONES AUXILIARES TEXTO / ESPECIES
# ============================================================

def normalize_text(txt):
    """Pasa a string, saca tildes y convierte a MAYÚSCULAS."""
    if txt is None:
        return ""
    txt = str(txt)
    txt_nfkd = unicodedata.normalize("NFD", txt)
    txt_sin_tildes = "".join(c for c in txt_nfkd if unicodedata.category(c) != "Mn")
    return txt_sin_tildes.upper()


def detectar_especie_target(txt_norm):
    """
    txt_norm: string ya normalizado (sin tildes, mayúsculas).
    Devuelve una especie_target (clave de species_patterns) o None si no matchea.
    Si hay varias, se queda con la primera según el orden en species_patterns.
    """
    for especie_target, patrones in species_patterns.items():
        for p in patrones:
            if p in txt_norm:
                return especie_target
    return None


# ============================================================
# CARGA Y PREPARACIÓN DE DATOS
# ============================================================

def cargar_y_preparar_datos():
    # 1) Cargar shapefile nacional
    gdf = gpd.read_file(
        "data/ccaa_nac_shp_2025/ccaa_nacional_4326.shp",
        encoding="utf-8"
    )

    # 2) Renombrar algunas columnas a nombres más cómodos
    column_aliases = {
        'COMUNA': 'comuna',
        'N_CODIGOCE': 'Centro',
        'TOPONIMIO': 'Sector',
        'T_GRUPOESP': 'Tipo de cultivo',
        'ESPECIES': 'ESPECIES',
        'REGION': 'Región'
    }
    gdf.rename(columns=column_aliases, inplace=True)

    # 3) Normalizar texto de ESPECIES
    gdf["ESPECIES_NORM"] = gdf["ESPECIES"].apply(normalize_text)

    # 4) Crear columna Especie_target (chorito / ostión / salmones) o None
    gdf["Especie_target"] = gdf["ESPECIES_NORM"].apply(detectar_especie_target)

    # 5) Quedarnos solo con concesiones que tienen una especie_target de interés
    target_labels = list(species_patterns.keys())
    gdf_sel = gdf[gdf["Especie_target"].isin(target_labels)].copy()

    # 6) Filtrar centros válidos (evitar '0')
    gdf_sel = gdf_sel[gdf_sel["Centro"] != '0']
    gdf_sel["Centro"] = gdf_sel["Centro"].astype(int)

    # 7) CRS y centroides
    if gdf_sel.crs is None:
        gdf_sel.set_crs(epsg=4326, inplace=True)

    # Calcular centroides en UTM (zona 19S aprox.) y volver a WGS84
    gdf_proj = gdf_sel.to_crs(epsg=32719)
    centroids_proj = gdf_proj.geometry.centroid
    centroids = centroids_proj.to_crs(gdf_sel.crs)

    gdf_sel["lat"] = centroids.y
    gdf_sel["lon"] = centroids.x

    # 8) Hover text (incluye ESPECIES completas y Especie_target)
    gdf_sel["hover_text"] = gdf_sel.apply(
        lambda row: (
            f"<b>Comuna:</b> {row['comuna']}<br>"
            f"<b>Centro:</b> {row['Centro']}<br>"
            f"<b>Sector:</b> {row['Sector']}<br>"
            f"<b>Tipo de cultivo:</b> {row['Tipo de cultivo']}<br>"
            f"<b>Especie target:</b> {row['Especie_target']}<br>"
            f"<b>Región:</b> {row['Región']}"
        ),
        axis=1
    )

    return gdf_sel


gdf_sel = cargar_y_preparar_datos()

# Lista ordenada de especies para el dropdown
species_options = sorted(gdf_sel["Especie_target"].dropna().unique().tolist())


# ============================================================
# FILTRO Y FIGURA
# ============================================================

def filter_gdf(selected_species):
    """
    selected_species: lista de strings (Especie_target) seleccionadas en el dropdown.
    """
    df = gdf_sel.copy()
    if not selected_species:
        return df.iloc[0:0]  # vacío
    return df[df["Especie_target"].isin(selected_species)]


def build_figure(df, centro_code: int = None):
    """
    Construye figura de Plotly (Scattermapbox) según el GeoDataFrame filtrado.
    Si se pasa centro_code, se resalta ese centro con tamaño mayor.
    """
    fig = go.Figure()

    if df is None or df.empty:
        fig.update_layout(
            mapbox=dict(
                style="carto-positron",
                center=dict(lat=lat, lon=lon),
                zoom=zoom,
            ),
            margin=dict(l=0, r=0, t=0, b=0),
            showlegend=False,
            annotations=[
                dict(
                    text="Instituto en Socio-Ecología Costera (SECOS) - Datacenter<br>Fuente: Sernapesca",
                    x=0,
                    xref="paper",
                    y=1,
                    yref="paper",
                    showarrow=False,
                    font=dict(size=10),
                    xanchor='left'
                )
            ]
        )
        return fig

    base_colors = df["Especie_target"].map(color_map).fillna("rgb(150, 150, 150)").tolist()
    sizes = [10] * len(df)

    if centro_code is not None:
        mask = df["Centro"] == centro_code
        idx = df.index[mask]
        if not idx.empty:
            pos = df.index.get_loc(idx[0])
            sizes[pos] = 20

    fig.add_trace(go.Scattermapbox(
        lat=df["lat"],
        lon=df["lon"],
        mode="markers",
        marker=go.scattermapbox.Marker(
            size=sizes,
            color=base_colors,
            opacity=0.9,
            symbol="circle",
        ),
        text=df["hover_text"],
        hoverinfo="text"
    ))

    fig.update_layout(
        mapbox=dict(
            style="carto-positron",
            center=dict(lat=lat, lon=lon),
            zoom=zoom,
        ),
        margin=dict(l=0, r=0, t=0, b=0),
        showlegend=False,
        width=1000,
        height=800,
        hoverlabel=dict(
            font=dict(size=9),
        ),
        annotations=[
            dict(
                text="Instituto en Socio-Ecología Costera (SECOS) - Datacenter<br>Fuente: Sernapesca",
                x=0,
                xref="paper",
                y=1,
                yref="paper",
                showarrow=False,
                font=dict(size=10),
                xanchor='left'
            )
        ]
    )

    return fig


# ============================================================
# APP DASH
# ============================================================

app = Dash(
    __name__,
    requests_pathname_prefix="/apps/concesiones/",
)

app.layout = html.Div([
    html.Div([
        html.Img(
            src=app.get_asset_url("infografia-acuicultura.png"),
            style={"height": "50px", "width": "50px", "margin-right": "10px"}
        ),
        html.H1(
            "Concesiones de acuicultura (bivalvos y salmones)",
            style={"margin-bottom": "10px"}
        ),
    ], style={"display": "flex", "align-items": "center"}),

    html.Div([
        html.Label("Selecciona especie(s) a mostrar:"),
        dcc.Dropdown(
            id="species-filter",
            options=[{"label": s, "value": s} for s in species_options],
            value=species_options,        # todas seleccionadas al inicio
            multi=True,                   # permite seleccionar varias
            clearable=False,
            style={"width": "400px", "margin-bottom": "10px"}
        )
    ]),

    html.Label("Ingrese código del centro para resaltar su ubicación:"),
    dcc.Input(id="input-center-code", type="text", debounce=True),
    html.Button("Buscar", id="search-button", n_clicks=0),

    # Contenedor del mapa + leyenda (posición relativa para poder usar absolute dentro)
    html.Div([
        # Leyenda estática
        html.Div([
            html.Div([
                html.Div(style={
                    "display": "inline-block",
                    "width": "15px",
                    "height": "15px",
                    "background-color": color_map["CHORITO"],
                    "margin-right": "5px",
                    "border-radius": "50%"
                }),
                html.Span("Chorito")
            ], style={"margin-bottom": "5px"}),

            html.Div([
                html.Div(style={
                    "display": "inline-block",
                    "width": "15px",
                    "height": "15px",
                    "background-color": color_map["OSTION DEL NORTE"],
                    "margin-right": "5px",
                    "border-radius": "50%"
                }),
                html.Span("Ostión del Norte")
            ], style={"margin-bottom": "5px"}),

            html.Div([
                html.Div(style={
                    "display": "inline-block",
                    "width": "15px",
                    "height": "15px",
                    "background-color": color_map["SALMON DEL ATLANTICO"],
                    "margin-right": "5px",
                    "border-radius": "50%"
                }),
                html.Span("Salmón del Atlántico")
            ], style={"margin-bottom": "5px"}),

            html.Div([
                html.Div(style={
                    "display": "inline-block",
                    "width": "15px",
                    "height": "15px",
                    "background-color": color_map["SALMON PLATEADO O COHO"],
                    "margin-right": "5px",
                    "border-radius": "50%"
                }),
                html.Span("Salmón Plateado / Coho")
            ], style={"margin-bottom": "5px"}),

            html.Div([
                html.Div(style={
                    "display": "inline-block",
                    "width": "15px",
                    "height": "15px",
                    "background-color": color_map["SALMON REY"],
                    "margin-right": "5px",
                    "border-radius": "50%"
                }),
                html.Span("Salmón Rey")
            ], style={"margin-bottom": "5px"}),
        ], style={
            "position": "absolute",
            "top": "20px",
            "left": "20px",
            "background-color": "white",
            "padding": "10px",
            "border": "1px solid #ccc",
            "border-radius": "8px",
            "box-shadow": "0 2px 4px rgba(0,0,0,0.2)",
            "zIndex": 999,
            "font-size": "12px"
        }),

        # Mapa con zoom por scroll
        dcc.Graph(id="map-graph", config={"scrollZoom": True}),
    ], style={"position": "relative"})
])


@app.callback(
    Output("map-graph", "figure"),
    [
        Input("search-button", "n_clicks"),
        Input("species-filter", "value")
    ],
    State("input-center-code", "value")
)
def update_map(n_clicks, selected_species, centro_code):
    df = filter_gdf(selected_species)

    centro_int = None
    if n_clicks and centro_code:
        try:
            centro_int = int(centro_code)
        except (ValueError, TypeError):
            centro_int = None

    return build_figure(df, centro_int)


# ============================================================
# MAIN
# ============================================================

if __name__ == "__main__":
    app.run(
        host="127.0.0.1",   # 👈 sólo interno
        port=8052,
        debug=False
    )