//
//  OneSongWaveformView.swift
//  vibes
//
//  Reusable waveform visualization component for One Song
//

import SwiftUI

struct OneSongWaveformView: View {
    let isAnimating: Bool
    let progress: Double
    let waveformSeed: Double
    let playbackProgress: Double

    @State private var frozenPhase: Double = 0
    @State private var lastAnimatingState: Bool = false

    var body: some View {
        ZStack {
            // Base waveform
            TimelineView(.animation(minimumInterval: 1/60, paused: false)) { timeline in
                Canvas { context, size in
                    let currentTime = timeline.date.timeIntervalSinceReferenceDate

                    // Capture phase at the exact moment we stop animating
                    let basePhase: Double
                    if isAnimating {
                        basePhase = currentTime
                        // Continuously update frozen phase while animating so it's ready
                        DispatchQueue.main.async {
                            frozenPhase = currentTime
                            lastAnimatingState = true
                        }
                    } else {
                        // Use the frozen phase that was captured at the last animating frame
                        basePhase = frozenPhase
                        if lastAnimatingState {
                            // Just transitioned to not animating, ensure we have the right phase
                            DispatchQueue.main.async {
                                lastAnimatingState = false
                            }
                        }
                    }

                    // Center outward formation clarity
                    let clarityProgress = progress

                    drawWaveform(context: context, size: size, phase: basePhase, clarity: clarityProgress, useOrange: false)
                }
            }
            .blur(radius: (1.0 - progress) * 10.0)  // Center outward blur effect

            // Orange gradient overlay (masked to playback progress)
            if playbackProgress > 0 {
                TimelineView(.animation(minimumInterval: 1/60, paused: false)) { timeline in
                    Canvas { context, size in
                        let currentTime = timeline.date.timeIntervalSinceReferenceDate
                        let basePhase = isAnimating ? currentTime : frozenPhase
                        let clarityProgress = progress

                        drawWaveform(context: context, size: size, phase: basePhase, clarity: clarityProgress, useOrange: true)
                    }
                }
                .blur(radius: (1.0 - progress) * 10.0)
                .mask(
                    Rectangle()
                        .frame(width: .infinity, height: .infinity)
                        .frame(maxWidth: .infinity, alignment: .leading)
                        .scaleEffect(x: playbackProgress, y: 1, anchor: .leading)
                )
            }
        }
    }

    private func drawWaveform(context: GraphicsContext, size: CGSize, phase: Double, clarity: Double, useOrange: Bool) {
        let centerY = size.height / 2
        let waveHeight = size.height * 0.06

        var path = Path()
        let points = 160

        // Calculate top curve
        for i in 0..<points {
            let x = (Double(i) / Double(points - 1)) * size.width
            let normalizedX = Double(i) / Double(points - 1)

            let amplitude = calculateAmplitude(x: x, normalizedX: normalizedX, waveHeight: waveHeight, size: size, phase: phase)

            // Center outward clarity
            let distanceFromCenter = abs(normalizedX - 0.5) * 2.0
            let localClarity = max(0, 1.0 - abs(distanceFromCenter - clarity) / 0.3)

            // Add noise based on clarity
            let noiseAmount = (1.0 - localClarity) * size.height * 0.08
            let seed1 = sin(x * 0.1)
            let seed2 = cos(x * 0.07)
            let noise = (seed1 + seed2 * 0.5) * noiseAmount

            let finalAmplitude = amplitude + noise
            let finalY = centerY - finalAmplitude

            if i == 0 {
                path.move(to: CGPoint(x: x, y: finalY))
            } else {
                path.addLine(to: CGPoint(x: x, y: finalY))
            }
        }

        // Draw bottom curve (mirror)
        for i in stride(from: points - 1, through: 0, by: -1) {
            let x = (Double(i) / Double(points - 1)) * size.width
            let normalizedX = Double(i) / Double(points - 1)

            let amplitude = calculateAmplitude(x: x, normalizedX: normalizedX, waveHeight: waveHeight, size: size, phase: phase)

            // Center outward clarity
            let distanceFromCenter = abs(normalizedX - 0.5) * 2.0
            let localClarity = max(0, 1.0 - abs(distanceFromCenter - clarity) / 0.3)

            // Add noise
            let noiseAmount = (1.0 - localClarity) * size.height * 0.08
            let seed1 = sin(x * 0.1)
            let seed2 = cos(x * 0.07)
            let noise = (seed1 + seed2 * 0.5) * noiseAmount

            let finalAmplitude = amplitude + noise
            let finalY = centerY + finalAmplitude

            path.addLine(to: CGPoint(x: x, y: finalY))
        }

        path.closeSubpath()

        // Choose gradient based on useOrange flag
        let gradient: Gradient
        if useOrange {
            gradient = Gradient(colors: [
                Constants.Colors.Accent.orange.opacity(clarity * 1.0),
                Constants.Colors.Accent.orange.opacity(clarity * 1.0)
            ])
        } else {
            gradient = Gradient(colors: [
                Constants.Colors.Foreground.tertiary.opacity(clarity * 0.4),
                Constants.Colors.Foreground.tertiary.opacity(clarity * 0.2)
            ])
        }

        context.fill(path, with: .linearGradient(
            gradient,
            startPoint: CGPoint(x: size.width / 2, y: 0),
            endPoint: CGPoint(x: size.width / 2, y: size.height)
        ))

        // Subtle stroke (only on base layer)
        if !useOrange {
            context.stroke(path, with: .color(Constants.Colors.Foreground.tertiary.opacity(clarity * 0.3)), lineWidth: 1)
        }
    }

