/// The AST for DreamStack.
/// Homoiconic: every node is also representable as data (tagged vectors/maps).

/// A complete DreamStack program is a list of top-level declarations.
#[derive(Debug, Clone)]
pub struct Program {
    pub declarations: Vec<Declaration>,
}

/// Visibility modifier for declarations.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Visibility {
    /// Default — visible within the current module only
    Private,
    /// `pub` — exported from the module
    Public,
}

impl Default for Visibility {
    fn default() -> Self { Visibility::Private }
}

/// Top-level declarations.
#[derive(Debug, Clone)]
pub enum Declaration {
    /// `let name = expr`
    Let(LetDecl),
    /// `view name = expr` or `view name(params) = expr`
    View(ViewDecl),
    /// `effect name(params): ReturnType`
    Effect(EffectDecl),
    /// `on event_name -> body`
    OnHandler(OnHandler),
    /// `component Name(props) = body`
    Component(ComponentDecl),
    /// `route "/path" -> view_expr`
    Route(RouteDecl),
    /// `constrain element.prop = expr`
    Constrain(ConstrainDecl),
    /// `stream main on "ws://..." { mode: signal }`
    Stream(StreamDecl),
    /// `every 500 -> expr`
    Every(EveryDecl),
    /// Top-level expression statement: `log("hello")`, `push(items, x)`
    ExprStatement(Expr),
    /// `import { Card, Button } from "./components"`
    Import(ImportDecl),
    /// `export let count = 0`, `export component Card(...) = ...`
    Export(String, Box<Declaration>),
    /// `type PositiveInt = Int where value > 0`
    TypeAlias(TypeAliasDecl),
    /// `layout dashboard { sidebar.width == 250 }`
    Layout(LayoutDecl),
    /// `enum Status { Loading, Ok(data), Error(msg) }`
    Enum(EnumDecl),
}

/// `enum Status { Loading, Ok(data), Error(msg) }`
#[derive(Debug, Clone)]
pub struct EnumDecl {
    pub name: String,
    pub variants: Vec<EnumVariant>,
    pub visibility: Visibility,
    pub doc: Option<String>,
    pub span: Span,
}

/// A single variant of an enum: `Ok(value: T)` or `Loading`
#[derive(Debug, Clone)]
pub struct EnumVariant {
    pub name: String,
    pub fields: Vec<Param>,
    pub span: Span,
}

/// `layout name { constraints }`
#[derive(Debug, Clone)]
pub struct LayoutDecl {
    pub name: String,
    pub constraints: Vec<LayoutConstraint>,
    pub span: Span,
}

/// A single layout constraint: `left op right [strength]`
#[derive(Debug, Clone)]
pub struct LayoutConstraint {
    pub left: LayoutExpr,
    pub op: ConstraintOp,
    pub right: LayoutExpr,
    pub strength: ConstraintStrength,
}

/// Constraint operators.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ConstraintOp {
    Eq,  // ==
    Gte, // >=
    Lte, // <=
}

/// Constraint strength/priority.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ConstraintStrength {
    Required,
    Strong,
    Medium,
    Weak,
}

impl Default for ConstraintStrength {
    fn default() -> Self { ConstraintStrength::Required }
}

/// A layout expression: `element.property`, `200`, or `expr + expr`.
#[derive(Debug, Clone)]
pub enum LayoutExpr {
    /// `sidebar.width`, `parent.height`
    Prop(String, String),
    /// Numeric constant
    Const(f64),
    /// Addition: `sidebar.x + sidebar.width`
    Add(Box<LayoutExpr>, Box<LayoutExpr>),
    /// Subtraction: `parent.width - sidebar.width`
    Sub(Box<LayoutExpr>, Box<LayoutExpr>),
    /// Multiplication: `parent.width * 0.3`
    Mul(Box<LayoutExpr>, Box<LayoutExpr>),
}

/// `import { Card, Button } from "./components"`
#[derive(Debug, Clone)]
pub struct ImportDecl {
    pub names: Vec<String>,
    pub source: String,
    pub span: Span,
}

/// `let count = 0` or `let doubled = count * 2`
#[derive(Debug, Clone)]
pub struct LetDecl {
    pub name: String,
    pub type_annotation: Option<TypeExpr>,
    pub value: Expr,
    pub visibility: Visibility,
    pub doc: Option<String>,
    pub span: Span,
}

/// `view counter = column [ ... ]`
/// `view profile(id: UserId) = ...`
#[derive(Debug, Clone)]
pub struct ViewDecl {
    pub name: String,
    pub params: Vec<Param>,
    pub body: Expr,
    pub visibility: Visibility,
    pub doc: Option<String>,
    pub span: Span,
}

