import SwiftUI

// MARK: - Форма линии

/// Сглаженная ломаная по точкам; `closed` замыкает контур вниз для заливки.
private struct SparkShape: Shape {
    let points: [CGPoint]
    let closed: Bool

    func path(in rect: CGRect) -> Path {
        var path = Path()
        guard points.count > 1 else { return path }

        path.move(to: points[0])
        for index in 0..<(points.count - 1) {
            let current = points[index]
            let next = points[index + 1]
            let mid = CGPoint(x: (current.x + next.x) / 2, y: (current.y + next.y) / 2)
            if index == 0 {
                path.addLine(to: mid)
            } else {
                path.addQuadCurve(to: mid, control: current)
            }
        }
        path.addLine(to: points[points.count - 1])

        if closed {
            path.addLine(to: CGPoint(x: points[points.count - 1].x, y: rect.maxY))
            path.addLine(to: CGPoint(x: points[0].x, y: rect.maxY))
            path.closeSubpath()
        }
        return path
    }
}

// MARK: - Спарклайн

/// График в стиле макета: линия с горизонтальным градиентом (яркая точка
/// на 77%), свечение-заливка под линией и «живая» точка на конце.
struct Sparkline: View {
    let values: [Double]
    let positive: Bool
    var lineWidth: CGFloat = 2
    var showPulse: Bool = true
    var pulseSize: CGFloat = 16
    var areaOpacity: Double = 0.25
    /// Отступ справа в диаметрах точки: линия обрывается, не доходя до края.
    var trailingRoom: CGFloat = 1.4

    /// Больше точек глазом уже не различить, а форма тяжелеет.
    private static let maxPoints = 96

    var body: some View {
        GeometryReader { geo in
            let points = points(in: geo.size)
            ZStack(alignment: .topLeading) {
                // Заливка: вертикальный уход в прозрачность + горизонтальная
                // маска — свечение сосредоточено слева, как в макете.
                SparkShape(points: points, closed: true)
                    .fill(
                        LinearGradient(
                            colors: [
                                RapiraPalette.line(positive).opacity(areaOpacity),
                                RapiraPalette.line(positive).opacity(0),
                            ],
                            startPoint: .top, endPoint: .bottom
                        )
                    )
                    .mask(
                        LinearGradient(
                            colors: [.white, .white.opacity(0.25)],
                            startPoint: .leading, endPoint: .trailing
                        )
                    )

                // Гало точки лежит под линией, ядро — над ней.
                if showPulse, let last = points.last {
                    Circle()
                        .fill(RapiraPalette.halo(positive))
                        .frame(width: pulseSize, height: pulseSize)
                        .position(last)
                }

                SparkShape(points: points, closed: false)
                    .stroke(
                        LinearGradient(
                            gradient: Gradient(stops: [
                                .init(color: RapiraPalette.lineEdge(positive), location: 0),
                                .init(color: RapiraPalette.line(positive), location: 0.77),
                                .init(color: RapiraPalette.lineEdge(positive), location: 1),
                            ]),
                            startPoint: .leading, endPoint: .trailing
                        ),
                        style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round)
                    )

                if showPulse, let last = points.last {
                    Circle()
                        .fill(RapiraPalette.line(positive))
                        .frame(width: pulseSize * 0.487, height: pulseSize * 0.487)
                        .position(last)
                }
            }
        }
    }

    // MARK: - Геометрия

    private func points(in size: CGSize) -> [CGPoint] {
        let series = downsampled(values, limit: Self.maxPoints)
        guard series.count > 1, size.width > 0, size.height > 0 else { return [] }

        let trailingInset = showPulse ? pulseSize * trailingRoom : lineWidth / 2
        let verticalPad = max(lineWidth, showPulse ? pulseSize / 2 : lineWidth)
        let width = max(size.width - trailingInset, 1)
        let height = max(size.height - verticalPad * 2, 1)

        let low = series.min() ?? 0
        let high = series.max() ?? 1
        let span = high - low

        return series.enumerated().map { index, value in
            let x = width * CGFloat(index) / CGFloat(series.count - 1)
            let normalized = span > 0 ? (value - low) / span : 0.5
            let y = verticalPad + height * (1 - CGFloat(normalized))
            return CGPoint(x: x, y: y)
        }
    }

    private func downsampled(_ values: [Double], limit: Int) -> [Double] {
        guard values.count > limit else { return values }
        let step = Double(values.count - 1) / Double(limit - 1)
        return (0..<limit).map { values[Int((Double($0) * step).rounded())] }
    }
}
