private/charts/Add-LineChartElements.ps1

function Add-LineChartElements {
    <#
    .SYNOPSIS
        Renders line chart elements onto a canvas with hover effects.
    #>

    param($Canvas, $Data, $Palette, [bool]$ShowValues, $XAxisLabel, $YAxisLabel)

    $width  = $Canvas.Width
    $height = $Canvas.Height
    $margin = 50
    $count  = $Data.Count

    $maxValue = ($Data | Measure-Object -Property Value -Maximum).Maximum
    if (!$maxValue) { $maxValue = 1 }

    $chartWidth  = $width - ($margin * 2)
    $chartHeight = $height - ($margin * 2)
    $pointGap    = $chartWidth / [math]::Max(1, $count - 1)

    # For large datasets, reduce point markers and labels
    $showDots       = $count -le 100
    $showDataLabels = $count -le 20
    $labelInterval  = [math]::Max(1, [math]::Ceiling($count / 10))
    $labelWidth     = [math]::Max(20, $pointGap - 4)

    $paletteEntry = $Palette[0]

    # Draw Y-axis
    $yAxis = [System.Windows.Shapes.Line]@{
        X1 = $margin; Y1 = $margin; X2 = $margin; Y2 = $height - $margin
        StrokeThickness = 1
    }
    $yAxis.SetResourceReference([System.Windows.Shapes.Shape]::StrokeProperty, 'ControlForegroundBrush')
    [void]$Canvas.Children.Add($yAxis)

    # Draw X-axis
    $xAxis = [System.Windows.Shapes.Line]@{
        X1 = $margin; Y1 = $height - $margin; X2 = $width - $margin; Y2 = $height - $margin
        StrokeThickness = 1
    }
    $xAxis.SetResourceReference([System.Windows.Shapes.Shape]::StrokeProperty, 'ControlForegroundBrush')
    [void]$Canvas.Children.Add($xAxis)

    # Build polyline points first
    $points = [System.Windows.Media.PointCollection]::new()
    for ($i = 0; $i -lt $count; $i++) {
        $item = $Data[$i]
        $x    = $margin + ($i * $pointGap)
        $y    = $height - $margin - (($item.Value / $maxValue) * $chartHeight)
        [void]$points.Add([System.Windows.Point]::new($x, $y))
    }

    # Draw area fill under the line for visual depth
    if ($count -gt 1) {
        $areaFigure            = [System.Windows.Media.PathFigure]::new()
        $areaFigure.StartPoint = [System.Windows.Point]::new($margin, $height - $margin)
        $areaFigure.IsClosed   = $true

        foreach ($pt in $points) {
            [void]$areaFigure.Segments.Add([System.Windows.Media.LineSegment]::new($pt, $true))
        }
        [void]$areaFigure.Segments.Add([System.Windows.Media.LineSegment]::new(
            [System.Windows.Point]::new($points[$count - 1].X, $height - $margin), $true))

        $areaGeometry = [System.Windows.Media.PathGeometry]::new()
        [void]$areaGeometry.Figures.Add($areaFigure)

        $areaPath = [System.Windows.Shapes.Path]@{
            Data    = $areaGeometry
            Opacity = 0.15
        }
        Set-ChartShapeFill -Shape $areaPath -PaletteEntry $paletteEntry
        [void]$Canvas.Children.Add($areaPath)
    }

    # Draw the connecting line
    $polyline = [System.Windows.Shapes.Polyline]@{
        Points          = $points
        StrokeThickness = 2
        StrokeLineJoin  = 'Round'
    }
    if ($paletteEntry.ResourceKey) {
        $polyline.SetResourceReference([System.Windows.Shapes.Shape]::StrokeProperty, $paletteEntry.ResourceKey)
    }
    else {
        $polyline.Stroke = [System.Windows.Media.BrushConverter]::new().ConvertFrom($paletteEntry.Fallback)
    }
    [void]$Canvas.Children.Add($polyline)

    # Draw point markers and labels (after line so they appear on top)
    for ($i = 0; $i -lt $count; $i++) {
        $item = $Data[$i]
        $x    = $points[$i].X
        $y    = $points[$i].Y

        # Point marker with hover effect (skip for huge datasets)
        if ($showDots) {
            $dot = [System.Windows.Shapes.Ellipse]@{
                Width  = 8
                Height = 8
                Cursor = [System.Windows.Input.Cursors]::Hand
            }
            Set-ChartShapeFill -Shape $dot -PaletteEntry $paletteEntry
            $dot.Stroke          = [System.Windows.Media.Brushes]::White
            $dot.StrokeThickness = 1.5
            [System.Windows.Controls.Canvas]::SetLeft($dot, $x - 4)
            [System.Windows.Controls.Canvas]::SetTop($dot, $y - 4)

            # Tooltip with label and value
            $dot.ToolTip = "$($item.Label): $([math]::Round($item.Value, 2))"

            # Hover effect: grow the point
            $dot.Add_MouseEnter({
                param($sender, $eventArgs)
                $sender.Width  = 12
                $sender.Height = 12
                [System.Windows.Controls.Canvas]::SetLeft($sender, [System.Windows.Controls.Canvas]::GetLeft($sender) - 2)
                [System.Windows.Controls.Canvas]::SetTop($sender, [System.Windows.Controls.Canvas]::GetTop($sender) - 2)
            }.GetNewClosure())

            $dot.Add_MouseLeave({
                param($sender, $eventArgs)
                $sender.Width  = 8
                $sender.Height = 8
                [System.Windows.Controls.Canvas]::SetLeft($sender, [System.Windows.Controls.Canvas]::GetLeft($sender) + 2)
                [System.Windows.Controls.Canvas]::SetTop($sender, [System.Windows.Controls.Canvas]::GetTop($sender) + 2)
            }.GetNewClosure())

            [void]$Canvas.Children.Add($dot)
        }

        # X-axis labels at intervals in ViewBox for auto-shrinking
        if ($i % $labelInterval -eq 0) {
            $label = [System.Windows.Controls.TextBlock]@{
                Text       = $item.Label
                FontSize   = 13
                FontWeight = 'Medium'
            }
            $label.SetResourceReference([System.Windows.Controls.TextBlock]::ForegroundProperty, 'ControlForegroundBrush')

            $viewBox = [System.Windows.Controls.Viewbox]@{
                Width            = $labelWidth
                Height           = 20
                Stretch          = 'Uniform'
                StretchDirection = 'DownOnly'
                Child            = $label
            }
            [System.Windows.Controls.Canvas]::SetLeft($viewBox, $x - ($labelWidth / 2))
            [System.Windows.Controls.Canvas]::SetTop($viewBox, $height - $margin + 3)
            [void]$Canvas.Children.Add($viewBox)
        }

        # Value labels with slope-aware positioning to avoid line clipping
        if ($ShowValues -and $showDataLabels) {
            $valueLabel = [System.Windows.Controls.TextBlock]@{
                Text       = [string][math]::Round($item.Value, 1)
                FontSize   = 11
                FontWeight = 'Medium'
            }
            $valueLabel.SetResourceReference([System.Windows.Controls.TextBlock]::ForegroundProperty, 'ControlForegroundBrush')

            # Determine slope direction by comparing to neighbors
            $prevValue  = if ($i -gt 0) { $Data[$i - 1].Value } else { $item.Value }
            $nextValue  = if ($i -lt $count - 1) { $Data[$i + 1].Value } else { $item.Value }
            $isLocalMax = $item.Value -ge $prevValue -and $item.Value -ge $nextValue
            $isLocalMin = $item.Value -le $prevValue -and $item.Value -le $nextValue
            $isRising   = $prevValue -lt $item.Value

            # Horizontal offset: first point right, last point left, peaks centered
            $labelX = if ($i -eq 0) { $x + 6 }
                      elseif ($i -eq $count - 1) { $x - 20 }
                      elseif ($isLocalMax -or $isLocalMin) { $x - 8 }
                      elseif ($isRising) { $x - 14 }
                      else { $x + 2 }

            # Vertical offset: local minima go well below the point to clear line segments
            $placeBelow = $isLocalMin -or ($i -eq $count - 1 -and $item.Value -lt $prevValue)
            $labelY = if ($placeBelow) { $y + 12 } else { $y - 14 }

            [System.Windows.Controls.Canvas]::SetLeft($valueLabel, $labelX)
            [System.Windows.Controls.Canvas]::SetTop($valueLabel, $labelY)
            [void]$Canvas.Children.Add($valueLabel)
        }
    }

    Add-ChartYAxisTicks -Canvas $Canvas -MaxValue $maxValue -ChartHeight $chartHeight -Margin $margin -Height $height
    Add-ChartAxisLabels -Canvas $Canvas -Width $width -Height $height -Margin $margin -XAxisLabel $XAxisLabel -YAxisLabel $YAxisLabel
}