/// `effect fetchUser(id: UserId): Result<User, ApiError>`
#[derive(Debug, Clone)]
pub struct EffectDecl {
    pub name: String,
    pub params: Vec<Param>,
    pub return_type: TypeExpr,
    pub doc: Option<String>,
    pub span: Span,
}

/// `on toggle_sidebar -> ...`
#[derive(Debug, Clone)]
pub struct OnHandler {
    pub event: String,
    pub param: Option<String>,
    pub body: Expr,
    pub span: Span,
}

/// `component Card(title: String, children: View) = ...`
#[derive(Debug, Clone)]
pub struct ComponentDecl {
    pub name: String,
    pub props: Vec<Param>,
    pub body: Expr,
    pub visibility: Visibility,
    pub doc: Option<String>,
    pub span: Span,
}

/// `route "/users/:id" -> column [ text "User {id}" ]`
#[derive(Debug, Clone)]
pub struct RouteDecl {
    pub path: String,
    pub body: Expr,
    pub span: Span,
}

/// `constrain sidebar.width = clamp(200, parent.width * 0.2, 350)`
#[derive(Debug, Clone)]
pub struct ConstrainDecl {
    pub element: String,
    pub prop: String,
    pub expr: Expr,
    pub span: Span,
}

/// `stream main on "ws://localhost:9100" { mode: signal }`
#[derive(Debug, Clone)]
pub struct StreamDecl {
    pub view_name: String,
    pub relay_url: Expr,
    pub mode: StreamMode,
    pub transport: StreamTransport,
    /// Explicit signal output list. Empty = stream all signals (backwards-compat).
    pub output: Vec<String>,
    pub span: Span,
}

/// `every 500 -> expr` — periodic timer
#[derive(Debug, Clone)]
pub struct EveryDecl {
    pub interval_ms: Expr,
    pub body: Expr,
    pub span: Span,
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum StreamMode {
    Pixel,      // raw RGBA framebuffer every frame
    Delta,      // XOR + RLE — only changed pixels
    Signal,     // JSON signal diffs (DreamStack-native)
}

impl Default for StreamMode {
    fn default() -> Self { StreamMode::Signal }
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum StreamTransport {
    WebSocket,  // default — frames over WebSocket via relay
    WebRTC,     // peer-to-peer data channels, relay for signaling only
}

impl Default for StreamTransport {
    fn default() -> Self { StreamTransport::WebSocket }
}

/// Function/view parameter.
#[derive(Debug, Clone)]
pub struct Param {
    pub name: String,
    pub type_annotation: Option<TypeExpr>,
}

/// Type expressions (simplified for Phase 0).
#[derive(Debug, Clone)]
pub enum TypeExpr {
    Named(String),
    Generic(String, Vec<TypeExpr>),
    /// Refinement type: `Int where value > 0`
    Refined {
        base: Box<TypeExpr>,
        predicate: Box<Expr>,
    },
}

/// `type PositiveInt = Int where value > 0`
#[derive(Debug, Clone)]
pub struct TypeAliasDecl {
    pub name: String,
    pub definition: TypeExpr,
    pub visibility: Visibility,
    pub doc: Option<String>,
    pub span: Span,
}

/// Expressions — the core of the language.
#[derive(Debug, Clone)]
pub enum Expr {
    /// Integer literal: `42`
    IntLit(i64),
    /// Float literal: `3.14`
    FloatLit(f64),
    /// String literal: `"hello"` (may contain `{interpolation}`)
    StringLit(StringLit),
    /// Boolean literal: `true` / `false`
    BoolLit(bool),
    /// Identifier: `count`, `sidebar.width`
    Ident(String),
    /// Dotted access: `user.name`
    DotAccess(Box<Expr>, String),
    /// Binary operation: `a + b`, `count > 10`
    BinOp(Box<Expr>, BinOp, Box<Expr>),
    /// Unary operation: `-x`, `!flag`
    UnaryOp(UnaryOp, Box<Expr>),
    /// Assignment: `count += 1`, `panel_x.target = 0`
    Assign(Box<Expr>, AssignOp, Box<Expr>),
    /// Function call: `clamp(200, 20vw, 350)`
    Call(String, Vec<Expr>),
    /// Block: multiple expressions, last is the value
    Block(Vec<Expr>),
    /// View element: `text "hello"`, `button "+" { click: ... }`
    Element(Element),
    /// Container: `column [ ... ]`, `row [ ... ]`
    Container(Container),
    /// When conditional: `when count > 10 -> ...` with optional `else -> ...`
    When(Box<Expr>, Box<Expr>, Option<Box<Expr>>),

