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

import os
import socket

import geopandas as gpd
import pandas as pd
import dash
from dash import dcc, html
from dash.dependencies import Input, Output
import plotly.graph_objects as go

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

RUN_DASH = True

# Centro y zoom del mapa (Chile)
lat = -32.5
lon = -71.0
zoom = 4.3

# Colores
COLOR_AMERB_FILL = "rgba(75, 154, 108, 0.35)"   # verde translúcido
COLOR_AMERB_LINE = "rgba(75, 154, 108, 0.9)"    # borde más sólido
COLOR_AMERB_POINT = "rgb(75, 154, 108)"         # mismo verde para centroides
COLOR_CALETAS = "rgb(70, 130, 180)"             # azulito para caletas


# ============================================================
# PUERTO AUTOMÁTICO
# ============================================================

def pick_port(preferred_port=8052):
    """
    Intenta usar preferred_port. Si está ocupado, pide al SO un puerto libre.
    """
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        try:
            s.bind(("", preferred_port))
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            return preferred_port
        except OSError:
            # Puerto ocupado -> que el SO elija uno libre
            s.bind(("", 0))
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            return s.getsockname()[1]


# ============================================================
# FUNCIONES PARA CARGAR DATOS
# ============================================================

def cargar_amerbs_shp():
    """
    Carga el shapefile de AMERBs, asegura CRS WGS84,
    calcula centroides usando un CRS proyectado (UTM 19S)
    y arma columnas de hover.
    """
    ruta_shape = "data/amerb_nac_shp/AMERB_NACIONAL.shp"
    gdf_raw = gpd.read_file(ruta_shape)

    # Aseguramos que esté en WGS84 para visualizar
    if gdf_raw.crs is None:
        gdf_raw.set_crs(epsg=4326, inplace=True)

    gdf = gdf_raw.to_crs(epsg=4326).copy()

    # Centroides precisos: proyectamos a UTM 19S, calculamos centroides,
    # luego devolvemos a WGS84
    gdf_proj = gdf.to_crs(epsg=32719)
    centroids_proj = gdf_proj.geometry.centroid
    centroids_wgs84 = gpd.GeoSeries(centroids_proj, crs=gdf_proj.crs).to_crs(epsg=4326)

    gdf["lat"] = centroids_wgs84.y
    gdf["lon"] = centroids_wgs84.x

    # Intentar encontrar columnas "nombre", "comuna", "región" de forma flexible
    cols_upper = {c.upper(): c for c in gdf.columns}

    def pick_col(candidates):
        for cand in candidates:
            if cand in cols_upper:
                return cols_upper[cand]
        return None

    col_nombre = pick_col(["NOMBRE", "NOMBRE_AMERB", "NOM_AMERB", "NOMAREA", "NOM_AMERB"])
    col_comuna = pick_col(["COMUNA", "NOM_COMUNA", "NOMCOMUNA"])
    col_region = pick_col(["REGION", "NOM_REGION", "NOMBRE_REGION"])

    def safe_val(row, col, default=""):
        if col is None or col not in row.index:
            return default
        v = row[col]
        return "" if pd.isna(v) else str(v)

    gdf["hover_text"] = gdf.apply(
        lambda row: (
            f"<b>Área de manejo:</b> {safe_val(row, col_nombre, 's/i')}<br>"
            f"<b>Comuna:</b> {safe_val(row, col_comuna, 's/i')}<br>"
            f"<b>Región:</b> {safe_val(row, col_region, 's/i')}"
        ),
        axis=1
    )

    return gdf


def cargar_caletas_csv():
    """
    Carga el CSV de caletas, detecta columnas de lat/lon de forma flexible
    y crea hover con nombre de caleta.
    """
    ruta_csv = ("data/CALETAS_SERNAPESCA_DATABASE-Coves.csv")
    df = pd.read_csv(ruta_csv, low_memory=False)

    df.columns = df.columns.str.strip()

    # Detectar columnas de lat/lon
    lat_candidates = ["lat", "LAT", "Lat", "LATITUD", "Latitud", "Y", "y"]
    lon_candidates = ["lon", "LON", "Lon", "LONGITUD", "Longitud", "X", "x"]

    def pick_col(candidates):
        for c in candidates:
            if c in df.columns:
                return c
        return None

    lat_col = pick_col(lat_candidates)
    lon_col = pick_col(lon_candidates)

    if lat_col is None or lon_col is None:
        raise ValueError(
            "No se encontraron columnas de latitud/longitud en el CSV de caletas. "
            "Revisa los nombres de columnas y ajusta la lista de candidatos."
        )

    df["lat"] = pd.to_numeric(df[lat_col], errors="coerce")
    df["lon"] = pd.to_numeric(df[lon_col], errors="coerce")

    # Columna para nombre de caleta
    name_candidates = ["Nm_Caleta", "Nombre_Caleta", "NOMBRE", "Nombre", "Caleta", "NOM_CALET"]
    name_col = None
    for c in name_candidates:
        if c in df.columns:
            name_col = c
            break

    if name_col is None:
        # Si no encontramos, usamos el Id como fallback
        name_col = df.columns[0]

    df["nombre_caleta"] = df[name_col].astype(str)

    df["hover_text"] = df.apply(
        lambda row: f"<b>Caleta:</b> {row['nombre_caleta']}",
        axis=1
    )

    # Filtrar filas sin coordenadas válidas
    df = df.dropna(subset=["lat", "lon"])

    return df


