"""Dynamic options panel based on selected figure type."""
from nicegui import ui

from .palette_picker import palette_picker

# Figure types and their field requirements (keys match canopy JSON registry)
# Order determines dropdown order; time_series first
FIGURE_TYPES = {
    "time_series": (1, None),  # 1 or more
    "map_simple": (1, 1),       # (min, max) fields
    "map_diff": (2, 2),
    "static": (2, 2),
    "distribution": (1, None),
    "latitudinal_plot": (1, None),
}

# Display labels for figure types
FIGURE_LABELS = {
    "time_series": "Time-series",
    "map_simple": "Simple map",
    "map_diff": "Difference map",
    "static": "Static plot",
    "distribution": "Distribution plot",
    "latitudinal_plot": "Latitudinal plot",
}

PROJECTIONS = [
    "EuroPP", "PlateCarree", "Mercator", "Robinson", "AlbersEqualArea",
    "LambertConformal", "LambertCylindrical", "Miller", "Mollweide",
    "Orthographic", "Sinusoidal", "Stereographic", "TransverseMercator",
    "UTM", "InterruptedGoodeHomolosine", "RotatedPole", "OSGB",
    "Geostationary", "NearsidePerspective", "LambertAzimuthalEqualArea",
    "NorthPolarStereo", "OSNI", "SouthPolarStereo",
]


# -----------------------------------------------------------------------------
# Public API
# -----------------------------------------------------------------------------

def options_panel(
    container: ui.element,
    state: dict,
    n_fields: int,
    selected_figure: str,
    on_figure_change,
    *,
    toggle_ref: dict | None = None,
    sub_panels: dict | None = None,
) -> None:
    """Render main figure toggle and dynamic sub-options panel.

    When toggle_ref and sub_panels are provided, uses visibility toggling instead
    of refresh to avoid NiceGUI #5765 (Vue props undefined in beforeUnmount).
    """
    with container:
        _add_preprocess_options(state)
        with ui.column().classes("w-full gap-1"):
            ui.label("Figure type").classes("text-base font-semibold")
            enabled_options = get_enabled_figures(n_fields)
            value = resolve_selected_figure(selected_figure, enabled_options)
            select = ui.select(
                enabled_options,
                value=value,
                label="",
            ).classes("w-full")
            select.on_value_change(lambda: on_figure_change(select.value))
            if toggle_ref is not None:
                toggle_ref["toggle"] = select

        figures_to_build = (
            list(FIGURE_TYPES) if sub_panels is not None
            else ([selected_figure] if selected_figure else [])
        )
        if figures_to_build:
            with ui.expansion("Function options", icon="palette").classes("w-full mt-2"):
                for fig in figures_to_build:
                    sub_container = ui.column().classes("w-full gap-2")
                    if sub_panels is not None:
                        sub_panels[fig] = sub_container
                    _add_figure_options(
                        sub_container, fig, state,
                        toggle_ref=toggle_ref, n_fields=n_fields, selected_figure=selected_figure,
                    )
                    sub_container.set_visibility(fig == selected_figure)
        with ui.expansion("Plot options", icon="image").classes("w-full mt-2"):
            plot_options_container = ui.column().classes("w-full gap-2")
            _add_plot_options(plot_options_container, state)


def is_figure_enabled(figure: str, n_fields: int) -> bool:
    """Check if a figure type is enabled for the given number of fields."""
    min_f, max_f = FIGURE_TYPES[figure]
    if n_fields < min_f:
        return False
    if max_f is not None and n_fields > max_f:
        return False
    return True


def get_enabled_figures(n_fields: int) -> dict[str, str]:
    """Return {figure_id: label} for figures enabled with n_fields."""
    return {f: FIGURE_LABELS[f] for f in FIGURE_TYPES if is_figure_enabled(f, n_fields)}


def resolve_selected_figure(selected: str, enabled: dict[str, str]) -> str | None:
    """Return the valid selected figure, or first enabled, or None."""
    if selected in enabled:
        return selected
    return list(enabled)[0] if enabled else None


# -----------------------------------------------------------------------------
# Helpers
# -----------------------------------------------------------------------------


