"""Elevation profile visualization module using Plotly."""

import plotly.graph_objects as go


class ElevationProfileRenderer:
    """Render elevation profiles with gradient-based coloring."""

    def __init__(self):
        """Initialize elevation profile renderer."""
        pass

    def _downsample_data(self, distances, elevations, gradients, max_points=800):
        """Downsample data if there are too many points.

        Args:
            distances: List of distances
            elevations: List of elevations
            gradients: List of gradients
            max_points: Maximum number of points to keep

        Returns:
            tuple: (downsampled_distances, downsampled_elevations, downsampled_gradients)
        """
        if len(distances) <= max_points:
            return distances, elevations, gradients

        # Calculate step size for uniform downsampling
        step = len(distances) / max_points

        # Use floating point indexing for smoother sampling
        indices = []
        for i in range(max_points):
            idx = int(i * step)
            if idx not in indices and idx < len(distances):
                indices.append(idx)

        # Always include first and last points
        if 0 not in indices:
            indices.insert(0, 0)
        if (len(distances) - 1) not in indices:
            indices.append(len(distances) - 1)

        downsampled_distances = [distances[i] for i in indices]
        downsampled_elevations = [elevations[i] for i in indices]

        # Recalculate gradients for downsampled data
        downsampled_gradients = []
        for i in range(1, len(indices)):
            idx_prev = indices[i - 1]
            idx_curr = indices[i]
            if idx_prev < len(gradients) and idx_curr <= len(gradients):
                # Use average gradient of the skipped segments
                avg_gradient = sum(gradients[idx_prev:idx_curr]) / max(1, idx_curr - idx_prev)
                downsampled_gradients.append(avg_gradient)

        return downsampled_distances, downsampled_elevations, downsampled_gradients

    def create_elevation_profile(self, distances, elevations, gradients):
        """Create an interactive elevation profile chart.

        Args:
            distances: List of cumulative distances in km
            elevations: List of elevations in meters
            gradients: List of gradients (slopes) in percentage

        Returns:
            plotly.graph_objects.Figure: Interactive elevation chart
        """
        if not distances or not elevations or len(distances) != len(elevations):
            return None

        # Downsample if too many points
        distances, elevations, gradients = self._downsample_data(distances, elevations, gradients)

        fig = go.Figure()

        # Add gradient list for first point (use 0 as placeholder)
        gradients_extended = [0] + gradients

        # Group consecutive segments with similar color into single traces
        # This dramatically reduces the number of traces
        i = 0
        while i < len(distances) - 1:
            gradient = gradients_extended[i + 1]

            # Determine color based on gradient thresholds (earth-tone palette)
            if gradient > 0:  # Ascending
                if gradient >= 15:
                    # Steep ascent: dark red-brown (brick/terracotta)
                    color = 'rgb(139, 69, 19)'  # Saddle brown
                else:
                    # Gentle ascent: light red-brown (sandy/tan)
                    color = 'rgb(210, 180, 140)'  # Tan
                is_ascending = True
            else:  # Descending
                if gradient <= -20:
                    # Steep descent: dark green (forest green)
                    color = 'rgb(34, 139, 34)'  # Forest green
                else:
                    # Gentle descent: light green (sage/olive)
                    color = 'rgb(154, 205, 50)'  # Yellow green
                is_ascending = False

            # Collect consecutive points with similar direction (ascending/descending)
            segment_x = [distances[i]]
            segment_y = [elevations[i]]
            segment_gradients = []

            j = i
            while j < len(distances) - 1:
                next_gradient = gradients_extended[j + 1]
                # Check if same direction (both positive or both negative/zero)
                if (next_gradient > 0) == is_ascending:
                    segment_x.append(distances[j + 1])
                    segment_y.append(elevations[j + 1])
                    segment_gradients.append(next_gradient)
                    j += 1
                else:
                    break

            # Add segment as a single trace with fill
            avg_gradient = sum(segment_gradients) / len(segment_gradients) if segment_gradients else gradient

            # Create fill color with 50% opacity (rgba format)
            # Extract RGB values from the color string
            rgb_values = color.replace('rgb(', '').replace(')', '').split(',')
            r, g, b = int(rgb_values[0]), int(rgb_values[1]), int(rgb_values[2])
            fill_color = f'rgba({r},{g},{b},0.5)'

            fig.add_trace(
                go.Scatter(
                    x=segment_x,
                    y=segment_y,
                    mode='lines',
                    line=dict(color=color, width=3),
                    fill='tozeroy',  # Fill to y=0
                    fillcolor=fill_color,  # Semi-transparent fill
                    hovertemplate=f'距離: %{{x:.2f}} km<br>海拔: %{{y:.0f}} m<br>平均坡度: {avg_gradient:.1f}%<extra></extra>',
                    showlegend=False,
                )
            )

            i = j

        # Update layout
        fig.update_layout(
            title='海拔剖面圖',
            xaxis_title='距離 (公里)',
            yaxis_title='海拔 (公尺)',
            hovermode='closest',
            template='plotly_white',
            height=400,
            xaxis=dict(showgrid=True, gridcolor='lightgray'),
            yaxis=dict(showgrid=True, gridcolor='lightgray'),
        )

        return fig

    def calculate_gradient_distribution(self, distances, gradients):
        """Calculate distance distribution across different gradient categories.

        Args:
            distances: List of cumulative distances in km
            gradients: List of gradients in percentage

        Returns:
            dict: Dictionary with distances for each category
        """
        if len(distances) < 2 or len(gradients) < 1:
            return None

        steep_ascent_km = 0.0  # >= 15%
        gentle_ascent_km = 0.0  # 0% to 15%
        steep_descent_km = 0.0  # <= -20%
        gentle_descent_km = 0.0  # -20% to 0%

        for i in range(len(gradients)):
            if i + 1 < len(distances):
                segment_distance = distances[i + 1] - distances[i]
                gradient = gradients[i]

                if gradient >= 15:
                    steep_ascent_km += segment_distance
                elif gradient > 0:
                    gentle_ascent_km += segment_distance
                elif gradient <= -20:
                    steep_descent_km += segment_distance
                else:  # gradient < 0 and > -20
                    gentle_descent_km += segment_distance

        return {
            'steep_ascent': steep_ascent_km,
            'gentle_ascent': gentle_ascent_km,
            'steep_descent': steep_descent_km,
            'gentle_descent': gentle_descent_km,
        }

    def create_gradient_pie_chart(self, distances, gradients):
        """Create a pie chart showing gradient distribution.

        Args:
            distances: List of cumulative distances in km
            gradients: List of gradients in percentage

        Returns:
            plotly.graph_objects.Figure: Pie chart figure
        """
        stats = self.calculate_gradient_distribution(distances, gradients)

        if not stats:
            return None

        # Prepare data
        labels = ['陡上坡 (≥15%)', '緩上坡 (<15%)', '陡下坡 (≤-20%)', '緩下坡 (>-20%)']
        values = [stats['steep_ascent'], stats['gentle_ascent'], stats['steep_descent'], stats['gentle_descent']]
        colors = [
            'rgb(139, 69, 19)',  # Steep ascent - dark brown
            'rgb(210, 180, 140)',  # Gentle ascent - tan
            'rgb(34, 139, 34)',  # Steep descent - forest green
            'rgb(154, 205, 50)',  # Gentle descent - yellow green
        ]

        # Create pie chart
        fig = go.Figure(
            data=[
                go.Pie(
                    labels=labels,
                    values=values,
                    marker=dict(colors=colors),
                    textinfo='label+percent',
                    textposition='inside',
                    hovertemplate='%{label}<br>%{value:.2f} km<br>%{percent}<extra></extra>',
                    showlegend=False,  # Don't show legend as requested
                )
            ]
        )

        fig.update_layout(
            title='坡度分布',
            height=400,
            margin=dict(l=20, r=20, t=40, b=20),
        )

        return fig

    def add_gradient_legend(self, fig):
        """Add a custom legend explaining the color scheme.

        Args:
            fig: Plotly figure object

        Returns:
            plotly.graph_objects.Figure: Updated figure with legend
        """
        # Add dummy traces for legend
        fig.add_trace(
            go.Scatter(
                x=[None],
                y=[None],
                mode='lines',
                line=dict(color='rgb(139,69,19)', width=3),
                name='陡上坡 (≥15%)',
            )
        )

        fig.add_trace(
            go.Scatter(
                x=[None],
                y=[None],
                mode='lines',
                line=dict(color='rgb(210,180,140)', width=3),
                name='緩上坡 (<15%)',
            )
        )

        fig.add_trace(
            go.Scatter(
                x=[None],
                y=[None],
                mode='lines',
                line=dict(color='rgb(34,139,34)', width=3),
                name='陡下坡 (≤-20%)',
            )
        )

        fig.add_trace(
            go.Scatter(
                x=[None],
                y=[None],
                mode='lines',
                line=dict(color='rgb(154,205,50)', width=3),
                name='緩下坡 (>-20%)',
            )
        )

        fig.update_layout(showlegend=True, legend=dict(x=0.02, y=0.98))

        return fig
