import SwiftUI

/// Shared visual language for orchestra (orchestration) mode: the signature gold, a
/// pulsing composer glow, and the small effort-pill label. Kept together so the home chat
/// bar, the session composer, and the effort menus all read identically.
enum OrchestraStyle {
    /// SF Symbol that marks orchestra throughout the UI — the same sparkles used for the
    /// agent's thinking, so "extra intelligence" reads consistently.
    static let symbol = "sparkles"

    /// A warm gold gradient used for the glow stroke, hot corner to cool corner.
    static func strokeGradient(intensity: Double) -> LinearGradient {
        LinearGradient(
            colors: [
                AppTheme.orchestra.opacity(0.65 + 0.35 * intensity),
                AppTheme.orchestra.opacity(0.38 + 0.32 * intensity),
            ],
            startPoint: .topLeading, endPoint: .bottomTrailing)
    }
}

/// A bright golden halo that throbs around a view while orchestra is the active effort.
/// Mirrors `StreakBadge`'s idiom — a single `@State` flag toggled to kick off an
/// `easeInOut(...).repeatForever(autoreverses:)` pulse — so the animation feels native to the
/// app: a bold, high-amplitude swell on a slow beat, with a long pause between pulses (the
/// ease lingers at the baseline) so each one lands deliberately rather than strobing. The halo
/// crests bright and never collapses to nothing, so even at the trough it reads as "orchestra
/// is on", not a flicker. Honors Reduce Motion: the glow still shows, it just holds steady
/// instead of pulsing.
struct OrchestraGlow: ViewModifier {
    /// Whether orchestra is the selected effort. When false the modifier is inert (no glow,
    /// no animation), so a plain chat composer is untouched.
    var active: Bool
    var cornerRadius: CGFloat = 8
    /// How bright the halo is allowed to get at the crest of a pulse, as a fraction of the
    /// full home-screen swell (1.0 = unchanged). The in-chat composer caps this below 1 so the
    /// glow reads as a calm presence rather than a loud beacon while you're reading a chat; the
    /// resting baseline is untouched, so a lower cap only dims the peak of the throb.
    var peakBrightness: Double = 1.0

    @Environment(\.accessibilityReduceMotion) private var reduceMotion
    /// Toggled once the view is on screen (and whenever `active` flips), which starts —
    /// or, under Reduce Motion, simply settles — the repeating pulse.
    @State private var pulsing = false
    /// Set one runloop after the first layout so neither the fade nor the pulse can animate the
    /// glow *into place*. When a window is restored onto an orchestra session the composer's
    /// frame is still settling on the first render (the window resizes from a default frame into
    /// its saved one); kicking off the pulse synchronously in that same pass lets the overlay's
    /// animation transaction sweep up that geometry, so the halo visibly slides down from the
    /// centre of the window into position. Holding both animations inert until we've laid out
    /// once lets the glow simply appear where it belongs and only then begin to throb.
    @State private var settled = false

    func body(content: Content) -> some View {
        // Animate only while orchestra is on, the pulse has been kicked off, and the user
        // hasn't asked to reduce motion; otherwise the halo holds at a calm mid intensity.
        let animating = active && pulsing && !reduceMotion
        // Crest values ease back toward the resting baseline as `peakBrightness` drops, so a
        // lower cap dims the whole swell without touching the calm resting halo.
        func crest(_ rest: Double, _ peak: Double) -> Double { rest + (peak - rest) * peakBrightness }
        let intensity = animating ? crest(0.5, 1.0) : 0.5
        return content
            .overlay {
                if active {
                    RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)
                        .strokeBorder(OrchestraStyle.strokeGradient(intensity: intensity),
                                      lineWidth: animating ? 2.4 : 1.4)
                        .shadow(color: AppTheme.orchestra.opacity(animating ? crest(0.28, 0.8) : 0.28),
                                radius: animating ? 14 : 5)
                        // A wider outer bloom reads as ambient light around the box; it swells
                        // hard on the pulse so the glow throbs rather than merely breathing.
                        .shadow(color: AppTheme.orchestra.opacity(animating ? crest(0.08, 0.5) : 0.08),
                                radius: animating ? 26 : 6)
                        .animation(
                            reduceMotion
                                ? .default
                                : .easeInOut(duration: 2.6).repeatForever(autoreverses: true),
                            value: pulsing)
                        .allowsHitTesting(false)  // purely decorative — never eats composer taps
                        .transition(.opacity)
                }
            }
            // Inert until `settled`, so flipping `active` while the window is still settling on
            // its first restore-time layout can't fade/slide the glow into place (see `settled`).
            .animation(settled ? .easeInOut(duration: 0.3) : nil, value: active)
            // Defer to the next runloop so the pulse starts only after the composer has laid out
            // in its final position — otherwise restoring onto an orchestra session makes the
            // halo drift in from the centre of the window.
            .onAppear { DispatchQueue.main.async { settled = true; pulsing = active } }
            .onChange(of: active) { _, now in pulsing = now }
    }
}

extension View {
    /// Wrap a composer (or any rounded box) in the orchestra golden glow while `active`.
    /// Place it *after* the view's own background so the halo sits around the box.
    /// `peakBrightness` (default 1.0) caps how bright the pulse crests — lower it to dim the
    /// throb (e.g. the in-chat composer) without changing the resting glow.
    func orchestraGlow(active: Bool, cornerRadius: CGFloat = 8, peakBrightness: Double = 1.0) -> some View {
        modifier(OrchestraGlow(active: active, cornerRadius: cornerRadius, peakBrightness: peakBrightness))
    }
}

/// The effort-menu trigger label when orchestra is selected: a sparkles glyph + "Orchestra"
/// in the signature gold, replacing the plain "Effort: xhigh" text so the mode is obvious at
/// a glance. The sparkle twinkles in time with the composer glow (steady under Reduce Motion).
struct OrchestraEffortLabel: View {
    @Environment(\.accessibilityReduceMotion) private var reduceMotion
    @State private var twinkle = false

    var body: some View {
        let on = twinkle && !reduceMotion
        return HStack(spacing: 4) {
            Image(systemName: OrchestraStyle.symbol)
                .opacity(on ? 1.0 : 0.6)
                .scaleEffect(on ? 1.08 : 0.85)
                .animation(
                    reduceMotion ? .default : .easeInOut(duration: 2.6).repeatForever(autoreverses: true),
                    value: twinkle)
            Text("Orchestra")
        }
        .foregroundStyle(AppTheme.orchestra)
        .onAppear { twinkle = true }
    }
}
