"""Hyperparameter Space Visualization Section."""

import pandas as pd
import streamlit as st

from entropice.dashboard.plots.hyperparameter_space import (
    plot_parameter_correlations,
    plot_parameter_distributions,
    plot_parameter_interactions,
    plot_score_evolution,
    plot_score_vs_parameters,
)
from entropice.dashboard.utils.formatters import format_metric_name
from entropice.dashboard.utils.loaders import TrainingResult
from entropice.utils.training import HPOCV


def _render_performance_summary(results: pd.DataFrame, refit_metric: str):
    """Render performance summary subsection."""
    best_idx = results[f"mean_test_{refit_metric}"].idxmax()
    best_row = results.loc[best_idx]
    # Extract parameter columns
    param_cols = [col for col in results.columns if col.startswith("param_") and col != "params"]
    best_params = {col.replace("param_", ""): best_row[col] for col in param_cols}

    # Display best parameter combination
    if not best_params:
        return

    with st.container(border=True):
        st.subheader("🏆 Best Parameter Combination")
        st.caption(f"Parameters of the best model (selected by {format_metric_name(refit_metric)} score)")
        n_params = len(best_params)
        cols = st.columns(n_params)
        for idx, (param_name, param_value) in enumerate(best_params.items()):
            with cols[idx]:
                # Format value based on type and magnitude
                if isinstance(param_value, int):
                    formatted_value = f"{param_value:.0f}"
                elif isinstance(param_value, float):
                    # Use scientific notation for very small numbers
                    if abs(param_value) < 0.001 and param_value != 0:
                        formatted_value = f"{param_value:.2e}"
                    else:
                        formatted_value = f"{param_value:.4f}"
                else:
                    formatted_value = str(param_value)

                st.metric(param_name, formatted_value)


def _render_parameter_distributions(results: pd.DataFrame, param_grid: dict | None):
    """Render parameter distributions subsection."""
    st.subheader("Parameter Distributions")
    st.caption("Distribution of hyperparameter values explored during random search")

    param_charts = plot_parameter_distributions(results, param_grid)

    if not param_charts:
        st.info("No parameter distribution data available.")
        return

    # Display charts in a grid
    param_names = list(param_charts.keys())
    n_cols = min(3, len(param_names))
    n_rows = (len(param_names) + n_cols - 1) // n_cols

    for row in range(n_rows):
        cols = st.columns(n_cols)
        for col_idx in range(n_cols):
            param_idx = row * n_cols + col_idx
            if param_idx < len(param_names):
                param_name = param_names[param_idx]
                with cols[col_idx]:
                    st.plotly_chart(param_charts[param_name], width="stretch")


def _render_score_evolution(results: pd.DataFrame, selected_metric: str):
    """Render score evolution subsection."""
    st.subheader("Score Evolution Over Iterations")
    st.caption(f"How {format_metric_name(selected_metric)} evolved during the random search")

    evolution_chart = plot_score_evolution(results, selected_metric)
    if evolution_chart:
        st.plotly_chart(evolution_chart, width="stretch")
    else:
        st.warning(f"Score evolution not available for metric: {selected_metric}")


def _render_score_vs_parameters(results: pd.DataFrame, selected_metric: str, param_grid: dict | None):
    """Render score vs parameters subsection."""
    st.subheader("Score vs Individual Parameters")
    st.caption(f"Relationship between {format_metric_name(selected_metric)} and each hyperparameter")

    score_vs_param_charts = plot_score_vs_parameters(results, selected_metric, param_grid)

    if not score_vs_param_charts:
        st.info("No score vs parameter data available.")
        return

    param_names = list(score_vs_param_charts.keys())
    n_cols = min(2, len(param_names))
    n_rows = (len(param_names) + n_cols - 1) // n_cols

    for row in range(n_rows):
        cols = st.columns(n_cols)
        for col_idx in range(n_cols):
            param_idx = row * n_cols + col_idx
            if param_idx < len(param_names):
                param_name = param_names[param_idx]
                with cols[col_idx]:
                    st.plotly_chart(score_vs_param_charts[param_name], width="stretch")


def _render_parameter_correlations(results: pd.DataFrame, selected_metric: str):
    """Render parameter correlations subsection."""
    st.subheader("Parameter-Score Correlations")
    st.caption(f"Correlation between numeric parameters and {format_metric_name(selected_metric)}")

    corr_chart = plot_parameter_correlations(results, selected_metric)
    if corr_chart:
        st.plotly_chart(corr_chart, width="stretch")
    else:
        st.info("No numeric parameters found for correlation analysis.")


def _render_parameter_interactions(results: pd.DataFrame, selected_metric: str, param_grid: dict | None):
    """Render parameter interactions subsection."""
    st.subheader("Parameter Interactions")
    st.caption(f"Interaction between parameter pairs and their effect on {format_metric_name(selected_metric)}")

    interaction_charts = plot_parameter_interactions(results, selected_metric, param_grid)

    if not interaction_charts:
        st.info("Not enough numeric parameters for parameter interaction visualization.")
        return

    n_cols = min(2, len(interaction_charts))
    n_rows = (len(interaction_charts) + n_cols - 1) // n_cols

    for row in range(n_rows):
        cols = st.columns(n_cols)
        for col_idx in range(n_cols):
            chart_idx = row * n_cols + col_idx
            if chart_idx < len(interaction_charts):
                with cols[col_idx]:
                    st.plotly_chart(interaction_charts[chart_idx], width="stretch")


def render_hparam_space_section(selected_result: TrainingResult, selected_metric: str):
    """Render the hyperparameter space visualization section.

    Args:
        selected_result: The selected TrainingResult object.
        selected_metric: The metric to focus analysis on.

    """
    st.header("🧩 Hyperparameter Space Exploration")
    if selected_result.run.method_type != "HPOCV":
        st.warning("Hyperparameter space visualization is only available for RandomSearchCV runs.")
        return
    assert isinstance(selected_result.run.method, HPOCV), "Expected method to be HPOCV for HPOCV runs"

    cv_results = selected_result.run.cv_results
    assert cv_results is not None, "CV results should not be None for HPOCV runs"
    refit_metric = selected_result._get_best_metric_name()
    param_grid = selected_result.run.method.hpconfig

    _render_performance_summary(cv_results, refit_metric)

    _render_parameter_distributions(cv_results, param_grid)

    _render_score_evolution(cv_results, selected_metric)

    _render_score_vs_parameters(cv_results, selected_metric, param_grid)

    _render_parameter_correlations(cv_results, selected_metric)

    _render_parameter_interactions(cv_results, selected_metric, param_grid)
