"""Bar chart figure factories for dashboard visualizations."""

from typing import Any

import pandas as pd
import plotly.express as px
import plotly.graph_objects as go


def create_ranking_bar(
    data: list[dict[str, Any]],
    name_column: str,
    value_column: str,
    title: str | None = None,
    top_n: int = 10,
    bottom_n: int = 10,
    color_top: str = "#4CAF50",
    color_bottom: str = "#F44336",
    value_format: str = ",.0f",
) -> go.Figure:
    """Create horizontal bar chart showing top and bottom rankings.

    Args:
        data: List of data records.
        name_column: Column name for labels.
        value_column: Column name for values.
        title: Optional chart title.
        top_n: Number of top items to show.
        bottom_n: Number of bottom items to show.
        color_top: Color for top performers.
        color_bottom: Color for bottom performers.
        value_format: Number format string for values.

    Returns:
        Plotly Figure object.
    """
    if not data:
        return _create_empty_figure(title or "Rankings")

    df = pd.DataFrame(data).sort_values(value_column, ascending=False)

    # Get top and bottom
    top_df = df.head(top_n).copy()
    bottom_df = df.tail(bottom_n).copy()

    top_df["group"] = "Top"
    bottom_df["group"] = "Bottom"

    # Combine with gap in the middle
    combined = pd.concat([top_df, bottom_df])
    combined["color"] = combined["group"].map(
        {"Top": color_top, "Bottom": color_bottom}
    )

    fig = go.Figure()

    # Add top bars
    fig.add_trace(
        go.Bar(
            y=top_df[name_column],
            x=top_df[value_column],
            orientation="h",
            marker_color=color_top,
            name="Top",
            text=top_df[value_column].apply(lambda x: f"{x:{value_format}}"),
            textposition="auto",
            hovertemplate=f"%{{y}}<br>{value_column}: %{{x:{value_format}}}<extra></extra>",
        )
    )

    # Add bottom bars
    fig.add_trace(
        go.Bar(
            y=bottom_df[name_column],
            x=bottom_df[value_column],
            orientation="h",
            marker_color=color_bottom,
            name="Bottom",
            text=bottom_df[value_column].apply(lambda x: f"{x:{value_format}}"),
            textposition="auto",
            hovertemplate=f"%{{y}}<br>{value_column}: %{{x:{value_format}}}<extra></extra>",
        )
    )

    fig.update_layout(
        title=title,
        barmode="group",
        showlegend=True,
        legend={"orientation": "h", "yanchor": "bottom", "y": 1.02},
        paper_bgcolor="rgba(0,0,0,0)",
        plot_bgcolor="rgba(0,0,0,0)",
        font_color="#c9c9c9",
        xaxis={"gridcolor": "rgba(128,128,128,0.2)", "title": None},
        yaxis={"autorange": "reversed", "title": None},
        margin={"l": 10, "r": 10, "t": 40, "b": 10},
    )

    return fig


def create_stacked_bar(
    data: list[dict[str, Any]],
    x_column: str,
    value_column: str,
    category_column: str,
    title: str | None = None,
    color_map: dict[str, str] | None = None,
    show_percentages: bool = False,
) -> go.Figure:
    """Create stacked bar chart for breakdown visualizations.

    Args:
        data: List of data records.
        x_column: Column name for x-axis categories.
        value_column: Column name for values.
        category_column: Column name for stacking categories.
        title: Optional chart title.
        color_map: Mapping of category to color.
        show_percentages: Whether to normalize to 100%.

    Returns:
        Plotly Figure object.
    """
    if not data:
        return _create_empty_figure(title or "Breakdown")

    df = pd.DataFrame(data)

    # Default color scheme
    if color_map is None:
        categories = df[category_column].unique()
        colors = px.colors.qualitative.Set2[: len(categories)]
        color_map = dict(zip(categories, colors, strict=False))

    fig = px.bar(
        df,
        x=x_column,
        y=value_column,
        color=category_column,
        color_discrete_map=color_map,
        barmode="stack",
        text=value_column if not show_percentages else None,
    )

    if show_percentages:
        fig.update_traces(texttemplate="%{y:.1f}%", textposition="inside")

    fig.update_layout(
        title=title,
        paper_bgcolor="rgba(0,0,0,0)",
        plot_bgcolor="rgba(0,0,0,0)",
        font_color="#c9c9c9",
        xaxis={"gridcolor": "rgba(128,128,128,0.2)", "title": None},
        yaxis={"gridcolor": "rgba(128,128,128,0.2)", "title": None},
        legend={"orientation": "h", "yanchor": "bottom", "y": 1.02},
        margin={"l": 10, "r": 10, "t": 60, "b": 10},
    )

    return fig


def create_horizontal_bar(
    data: list[dict[str, Any]],
    name_column: str,
    value_column: str,
    title: str | None = None,
    color: str = "#2196F3",
    value_format: str = ",.0f",
    sort: bool = True,
) -> go.Figure:
    """Create simple horizontal bar chart.

    Args:
        data: List of data records.
        name_column: Column name for labels.
        value_column: Column name for values.
        title: Optional chart title.
        color: Bar color.
        value_format: Number format string.
        sort: Whether to sort by value descending.

    Returns:
        Plotly Figure object.
    """
    if not data:
        return _create_empty_figure(title or "Bar Chart")

    df = pd.DataFrame(data)

    if sort:
        df = df.sort_values(value_column, ascending=True)

    fig = go.Figure(
        go.Bar(
            y=df[name_column],
            x=df[value_column],
            orientation="h",
            marker_color=color,
            text=df[value_column].apply(lambda x: f"{x:{value_format}}"),
            textposition="outside",
            hovertemplate=f"%{{y}}<br>Value: %{{x:{value_format}}}<extra></extra>",
        )
    )

    fig.update_layout(
        title=title,
        paper_bgcolor="rgba(0,0,0,0)",
        plot_bgcolor="rgba(0,0,0,0)",
        font_color="#c9c9c9",
        xaxis={"gridcolor": "rgba(128,128,128,0.2)", "title": None},
        yaxis={"title": None},
        margin={"l": 10, "r": 10, "t": 40, "b": 10},
    )

    return fig


def _create_empty_figure(title: str) -> go.Figure:
    """Create an empty figure with a message."""
    fig = go.Figure()
    fig.add_annotation(
        text="No data available",
        xref="paper",
        yref="paper",
        x=0.5,
        y=0.5,
        showarrow=False,
        font={"size": 14, "color": "#888888"},
    )
    fig.update_layout(
        title=title,
        paper_bgcolor="rgba(0,0,0,0)",
        plot_bgcolor="rgba(0,0,0,0)",
        font_color="#c9c9c9",
        xaxis={"visible": False},
        yaxis={"visible": False},
    )
    return fig