amerb_gdf = cargar_amerbs_shp()
caletas_df = cargar_caletas_csv()


# ============================================================
# FUNCIONES PARA FIGURA
# ============================================================

def build_figure(show_polygons=True, show_amerb_points=True, show_caletas=True):
    """
    Crea la figura de Plotly con:
      - Polígonos AMERB (fill transparente)
      - Centroides AMERB
      - Puntos caletas
    """
    fig = go.Figure()

    # -------------------------
    # 1) Polígonos de AMERBs
    # -------------------------
    if show_polygons and not amerb_gdf.empty:
        for _, row in amerb_gdf.iterrows():
            geom = row.geometry
            hover_text = row["hover_text"]

            if geom is None:
                continue

            def add_polygon(poly):
                xs, ys = poly.exterior.coords.xy
                fig.add_trace(go.Scattermapbox(
                    lon=list(xs),
                    lat=list(ys),
                    mode="lines",
                    fill="toself",
                    fillcolor=COLOR_AMERB_FILL,
                    line=dict(color=COLOR_AMERB_LINE, width=1),
                    opacity=0.5,
                    hoverinfo="text",
                    text=hover_text,
                    showlegend=False,
                ))

            if geom.geom_type == "Polygon":
                add_polygon(geom)
            elif geom.geom_type == "MultiPolygon":
                for poly in geom.geoms:
                    add_polygon(poly)

    # -------------------------
    # 2) Centroides AMERBs
    # -------------------------
    if show_amerb_points and not amerb_gdf.empty:
        fig.add_trace(go.Scattermapbox(
            lat=amerb_gdf["lat"],
            lon=amerb_gdf["lon"],
            mode="markers",
            marker=dict(
                size=7,
                color=COLOR_AMERB_POINT,
                opacity=0.9,
            ),
            text=amerb_gdf["hover_text"],
            hoverinfo="text",
            name="AMERB (centroide)"
        ))

    # -------------------------
    # 3) Puntos de caletas
    # -------------------------
    if show_caletas and not caletas_df.empty:
        fig.add_trace(go.Scattermapbox(
            lat=caletas_df["lat"],
            lon=caletas_df["lon"],
            mode="markers",
            marker=dict(
                size=8,
                color=COLOR_CALETAS,
                opacity=0.9,
                symbol="circle",
            ),
            text=caletas_df["hover_text"],
            hoverinfo="text",
            name="Caletas"
        ))

    # -------------------------
    # Layout del mapa
    # -------------------------
    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),
        hoverlabel=dict(font=dict(size=9)),
        legend=dict(
            bgcolor="rgba(255,255,255,0.8)",
            bordercolor="rgba(0,0,0,0.1)",
            borderwidth=1,
            x=0.01,
            y=0.01,
            xanchor="left",
            yanchor="bottom",
            font=dict(size=11),
        ),
        annotations=[
            dict(
                text="Instituto en Socio-Ecología Costera (SECOS) - Datacenter<br>"
                     "Fuente: AMERBs (Subpesca/Sernapesca), Caletas (Sernapesca)",
                x=0,
                xref="paper",
                y=1,
                yref="paper",
                showarrow=False,
                font=dict(size=10),
                xanchor='left'
            )
        ],
    )

    return fig


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

app = dash.Dash(__name__)

app.layout = html.Div(
    style={"fontFamily": "Arial, sans-serif"},
    children=[
        html.Div(
            [
                html.H1(
                    "Áreas de Manejo (AMERBs) y Caletas Artesanales",
                    style={"margin-bottom": "5px"}
                ),
                html.Div(
                    "Visualización conjunta de áreas de manejo (polígonos y centroides) y caletas Sernapesca",
                    style={"fontSize": "13px", "color": "#555"}
                ),
            ],
            style={"padding": "10px 20px"}
        ),

        # Controles
        html.Div(
            [
                html.Label("Capas a mostrar:", style={"fontWeight": "bold", "marginRight": "10px"}),
                dcc.Checklist(
                    id="layer-checklist",
                    options=[
                        {"label": " Polígonos AMERB", "value": "polygons"},
                        {"label": " Centroides AMERB", "value": "amerb_points"},
                        {"label": " Caletas", "value": "caletas"},
                    ],
                    value=["polygons", "amerb_points", "caletas"],
                    inline=True,
                    style={"fontSize": "13px"},
                ),
            ],
            style={"padding": "0 20px 10px 20px"}
        ),

        # Mapa
        html.Div(
            dcc.Graph(id="map-amerb-caletas", config={"scrollZoom": True}),
            style={"height": "85vh", "padding": "0 10px 10px 10px"},
        ),
    ]
)


@app.callback(
    Output("map-amerb-caletas", "figure"),
    Input("layer-checklist", "value"),
)
def update_map(layers):
    show_polygons = "polygons" in layers
    show_amerb_points = "amerb_points" in layers
    show_caletas = "caletas" in layers
    fig = build_figure(
        show_polygons=show_polygons,
        show_amerb_points=show_amerb_points,
        show_caletas=show_caletas,
    )
    return fig


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

if __name__ == "__main__":
    app.run(
        host="127.0.0.1",
        port=8053,
        debug=False
    )