import SwiftUI
import UIKit

/// # WebTerm Design System — token vocabulary (FROZEN public surface)
///
/// The single source of truth for every visual constant in the App layer.
/// Screens/components must reference these tokens — never inline colors,
/// spacings, radii, opacities, durations or haptics. Direction: "精致原生"
/// (refined native, Apple HIG), dark-mode-first, amber-gold accent (desktop-matched)
/// continuing the web selection color.
///
/// Vocabulary (all under `DS.`):
/// - `Palette`  — adaptive `accent` (amber gold, matches desktop) · semantic `status*` colors
///                (color is NEVER the only status signal — pair with a symbol,
///                see `StatusStyle`) · `surface`/`card`/`hairline` surfaces ·
///                `textPrimary`/`textSecondary`/`textTertiary`.
/// - `Space`    — 2·4·8·12·16·20·24 scale (`xs2`…`xxl24`). No off-scale gaps.
/// - `Radius`   — `sm8`/`md12`/`lg16`/`pill`.
/// - `Stroke`   — `hairline` (1pt border width).
/// - `Opacity`  — `stale`/`exited`/`pressed` dimming multipliers.
/// - `Layout`   — `minHitTarget` (44pt HIG minimum).
/// - `Motion`   — `fast`/`base` eased animations + `gated(_:reduceMotion:)`
///                which collapses to `nil` under Reduce Motion.
/// - `Haptics`  — `selection`/`success`/`warning` (`@MainActor`).
///
/// Companion files: `Typography.swift` (`DS.Typography`), `StatusStyle.swift`
/// (`StatusStyle` / `DisplayStatus`), `Primitives.swift` (reusable views).
enum DS {

    // MARK: - Palette

    /// Colors. `accent` is asset-free adaptive (no `.xcassets`); the semantic
    /// status colors use the direction's exact hex so light/dark stay on-brand.
    enum Palette {

        // ── Accent (amber gold — matches the desktop/web theme) ─────────────
        // The desktop web/Electron UI uses --accent #E3A64A ("amber gold",
        // public/style.css:14) on a warm near-neutral dark surface. We match it.
        // Adaptive: dark = #E3A64A (gold), light = #C9892F (deeper gold =
        // --accent-2) for adequate contrast on a light background. Used
        // sparingly — primary actions, selection, active state only. Gold needs
        // DARK text on top → use `onAccent`, never white/textPrimary.

        /// The one accent token. Inject once at the root via `.tint(DS.Palette.accent)`.
        static let accent = Color(uiColor: accentUIColor())

        /// The accent as a dynamic `UIColor`. Exposed so tests can resolve the
        /// two schemes deterministically (`resolvedColor(with:)`) without going
        /// through the SwiftUI→UIKit bridge.
        static func accentUIColor() -> UIColor {
            UIColor { trait in
                trait.userInterfaceStyle == .dark
                    ? UIColor(red: 0xE3 / 255.0, green: 0xA6 / 255.0, blue: 0x4A / 255.0, alpha: 1) // #E3A64A --accent
                    : UIColor(red: 0xC9 / 255.0, green: 0x89 / 255.0, blue: 0x2F / 255.0, alpha: 1) // #C9892F --accent-2
            }
        }

        /// Text/icon color to place ON an accent-filled surface (gold needs dark
        /// ink for contrast — mirrors web --on-accent #1A1305).
        static let onAccent = rgb(26, 19, 5)
        /// Faint accent wash (selection/soft highlight) — web --accent-soft.
        static let accentSoft = Color(red: 0xE3 / 255.0, green: 0xA6 / 255.0, blue: 0x4A / 255.0, opacity: 0.15)

        // ── Semantic status colors (match the desktop/web status palette) ───
        // These are the ONLY status colors. `StatusStyle` pairs each with a
        // distinct SF Symbol so status is never conveyed by color alone. Hex
        // mirrors the web's warm status set (public/style.css:18-20) so iOS and
        // desktop read the same. `waiting` amber #F5B14C stays distinct from the
        // gold accent #E3A64A (brighter/less brown), and is only ever a small
        // badge fill, never a large accent surface.
        /// working — #46D07F (web --green).
        static let statusWorking = rgb(70, 208, 127)
        /// waiting / needs-me — #F5B14C (web --amber).
        static let statusWaiting = rgb(245, 177, 76)
        /// stuck — #FF6B6B (web --red).
        static let statusStuck = rgb(255, 107, 107)
        /// idle — quiet secondary gray (distinguished from `unknown` by shape).
        static let statusIdle = Color.secondary
        /// unknown / no signal yet — gray.
        static let statusUnknown = Color.gray
        /// exited — dimmed secondary (apply `Opacity.exited` on the container).
        static let statusExited = Color.secondary

