"""
init-mater-project

Copyright (C) 2025 [Lauranne Sarribouette] <lauranne.sarribouette@univ-grenoble-alpes.fr>

SPDX-License-Identifier: LGPL-3.0-or-later
"""

from pathlib import Path
from typing import List, Tuple, Dict, Optional
import pandas as pd
import streamlit as st

from src.app import data


def render_sidebar(
    output_folder: Path,
    structure_mode: data.OutputStructure,
    simulations: Optional[List[str]] = None,
) -> Tuple[Optional[str], str, Dict[str, List[str]]]:
    """
    Render adaptive sidebar based on structure mode

    Returns:
        (selected_simulation, selected_variable, dimension_filters)
        selected_simulation is None in SINGLE_SIMULATION mode
    """
    with st.sidebar:
        st.header("MATER Outputs Visualisation App")
        st.markdown("---")

        selected_simulation = None

        if structure_mode == data.OutputStructure.MULTIPLE_SIMULATIONS:
            if not simulations:
                st.error("No simulations found")
                return None, "", {}

            st.subheader("Simulation")
            selected_simulation = st.selectbox(
                "Simulation",
                simulations,
                help="Select which simulation outputs to visualize",
                label_visibility="collapsed",
            )
            simulation_path = output_folder / selected_simulation

        elif structure_mode == data.OutputStructure.SINGLE_SIMULATION:
            simulation_path = output_folder
            st.info(f"📁 {output_folder.name}")

        else:
            st.error("Invalid structure detected")
            return None, "", {}

        variables = data.get_available_variables(simulation_path)

        if not variables:
            st.warning("No variables found in simulation")
            return selected_simulation, "", {}

        st.subheader("Variable")

        if "selected_variable" not in st.session_state:
            st.session_state["selected_variable"] = variables[0]

        selected_variable = st.selectbox(
            "Variable",
            variables,
            index=variables.index(st.session_state["selected_variable"])
            if st.session_state["selected_variable"] in variables
            else 0,
            help="Select variable to display",
            label_visibility="collapsed",
            key="variable_selectbox",
        )

        st.session_state["selected_variable"] = selected_variable

        if not selected_variable:
            st.info("👈 Select a variable")
            return selected_simulation, "", {}

        df = data.load_variable_data(simulation_path, selected_variable)

        if df.empty:
            st.error("Cannot load data. Check simulation outputs.")
            return selected_simulation, selected_variable, {}

        st.subheader("Dimension Filters")

        dimension_filters = render_dimension_filters(df)

        is_valid, error_msg = data.validate_dimension_filters(dimension_filters)
        if not is_valid:
            st.error(f"⚠️ {error_msg}")

        return selected_simulation, selected_variable, dimension_filters


def render_dimension_filters(df: pd.DataFrame) -> Dict[str, List[str]]:
    """
    Render dimension filters
    """
    dimensions = [name for name in df.index.names if name not in ["time", "variable"]]

    if not dimensions:
        return {}

    filters = {}

    for dim in dimensions:
        available_values = [
            str(v) for v in df.index.get_level_values(dim).unique().tolist()
        ]
        available_values = sorted(available_values)

        filter_key = f"filter_{dim}"

        if filter_key not in st.session_state:
            st.session_state[filter_key] = available_values.copy()

        current_selection = st.session_state[filter_key]
        n_selected = len(current_selection)
        n_total = len(available_values)

        col1, col2 = st.columns([4, 1])

        with col1:
            st.markdown(f"**{dim.replace('_', ' ').title()} ({n_selected}/{n_total})**")

        with col2:
            is_all_selected = n_selected == n_total
            button_type = "primary" if is_all_selected else "secondary"

            if st.button(
                "All", key=f"btn_all_{dim}", width="stretch", type=button_type
            ):
                if is_all_selected:
                    st.session_state[filter_key] = []
                else:
                    st.session_state[filter_key] = available_values.copy()
                st.rerun()

        selected = st.multiselect(
            f"{dim} values",
            available_values,
            key=filter_key,
            label_visibility="collapsed",
        )

        filters[dim] = selected

    return filters


def render_time_filter_for_tab(
    df: pd.DataFrame, mode: str = "range", key_suffix: str = ""
) -> Tuple[Optional[pd.Timestamp], Optional[pd.Timestamp]]:
    """
    Render time filter adapted for specific tab

    Args:
        df: DataFrame with time data
        mode: "range" for slider with range, "single" for single point selector
        key_suffix: Unique suffix for widget keys

    Returns:
        (start_time, end_time) for range mode
        (selected_time, selected_time) for single mode
    """
    if "time" not in df.index.names:
        return (None, None)

    times = df.index.get_level_values("time").unique().sort_values()

    if len(times) == 0:
        return (None, None)

    min_time = times.min()
    max_time = times.max()

    if mode == "single":
        st.markdown("**Time Point**")

        selected_time = st.selectbox(
            "Select time point",
            options=times.tolist(),
            format_func=lambda x: x.strftime("%Y")
            if hasattr(x, "strftime")
            else str(x),
            key=f"time_single_{key_suffix}",
            label_visibility="collapsed",
        )
        return (selected_time, selected_time)

    else:
        selected_range = st.slider(
            "Time range",
            min_value=min_time.to_pydatetime(),
            max_value=max_time.to_pydatetime(),
            value=(min_time.to_pydatetime(), max_time.to_pydatetime()),
            format="YYYY",
            key=f"time_range_{key_suffix}",
        )
        return (pd.Timestamp(selected_range[0]), pd.Timestamp(selected_range[1]))


def display_simulation_info(
    simulation_name: Optional[str],
    selected_variable: str,
    df: pd.DataFrame,
    structure_mode: data.OutputStructure,
    simulation_path: Path,
):
    """Display simulation metadata with variables and dimension lists"""
    st.markdown("## ℹ️ Simulation Information")

    if "time" in df.index.names:
        times = df.index.get_level_values("time").unique().sort_values()
        min_year = (
            times.min().year
            if hasattr(times.min(), "year")
            else int(str(times.min())[:4])
        )
        max_year = (
            times.max().year
            if hasattr(times.max(), "year")
            else int(str(times.max())[:4])
        )
        st.markdown(f"**Period:** {min_year} - {max_year}")

    st.markdown("---")

    all_variables = data.get_available_variables(simulation_path)
    st.markdown(f"**Variables** ({len(all_variables)} total)")

    st.markdown(", ".join(f"`{var}`" for var in sorted(all_variables)))

    st.markdown("---")

    st.markdown("**Dimensions**")

    dimensions = [name for name in df.index.names if name not in ["time"]]

    if dimensions:
        for dim in dimensions:
            values = df.index.get_level_values(dim).unique().tolist()
            sorted_values = sorted([str(v) for v in values])

            st.markdown(
                f"**{dim.replace('_', ' ').title()}** ({len(sorted_values)} values)"
            )
            st.markdown(", ".join(f"`{val}`" for val in sorted_values))
            st.markdown("")
    else:
        st.markdown("_No dimensions in current data_")


def display_filter_summary(dimension_filters: Dict[str, List[str]]):
    """Display summary of applied filters"""
    active_filters = dimension_filters

    if not active_filters:
        st.info("💡 No filters applied")
    else:
        st.markdown("**Active filters:**")
        for dim, values in active_filters.items():
            st.markdown(f"- **{dim}**: {', '.join(values)}")