    /// Each loop: `each item in list => template`
    Each(String, Box<Expr>, Box<Expr>),
    /// Match expression
    Match(Box<Expr>, Vec<MatchArm>),
    /// Pipe: `expr | operator`
    Pipe(Box<Expr>, Box<Expr>),
    /// `perform effectName(args)`
    Perform(String, Vec<Expr>),
    /// `stream from source`
    StreamFrom {
        source: String,
        mode: Option<StreamMode>,
        /// Receiver-side field selector. Empty = receive all fields.
        select: Vec<String>,
    },
    /// Lambda: `(x -> x * 2)`
    Lambda(Vec<String>, Box<Expr>),
    /// Record literal: `{ key: value, ... }`
    Record(Vec<(String, Expr)>),
    /// List literal: `[a, b, c]`
    List(Vec<Expr>),
    /// `if cond then a else b`
    If(Box<Expr>, Box<Expr>, Box<Expr>),
    /// Spring: `spring(target: 0, stiffness: 300, damping: 30)`
    Spring(Vec<(String, Expr)>),
    /// `for item in items -> body` with optional index `for item, i in items -> body`
    ForIn {
        item: String,
        index: Option<String>,
        iter: Box<Expr>,
        body: Box<Expr>,
    },
    /// Component instantiation: `<Card title="hello" />`
    ComponentUse {
        name: String,
        props: Vec<(String, Expr)>,
        children: Vec<Expr>,
    },
    /// Index access: `grid[i]`, `pads[8 + i]`
    Index(Box<Expr>, Box<Expr>),
    /// Method call: `items.push(x)`, `items.filter(fn)`
    MethodCall(Box<Expr>, String, Vec<Expr>),
    /// Slot: renders children passed to a component
    Slot,
    /// Await: `await fetchJSON("/api")`
    Await(Box<Expr>),
    /// Merge streams: `merge(stream1, stream2, ...)`
    Merge(Vec<Expr>),
    /// Triple-quoted raw string: `"""content"""`
    RawString(String),
}

/// String literal with interpolation segments.
#[derive(Debug, Clone)]
pub struct StringLit {
    pub segments: Vec<StringSegment>,
}

#[derive(Debug, Clone)]
pub enum StringSegment {
    Literal(String),
    Interpolation(Box<Expr>),
}

/// Binary operators.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BinOp {
    Add,
    Sub,
    Mul,
    Div,
    Mod,
    Eq,
    Neq,
    Lt,
    Gt,
    Lte,
    Gte,
    And,
    Or,
}

/// Unary operators.
#[derive(Debug, Clone, Copy)]
pub enum UnaryOp {
    Neg,
    Not,
}

/// Assignment operators.
#[derive(Debug, Clone, Copy)]
pub enum AssignOp {
    Set,
    AddAssign,
    SubAssign,
    MulAssign,
    DivAssign,
}

/// A UI element: `text label`, `button "+" { click: handler }`
#[derive(Debug, Clone)]
pub struct Element {
    pub tag: String,
    pub args: Vec<Expr>,
    pub props: Vec<(String, Expr)>,
    pub modifiers: Vec<Modifier>,
}

/// A container: `column [ child1, child2 ]`
#[derive(Debug, Clone)]
pub struct Container {
    pub kind: ContainerKind,
    pub children: Vec<Expr>,
    pub props: Vec<(String, Expr)>,
}

#[derive(Debug, Clone)]
pub enum ContainerKind {
    Column,
    Row,
    Stack,
    List,
    Panel,
    Form,
    Scene,
    Custom(String),
}

/// Match arm: `Ok(u) -> column [ ... ]`
#[derive(Debug, Clone)]
pub struct MatchArm {
    pub pattern: Pattern,
    pub body: Expr,
}

/// Pattern matching.
#[derive(Debug, Clone)]
pub enum Pattern {
    Wildcard,
    Ident(String),
    Constructor(String, Vec<Pattern>),
    Literal(Expr),
    /// Tuple pattern: `(a, b, c)`
    Tuple(Vec<Pattern>),
    /// Integer literal pattern: `42`
    IntLiteral(i64),
    /// Boolean literal pattern: `true` / `false`
    BoolLiteral(bool),
}

/// Modifiers: `| animate fade-in 200ms`
#[derive(Debug, Clone)]
pub struct Modifier {
    pub name: String,
    pub args: Vec<Expr>,
}

/// Source location tracking.
#[derive(Debug, Clone, Copy, Default)]
pub struct Span {
    pub start: usize,
    pub end: usize,
    pub line: usize,
    pub col: usize,
}

impl Span {
    /// Create a span from a token's position.
    pub fn from_token_start(line: usize, col: usize, byte_offset: usize) -> Self {
        Span { start: byte_offset, end: byte_offset, line, col }
    }

    /// Extend this span to include another span's end position.
    pub fn extend_to(&self, other: &Span) -> Self {
        Span { start: self.start, end: other.end, line: self.line, col: self.col }
    }
}
