// GlassStyle.swift — the app's single, availability-gated entry point to Apple's "Liquid
// Glass" (iOS / macOS / tvOS 26). Every Liquid Glass symbol (glassEffect, Glass, the
// .glassProminent button style …) is HARD-gated to OS 26: referencing one with our
// deployment targets (macOS 14 / iOS 17 / tvOS 17) is a COMPILE error, not a silent no-op,
// unless it sits behind `if #available`. So all glass in the app routes through the two
// helpers below, each of which falls back to the EXACT look the app shipped before
// (.regularMaterial / .borderedProminent) — nothing regresses on older OSes, and the gating
// lives in exactly one file.

import SwiftUI

// MARK: - Glass background

/// Liquid Glass behind a floating / overlay surface, with the pre-26 `.regularMaterial`
/// look as the fallback. Use ONLY on the floating control / overlay layer (the streaming
/// HUD, the trust card, the touch exit chip) — never on content tiles or dense forms (HIG).
///
/// `glassEffect()`'s own default shape is a Capsule, so panels MUST pass an explicit shape
/// (a RoundedRectangle / Circle) or they render as a pill. `interactive` makes the glass
/// react to press — only meaningful when the glass itself is the tap target.
private struct GlassBackground<S: Shape>: ViewModifier {
    let shape: S
    var interactive = false

    func body(content: Content) -> some View {
        if #available(iOS 26, macOS 26, tvOS 26, *) {
            content.glassEffect(interactive ? .regular.interactive() : .regular, in: shape)
        } else {
            content.background(.regularMaterial, in: shape)
        }
    }
}

extension View {
    /// Liquid Glass (26+) or the existing `.regularMaterial` (pre-26) behind a floating
    /// surface. Pass the surface's shape explicitly — glass defaults to a Capsule otherwise.
    func glassBackground<S: Shape>(_ shape: S, interactive: Bool = false) -> some View {
        modifier(GlassBackground(shape: shape, interactive: interactive))
    }
}

// MARK: - Glass primary button

/// The single prominent action on a floating / overlay or sheet surface: the Liquid-Glass
/// prominent button style on 26+, falling back to `.borderedProminent` (the app's current
/// primary style) below. Apply directly to a `Button`; role / keyboardShortcut / disabled
/// chain after it as usual. tvOS stays `.borderedProminent` always — glass chrome fights the
/// focus engine, and keeping it preserves today's tvOS look exactly.
private struct GlassProminentButton: ViewModifier {
    func body(content: Content) -> some View {
        #if os(tvOS)
        content.buttonStyle(.borderedProminent)
        #else
        if #available(iOS 26, macOS 26, *) {
            content.buttonStyle(.glassProminent)
        } else {
            content.buttonStyle(.borderedProminent)
        }
        #endif
    }
}

extension View {
    /// Liquid-Glass prominent style (26+, non-tvOS) or `.borderedProminent`. Drop-in for the
    /// `.buttonStyle(.borderedProminent)` on a surface's primary action.
    func glassProminentButtonStyle() -> some View {
        modifier(GlassProminentButton())
    }
}

// MARK: - Console glass (gamepad host tiles + settings rows)

/// Liquid Glass tuned for the gamepad UI's "console" surfaces — the host-carousel tiles and
/// the settings rows. Unlike `glassBackground` (floating-overlay only, per HIG), this deliberately
/// clads content tiles / dense rows: a chosen part of the 10-foot console look. `tint` washes the
/// glass toward a color (the palette accent on the focused / primary surface); `interactive` makes
/// it flex on press.
///
/// Every tier is WASHED with the palette's `ink.glass` — the same surface colour the console
/// fills its panels with — so switching the background palette recolours the surfaces, not just
/// the text on them. The wash alphas are tune-on-device values with one fixed direction: the
/// pale palettes' white frost needs MORE body than the dark glass (the console's 0.66-vs-0.62
/// pair), because a thin white wash over a colourful field reads as haze, not as a surface.
private struct ConsoleGlass<S: Shape>: ViewModifier {
    let shape: S
    var tint: Color?
    var interactive = false
    /// Take the MATERIAL path even where real Liquid Glass is available. For surfaces that get
    /// transformed while they animate: glass samples the backdrop through its own layer, and under
    /// a `rotation3DEffect` / `opacity` it cannot, so it renders one way mid-animation and snaps to
    /// another the instant the transform ends — on glass that reads as the tile being SWAPPED for a
    /// different one as it lands. A material is a flat composite and looks identical throughout.
    var forceMaterial = false
    /// The console surface follows the background palette: a PALE field needs the material to
    /// frost light and the glass to read as white, or the dark ink on top of it disappears.
    /// Defaults to the dark ink, so every non-gamepad caller is unchanged.
    @Environment(\.gamepadInk) private var ink

    private var scheme: ColorScheme { ink.isLight ? .light : .dark }
    /// The palette wash over the material tiers (the material itself supplies the blur body).
    private var materialWash: Color { ink.glass(ink.isLight ? 0.55 : 0.40) }