def _add_figure_options(
    container: ui.element,
    figure_type: str,
    state: dict,
    *,
    toggle_ref: dict | None = None,
    n_fields: int = 0,
    selected_figure: str = "",
) -> None:
    """Add figure-specific options to container."""
    if figure_type == "time_series":
        _options_time_series(
            container, state,
            toggle_ref=toggle_ref, n_fields=n_fields, selected_figure=selected_figure,
        )
    elif figure_type == "latitudinal_plot":
        _options_latitudinal_plot(
            container, state,
            toggle_ref=toggle_ref, n_fields=n_fields, selected_figure=selected_figure,
        )
    elif figure_type == "distribution":
        _options_distribution(
            container, state,
            toggle_ref=toggle_ref, n_fields=n_fields, selected_figure=selected_figure,
        )
    else:
        FIGURE_OPTIONS[figure_type](container, state)


def _add_preprocess_options(state: dict) -> None:
    """Pre-process field(s): Grid type + file_format, source, slices, region, etc."""
    with ui.expansion("Pre-process field(s)", icon="tune").classes("w-full"):
        # Grid type
        ui.select(
            ["lonlat", "sites"],
            value=state.get("grid_type", "lonlat"),
            label="Grid type",
        ).bind_value(state, "grid_type").classes("w-full")
        # File format
        ui.select(
            ["lpjg_annual", "lpjg_monthly", "fluxnet2015"],
            value=state.get("file_format") or "lpjg_annual",
            label="File format",
        ).bind_value(state, "file_format").classes("w-full")
        # Source
        ui.input(
            "Source", value=state.get("source", ""), 
            placeholder="e.g.: lpjguess:anpp"
            ).bind_value(state, "source").classes("w-full")
        # Time slice
        with ui.row():
            ui.number("Time start", value=state.get("time_slice_start"), placeholder="e.g. 1901").bind_value(state, "time_slice_start").classes("w-full")
            ui.number("Time end", value=state.get("time_slice_end"), placeholder="e.g. 2020").bind_value(state, "time_slice_end").classes("w-full")
        # Lat slice
        with ui.row():
            ui.number("Lat min", value=state.get("lat_slice_min"), placeholder="e.g. -90").bind_value(state, "lat_slice_min").classes("w-full")
            ui.number("Lat max", value=state.get("lat_slice_max"), placeholder="e.g. 90").bind_value(state, "lat_slice_max").classes("w-full")
        # Lon slice
        with ui.row():
            ui.number("Lon min", value=state.get("lon_slice_min"), placeholder="e.g. -180").bind_value(state, "lon_slice_min").classes("w-full")
            ui.number("Lon max", value=state.get("lon_slice_max"), placeholder="e.g. 180").bind_value(state, "lon_slice_max").classes("w-full")
        # Region type
        ui.select(
            ["country", "giorgi", "SREX", "AR6"],
            value=state.get("region_type", "country"),
            label="Region type",
        ).bind_value(state, "region_type").classes("w-full")
        # Region
        ui.input(
            "Region", value=state.get("region", ""), 
            placeholder="e.g.: Germany or NEU").bind_value(state, "region").classes("w-full")
        # Convert units
        ui.input(
            "Convert units (factor, units)", value=state.get("convert_units", ""), 
            placeholder="e.g.: 1e-12, GtC"
            ).bind_value(state, "convert_units").classes("w-full")
        # Filter
        ui.input(
            "Filter (pandas query)", value=state.get("filter", ""), 
            placeholder="e.g.: Total > 0.5"
            ).bind_value(state, "filter").classes("w-full")
        # Apply (op, operand, layers)
        with ui.row().classes("w-full gap-2"):
            ui.select(
                ["", "*", "+", "-", "/"],
                value=state.get("apply_op", ""),
                label="Apply op",
            ).bind_value(state, "apply_op").classes("flex-1")
            ui.number(
                "Operand", value=state.get("apply_operand"),
                placeholder="e.g.: 100",
            ).bind_value(state, "apply_operand").classes("flex-1")
        ui.input(
            "Apply layers (empty = all)", value=state.get("apply_layers", ""),
            placeholder="e.g.: Total or leave empty for all",
        ).bind_value(state, "apply_layers").classes("w-full")
        # Unite
        ui.checkbox("Unite", value=state.get("unite", False)).bind_value(state, "unite")