        // ── Timeline event classes (T-iOS-24) ──────────────────────────────
        // Semantic colors for the activity-timeline event classes. `waiting`
        // and `stuck` reuse the status tokens above (same meaning); `done`
        // reuses `statusWorking` (a completed run). `tool`/`user` get their own
        // tokens so nothing in the app hardcodes a raw SwiftUI color.
        /// tool run — indigo, tied to the app accent family (#5E9EFF-ish).
        static let timelineTool = rgb(94, 158, 255)
        /// user message — violet (#AF7BFF), distinct from accent & tool.
        static let timelineUser = rgb(175, 123, 255)

        // ── Surfaces & text ────────────────────────────────────────────────

        /// Base screen background.
        static let surface = Color(uiColor: .systemBackground)
        /// Card / grouped-content background (a step up from `surface`).
        static let card = Color(uiColor: .secondarySystemBackground)
        /// Hairline separator/border color.
        static let hairline = Color(uiColor: .separator)
        /// Primary label color.
        static let textPrimary = Color.primary
        /// Secondary label color (meta, captions).
        static let textSecondary = Color.secondary
        /// Tertiary label color (de-emphasized detail).
        static let textTertiary = Color(uiColor: .tertiaryLabel)

        // ── Terminal canvas (fixed warm-dark, matches the desktop terminal) ──
        // A terminal reads as dark regardless of app appearance (like the
        // desktop). Values mirror the web chrome: --bg #100F0D / --text #ECE9E3.
        /// Terminal background — warm near-black #100F0D (web --bg).
        static let terminalBackground = rgb(16, 15, 13)
        /// Terminal foreground — warm off-white #ECE9E3 (web --text).
        static let terminalForeground = rgb(236, 233, 227)

        /// Build an opaque sRGB color from 0–255 components.
        private static func rgb(_ r: Double, _ g: Double, _ b: Double) -> Color {
            Color(.sRGB, red: r / 255, green: g / 255, blue: b / 255, opacity: 1)
        }
    }

    // MARK: - Space

    /// Spacing scale — 2·4·8·12·16·20·24. Every gap/padding picks one of these;
    /// there are no in-between values (the audit's 3/5/6/10/14 all round here).
    enum Space {
        static let xs2: CGFloat = 2
        static let xs4: CGFloat = 4
        static let sm8: CGFloat = 8
        static let md12: CGFloat = 12
        static let lg16: CGFloat = 16
        static let xl20: CGFloat = 20
        static let xxl24: CGFloat = 24
    }

    // MARK: - Radius

    /// Corner radii. Legacy 6/10 both fold into `sm8`/`md12`.
    enum Radius {
        static let sm8: CGFloat = 8
        static let md12: CGFloat = 12
        static let lg16: CGFloat = 16
        /// Fully-rounded (capsule/pill).
        static let pill: CGFloat = 999
    }

    // MARK: - Stroke

    /// Border widths.
    enum Stroke {
        /// Hairline card/overlay border.
        static let hairline: CGFloat = 1
    }

    // MARK: - Opacity

    /// Dimming multipliers (used with `.opacity()`, sometimes `.grayscale()`).
    enum Opacity {
        /// Telemetry gone stale (past its TTL).
        static let stale: Double = 0.45
        /// A session that has exited.
        static let exited: Double = 0.55
        /// Pressed-state feedback on buttons.
        static let pressed: Double = 0.72
    }

    // MARK: - Layout

    /// Layout constants that are not spacings.
    enum Layout {
        /// HIG minimum touch target (44×44pt).
        static let minHitTarget: CGFloat = 44
    }

    // MARK: - Motion

    /// Animation tokens. Subtle, eased (~0.18–0.25s). ALWAYS route through
    /// `gated(_:reduceMotion:)` so Reduce Motion collapses to no motion.
    enum Motion {
        static let fastDuration: Double = 0.18
        static let baseDuration: Double = 0.25

        /// Quick affordance (chips, presses).
        static let fast = Animation.easeInOut(duration: fastDuration)
        /// Standard transition (banners, sheets, list changes).
        static let base = Animation.easeInOut(duration: baseDuration)

        /// Returns `animation` normally, or `nil` (instant, no motion) when
        /// Reduce Motion is enabled. Callers pass the environment flag.
        static func gated(_ animation: Animation, reduceMotion: Bool) -> Animation? {
            reduceMotion ? nil : animation
        }
    }

    // MARK: - Haptics

    /// Tactile feedback for key interactions. `@MainActor` — the underlying
    /// UIKit generators must be touched on the main thread. Additive to the
    /// existing `GateViewModel` gate-arrival haptic (different call sites).
    @MainActor
    enum Haptics {
        /// Light selection tap — opening a session, tapping a key.
        static func selection() {
            UIImpactFeedbackGenerator(style: .light).impactOccurred()
        }

        /// Success notification — a gate approve resolved.
        static func success() {
            UINotificationFeedbackGenerator().notificationOccurred(.success)
        }

        /// Warning notification — a destructive/reject decision.
        static func warning() {
            UINotificationFeedbackGenerator().notificationOccurred(.warning)
        }
    }
}