    private func calculateAmplitude(x: Double, normalizedX: Double, waveHeight: Double, size: CGSize, phase: Double) -> Double {
        // Use seed to create unique waveform variations
        let seedModifier = waveformSeed.truncatingRemainder(dividingBy: 1000.0) / 1000.0
        let frequency = 11.0 + (seedModifier * 6.0) // Vary between 11-17

        // Unique harmonic ratios based on seed
        let harmonic2Ratio = 3.2 + (seedModifier * 1.0) // 3.2-4.2
        let harmonic3Ratio = 5.0 + (seedModifier * 1.5) // 5.0-6.5

        // Multiple harmonics with seed variation
        let baseFreq = frequency * Double.pi * 2.0
        let baseWave = sin(normalizedX * baseFreq + phase)

        let harmonic2 = sin(normalizedX * frequency * Double.pi * harmonic2Ratio + phase * 1.3) * 0.4
        let harmonic3 = sin(normalizedX * frequency * Double.pi * harmonic3Ratio + phase * 0.8) * 0.25
        let positionMod = sin(normalizedX * Double.pi * 2.0 + phase * 0.5) * 0.15

        let combined = baseWave + harmonic2 + harmonic3 + positionMod

        // Audio envelope with seed variation
        let envelope = getAudioEnvelope(normalizedX: normalizedX, seedOffset: seedModifier)

        // Edge fade
        let edgeFade = calculateEdgeFade(normalizedX: normalizedX)

        return combined * waveHeight * edgeFade * envelope
    }

    private func calculateEdgeFade(normalizedX: Double) -> Double {
        let fadeWidth = 0.15

        if normalizedX < fadeWidth {
            let t = normalizedX / fadeWidth
            return t * t * (3.0 - 2.0 * t)
        } else if normalizedX > (1.0 - fadeWidth) {
            let t = (1.0 - normalizedX) / fadeWidth
            return t * t * (3.0 - 2.0 * t)
        } else {
            return 1.0
        }
    }

    private func getAudioEnvelope(normalizedX: Double, seedOffset: Double) -> Double {
        // Verse/chorus/bridge amplitude envelope with seed variation
        let chorusBoost = 0.8 + (seedOffset * 0.4) // Vary chorus intensity

        if normalizedX < 0.15 {
            return 0.3 + (normalizedX / 0.15) * 0.3
        } else if normalizedX < 0.30 {
            return 0.5 + (seedOffset * 0.2) // Varied verse level
        } else if normalizedX < 0.35 {
            return (0.5 + (seedOffset * 0.2)) + ((normalizedX - 0.30) / 0.05) * 0.4
        } else if normalizedX < 0.50 {
            return chorusBoost
        } else if normalizedX < 0.65 {
            return 0.45 + (seedOffset * 0.2)
        } else if normalizedX < 0.75 {
            return (0.45 + (seedOffset * 0.2)) + ((normalizedX - 0.65) / 0.10) * 0.5
        } else if normalizedX < 0.90 {
            return chorusBoost
        } else {
            return chorusBoost - ((normalizedX - 0.90) / 0.10) * 0.7
        }
    }
}

#Preview {
    OneSongWaveformView(isAnimating: false, progress: 1.0, waveformSeed: 42.0, playbackProgress: 0.5)
        .frame(height: 300)
        .background(Constants.Colors.Background.primary)
}