    func body(content: Content) -> some View {
        // The scheme goes on the WHOLE modified view, not just the fill inside `.background {}`.
        // Scoped to the fill it frosts the material correctly and stops there, so a system colour
        // in the row's own content (a `.secondary` label, a `.bordered` button) still resolved
        // against the device appearance — which is how the pale palettes came out light-on-light
        // on tvOS, whose appearance is always Dark. The 26 branch had it right all along; the
        // tvOS and pre-26 branches were the odd ones out.
        #if os(tvOS)
        // ALWAYS the material fallback on tvOS: the gamepad settings list is 15+ of these
        // surfaces, and live Liquid Glass per row made the whole screen visibly laggy on the
        // Apple TV's GPU (same class of call GlassProminentButton already makes — glass fights
        // the 10-foot platform). The wash and tint ride overlays — two flat fills, no GPU cost.
        content
            .background {
                shape.fill(.ultraThinMaterial)
                    .environment(\.colorScheme, scheme)
                    .overlay { shape.fill(materialWash) }
                    .overlay {
                        if let tint { shape.fill(tint) }
                    }
            }
            .environment(\.colorScheme, scheme)
        #else
        if #available(iOS 26, macOS 26, *), !forceMaterial {
            content
                // The caller's tint rides HERE, not in `Glass.tint`, so it can ANIMATE. A Glass
                // value is opaque to SwiftUI's animation system: changing its tint swaps one
                // effect for another, which is why a focused row's accent used to appear (and,
                // worse, disappear a beat late) as a hard jump while the row's scale animated
                // smoothly beside it. A plain fill interpolates, so `.animation(value: focused)`
                // at the call site now covers the whole row. Sits between the glass and the
                // content: `.background` is behind the label, `glassEffect` behind both.
                .background { shape.fill(tint ?? .clear) }
                .glassEffect(glass, in: shape)
                .environment(\.colorScheme, scheme)
        } else {
            content
                .background {
                    shape.fill(.ultraThinMaterial)
                        .environment(\.colorScheme, scheme)
                        .overlay { shape.fill(materialWash) }
                        .overlay {
                            if let tint { shape.fill(tint) }
                        }
                }
                .environment(\.colorScheme, scheme)
        }
        #endif
    }

    #if !os(tvOS)
    @available(iOS 26, macOS 26, *)
    private var glass: Glass {
        // The glass carries the PALETTE wash only — the caller's focus tint is an animatable fill
        // above it now (see `body`).
        //
        // A pale palette gets `.clear` glass, not `.regular`. Its `ink.glass` is literal white, so
        // over `.regular` — which is already a bright, high-body material — even a light white
        // wash lands as a flat white slab: the refraction and the blurred field behind stop
        // reading entirely, which is the "opaque fully white bg" on every row, pill and legend.
        // Lowering the tint alone did NOT fix it, because the opacity was coming from the glass
        // BODY rather than from the tint. `.clear` is the variant meant for exactly this — a
        // surface over content that must stay visible through it — and a small white wash on top
        // of it is enough to keep the dark ink legible without closing the surface up.
        let wash = ink.glass(ink.isLight ? 0.18 : 0.45)
        // Spelled out rather than `.clear`/`.regular`: a ternary between two leading-dot members
        // gives the compiler no base type to infer from.
        var g: Glass = (ink.isLight ? Glass.clear : Glass.regular).tint(wash)
        if interactive { g = g.interactive() }
        return g
    }
    #endif
}

extension View {
    /// Liquid Glass for a console surface (a host tile / settings row), or `.ultraThinMaterial`
    /// pre-26 — both washed with the palette's own glass colour, both frosting to the palette's
    /// scheme. Pass the surface's shape explicitly — glass defaults to a Capsule.
    ///
    /// `forceMaterial` opts a TRANSFORMED surface out of live glass; see the property.
    func consoleGlass<S: Shape>(
        _ shape: S, tint: Color? = nil, interactive: Bool = false, forceMaterial: Bool = false
    ) -> some View {
        modifier(ConsoleGlass(
            shape: shape, tint: tint, interactive: interactive, forceMaterial: forceMaterial))
    }
}

// MARK: - Console floating glass (the gamepad screens' close buttons)

/// `glassBackground` for a floating control INSIDE the gamepad UI (the close ✕): same shape
/// contract, but washed with the palette's ink and frosted to the palette's scheme — plain
/// `glassBackground` follows the SYSTEM appearance, which leaves the frost dark under dark ink
/// when a pale palette is up. The non-gamepad floating surfaces (the HUD, the trust card, the
/// touch connect modal) keep plain `glassBackground`: they sit over video or the touch UI,
/// where the palette means nothing.
private struct ConsoleGlassBackground<S: Shape>: ViewModifier {
    let shape: S
    var interactive = false
    @Environment(\.gamepadInk) private var ink

    private var scheme: ColorScheme { ink.isLight ? .light : .dark }

    func body(content: Content) -> some View {
        if #available(iOS 26, macOS 26, tvOS 26, *) {
            content
                .glassEffect(
                    (interactive ? Glass.regular.interactive() : .regular)
                        .tint(ink.glass(ink.isLight ? 0.60 : 0.45)),
                    in: shape)
                .environment(\.colorScheme, scheme)
        } else {
            // Same hoist as ConsoleGlass: the content needs the scheme too, not only the frost.
            content
                .background {
                    shape.fill(.regularMaterial)
                        .environment(\.colorScheme, scheme)
                        .overlay { shape.fill(ink.glass(ink.isLight ? 0.55 : 0.40)) }
                }
                .environment(\.colorScheme, scheme)
        }
    }
}

extension View {
    /// Palette-washed floating glass for the gamepad screens' own controls. Same fallback story
    /// as `glassBackground` (`.regularMaterial` pre-26), plus the ink wash and scheme flip.
    func consoleGlassBackground<S: Shape>(_ shape: S, interactive: Bool = false) -> some View {
        modifier(ConsoleGlassBackground(shape: shape, interactive: interactive))
    }
}