def _options_map_simple(container, state: dict) -> None:
    with container:
        # Layer
        ui.input(
            "Layer",
            value=state.get("layer", ""),
            placeholder="e.g.: Total",
        ).bind_value(state, "layer").classes("w-full")
        # Time operation
        ui.select(
            ["av", "sum"],
            value=state.get("timeop", "av"),
            label="Time operation",
        ).bind_value(state, "timeop").classes("w-full")
        # Palette
        palette_picker(value=state.get("palette", ""), label="Palette", state=state)
        # n_classes
        with ui.row():
            ui.slider(min=2, max=12, value=state.get("n_classes", 4)).bind_value(state, "n_classes")
            ui.label().bind_text_from(state, "n_classes", lambda v: f"Classes: {v}")
        # Classification
        ui.select(
            ["linear", "quantile", "jenks", "std"],
            value=state.get("classification", "linear"),
            label="Classification",
        ).bind_value(state, "classification").classes("w-full")
        # Colorbar orientation
        ui.select(
            ["horizontal", "vertical"],
            value=state.get("orientation", "horizontal"),
            label="Colorbar orientation",
        ).bind_value(state, "orientation").classes("w-full")
        # Colorbar extend
        ui.select(
            ["neither", "min", "max", "both"],
            value=state.get("extend", "neither"),
            label="Colorbar extend",
        ).bind_value(state, "extend").classes("w-full")
        # Colorbar label
        ui.input(
            "Colorbar label", value=state.get("cb_label", ""), 
            placeholder="e.g.: Carbon pool"
            ).bind_value(state, "cb_label").classes("w-full")
        # Unit
        ui.input(
            "Unit", value=state.get("unit", ""), 
            placeholder="e.g.: GtC"
            ).bind_value(state, "unit").classes("w-full")
        # Projection
        ui.select(
            PROJECTIONS,
            value=state.get("proj", "Robinson"),
            label="Projection",
        ).bind_value(state, "proj").classes("w-full")
        # Force zero
        ui.checkbox("Force zero", value=state.get("force_zero", False)).bind_value(state, "force_zero")
        # Stats annotation
        ui.checkbox("Stats annotation", value=state.get("stats_annotation", False)).bind_value(state, "stats_annotation")


def _options_map_diff(container, state: dict) -> None:
    with container:
        # Percentage
        ui.checkbox("Percentage", value=state.get("percentage", False)).bind_value(state, "percentage")
        # Mask non-significant
        ui.checkbox("Mask non-significant", value=state.get("nonsig", False)).bind_value(state, "nonsig")
    _options_map_simple(container, state)


def _options_time_series(
    container,
    state: dict,
    *,
    toggle_ref: dict | None = None,
    n_fields: int = 0,
    selected_figure: str = "",
) -> None:
    with container:
        # Make diff
        make_diff_container = ui.column().classes("w-full")
        with make_diff_container:
            ui.checkbox("Make diff", value=state.get("make_diff", False)).bind_value(state, "make_diff")
        if toggle_ref is not None:
            toggle_ref["make_diff_container"] = make_diff_container
            make_diff_container.set_visibility(n_fields == 2 and selected_figure == "time_series")
        # Field labels
        field_labels_container = ui.column().classes("w-full")
        with field_labels_container:
            ui.input(
                "Field labels", value=state.get("field_labels", ""), 
                placeholder="e.g.: SSP1, SSP2"
                ).bind_value(state, "field_labels").classes("w-full")
        if toggle_ref is not None:
            toggle_ref["field_labels_container"] = field_labels_container
            field_labels_container.set_visibility(n_fields > 1 and selected_figure == "time_series")
        # Layers
        ui.input(
            "Layers", value=state.get("layers", ""), 
            placeholder="e.g. Total or Total, VegC"
            ).bind_value(state, "layers").classes("w-full")
        # Grid operation
        ui.select(
            [None, "av", "sum"],
            value=state.get("gridop", None),
            label="Grid operation",
        ).bind_value(state, "gridop").classes("w-full")
        # Palette
        palette_picker(value=state.get("palette", ""), label="Palette", state=state)
        # Reverse hue
        ui.checkbox("Reverse hue and style", value=state.get("reverse_hue_style", False)).bind_value(state, "reverse_hue_style")
        # Stacked
        ui.checkbox("Stacked", value=state.get("stacked", False)).bind_value(state, "stacked")
        # Relative
        ui.checkbox("Relative", value=state.get("relative", False)).bind_value(state, "relative")
        # Rolling size
        ui.number(
            "Rolling size (number of years)", value=state.get("rolling_size"), 
            placeholder="e.g.: 10"
            ).bind_value(state, "rolling_size").classes("w-full")
        # Baseline
        ui.checkbox("Baseline", value=state.get("baseline", False)).bind_value(state, "baseline")
        # Y-axis limits
        ui.input(
            "Y-axis limits", value=state.get("yaxis_lim", ""),
            placeholder="e.g.: 0, 10"
        ).bind_value(state, "yaxis_lim").classes("w-full")
        # Y-axis label
        ui.input(
            "Y-axis label", value=state.get("yaxis_label", ""), 
            placeholder="e.g.: Carbon pool"
            ).bind_value(state, "yaxis_label").classes("w-full")
        # Unit
        ui.input(
            "Unit", value=state.get("unit", ""), 
            placeholder="e.g.: GtC"
            ).bind_value(state, "unit").classes("w-full")
        # Legend style
        ui.select(
            ["default", "highlighted", "end-of-line", "hidden"],
            value=state.get("legend_style", "default"),
            label="Legend style",
        ).bind_value(state, "legend_style").classes("w-full")
        # Move legend
        ui.checkbox("Move legend", value=state.get("move_legend", False)).bind_value(state, "move_legend")
        # Max labels per col
        ui.number("Max labels per col", value=state.get("max_labels_per_col", 15)).bind_value(state, "max_labels_per_col").classes("w-full")


def _options_static(container, state: dict) -> None:
    with container:
        # Field A label
        ui.input(
            "Field A label", value=state.get("field_a_label", ""), 
            placeholder="e.g.: SSP1"
            ).bind_value(state, "field_a_label").classes("w-full")
        # Field B label
        ui.input(
            "Field B label", value=state.get("field_b_label", ""),
             placeholder="e.g.: SSP3"
             ).bind_value(state, "field_b_label").classes("w-full")
        # Layers
        ui.input(
            "Layers", value=state.get("layers", ""), 
            placeholder="e.g.: Total or Total, VegC"
            ).bind_value(state, "layers").classes("w-full")
        # Palette
        palette_picker(value=state.get("palette", ""), label="Palette", state=state)
        # Unit A
        ui.input(
            "Unit A", value=state.get("unit_a", ""), 
            placeholder="e.g.: GtC"
            ).bind_value(state, "unit_a").classes("w-full")
        # Unit B
        ui.input(
            "Unit B", value=state.get("unit_b", ""), 
            placeholder="e.g.: GtC"
            ).bind_value(state, "unit_b").classes("w-full")
        # Scatter size
        with ui.row():
            ui.slider(min=1, max=20, value=state.get("scatter_size", 6)).bind_value(state, "scatter_size")
            ui.label().bind_text_from(state, "scatter_size", lambda v: f"Scatter size: {v}")
        # Scatter alpha
        with ui.row():
            ui.slider(min=0, max=1, step=0.1, value=state.get("scatter_alpha", 0.1)).bind_value(state, "scatter_alpha")
            ui.label().bind_text_from(state, "scatter_alpha", lambda v: f"Alpha: {v}")
        # Move legend
        ui.checkbox("Move legend", value=state.get("move_legend", False)).bind_value(state, "move_legend")


def _options_distribution(
    container,
    state: dict,
    *,
    toggle_ref: dict | None = None,
    n_fields: int = 0,
    selected_figure: str = "",
) -> None:
    with container:
        # Plot type
        ui.select(
            ["strip", "swarm", "box", "violin", "boxen", "point", "bar"],
            value=state.get("plot_type", "box"),
            label="Plot type",
        ).bind_value(state, "plot_type").classes("w-full")
        # Field labels (only when more than one field)
        field_labels_container = ui.column().classes("w-full")
        with field_labels_container:
            ui.input(
                "Field labels", value=state.get("field_labels", ""),
                placeholder="e.g.: SSP1, SSP3"
            ).bind_value(state, "field_labels").classes("w-full")
        if toggle_ref is not None:
            toggle_ref["distribution_field_labels_container"] = field_labels_container
            field_labels_container.set_visibility(n_fields > 1 and selected_figure == "distribution")
        # Layers
        ui.input(
            "Layers", value=state.get("layers", ""), 
            placeholder="e.g.: Total or Total, VegC"
            ).bind_value(state, "layers").classes("w-full")
        # Grid operation (default av for distribution)
        ui.select(
            ["av", "sum", None],
            value=state.get("gridop") if state.get("gridop") is not None else "av",
            label="Grid operation",
        ).bind_value(state, "gridop").classes("w-full")
        # Palette
        palette_picker(value=state.get("palette", ""), label="Palette", state=state)
        # Horizontal
        ui.checkbox("Horizontal", value=state.get("horizontal", False)).bind_value(state, "horizontal")
        # Vertical x-labels
        ui.checkbox("Vertical x-labels", value=state.get("vertical_xlabels", False)).bind_value(state, "vertical_xlabels")
        # Y-axis label
        ui.input(
            "Y-axis label", value=state.get("yaxis_label", ""), 
            placeholder="e.g.: Carbon pool"
            ).bind_value(state, "yaxis_label").classes("w-full")
        # Unit
        ui.input(
            "Unit", value=state.get("unit", ""), 
            placeholder="e.g.: GtC"
            ).bind_value(state, "unit").classes("w-full")
        # Move legend
        ui.checkbox("Move legend", value=state.get("move_legend", False)).bind_value(state, "move_legend")


def _options_latitudinal_plot(
    container,
    state: dict,
    *,
    toggle_ref: dict | None = None,
    n_fields: int = 0,
    selected_figure: str = "",
) -> None:
    with container:
        # Field labels (only when more than one field)
        field_labels_container = ui.column().classes("w-full")
        with field_labels_container:
            ui.input(
                "Field labels", value=state.get("field_labels", ""),
                placeholder="e.g.: SSP1, SSP3"
            ).bind_value(state, "field_labels").classes("w-full")
        if toggle_ref is not None:
            toggle_ref["latitudinal_field_labels_container"] = field_labels_container
            field_labels_container.set_visibility(n_fields > 1 and selected_figure == "latitudinal_plot")
        # Layers
        ui.input(
            "Layers", value=state.get("layers", ""), 
            placeholder="e.g.: Total or Total, VegC"
            ).bind_value(state, "layers").classes("w-full")
        # Palette
        palette_picker(value=state.get("palette", ""), label="Palette", state=state)
        # Y-axis label
        ui.input(
            "Y-axis label", value=state.get("yaxis_label", ""), 
            placeholder="e.g.: Carbon pool"
            ).bind_value(state, "yaxis_label").classes("w-full")
        # Unit
        ui.input(
            "Unit", value=state.get("unit", ""), 
            placeholder="e.g.: GtC"
            ).bind_value(state, "unit").classes("w-full")
        # Legend style
        ui.select(
            ["default", "highlighted", "end-of-line", "hidden"],
            value=state.get("legend_style", "default"),
            label="Legend style",
        ).bind_value(state, "legend_style").classes("w-full")
        # Move legend
        ui.checkbox("Move legend", value=state.get("move_legend", False)).bind_value(state, "move_legend")
        # Max labels per col
        ui.number("Max labels per col", value=state.get("max_labels_per_col", 15)).bind_value(state, "max_labels_per_col").classes("w-full")


def _add_plot_options(container, state: dict) -> None:
    """Add common options: title, palette, dark_mode, transparent, x_fig, y_fig."""
    with container:
        # Title
        ui.input(
            "Title", value=state.get("title", ""), 
            placeholder="e.g.: Global carbon pool"
            ).bind_value(state, "title").classes("w-full")
        # Dark mode
        with ui.row():
            ui.checkbox("Dark mode", value=state.get("dark_mode", False)).bind_value(state, "dark_mode")
            ui.checkbox("Transparent", value=state.get("transparent", False)).bind_value(state, "transparent")
        # Width
        with ui.row():
            ui.slider(min=5, max=20, value=state.get("x_fig", 10)).bind_value(state, "x_fig")
            ui.label().bind_text_from(state, "x_fig", lambda v: f"Width: {v}")
        # Height
        with ui.row():
            ui.slider(min=5, max=20, value=state.get("y_fig", 10)).bind_value(state, "y_fig")
            ui.label().bind_text_from(state, "y_fig", lambda v: f"Height: {v}")


FIGURE_OPTIONS = {
    "map_simple": _options_map_simple,
    "map_diff": _options_map_diff,
    "time_series": _options_time_series,
    "static": _options_static,
    "distribution": _options_distribution,
    "latitudinal_plot": _options_latitudinal_plot,
}
