use alloc::{string::String, vec::Vec};

use crate::{ExprId, Span, ast::CompareOp};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ModuleId(pub u32);

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ExprRef {
    pub module: ModuleId,
    pub expr: ExprId,
}

#[derive(Debug, Clone, PartialEq)]
pub enum RuntimeValue {
    Concrete(ConcreteValue),
    Abstract(AbstractValue),
}

#[derive(Debug, Clone, PartialEq)]
pub enum ConcreteValue {
    String(String),
    Int(i64),
    Float(f64),
    Bool(bool),
    Array(Vec<ThunkId>),
    Object(ObjectValue),
    Function(FunctionValue),
}

#[derive(Debug, Clone, PartialEq)]
pub struct ObjectValue {
    pub fields: Vec<ObjectField>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct ObjectField {
    pub name: String,
    pub value: ThunkId,
    pub span: Span,
}

#[derive(Debug, Clone, PartialEq)]
pub struct FunctionValue {
    pub params: Vec<FunctionParam>,
    pub body: ExprRef,
    pub env: EnvId,
}

#[derive(Debug, Clone, PartialEq)]
pub struct FunctionParam {
    pub name: String,
    pub constraint: Option<ExprRef>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct AbstractValue {
    pub constraints: Vec<ConstraintEntry>,
    pub default: Option<ThunkId>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct ConstraintEntry {
    pub constraint: Constraint,
    pub span: Span,
}

#[derive(Debug, Clone, PartialEq)]
pub enum Constraint {
    Type(PrimitiveType),
    Compare(CompareOp, LiteralValue),
    Regex(String),
    BuiltinPredicate(String),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PrimitiveType {
    String,
    Int,
    Float,
    Bool,
    Array,
}

#[derive(Debug, Clone, PartialEq)]
pub enum LiteralValue {
    String(String),
    Int(i64),
    Float(f64),
    Bool(bool),
}

#[derive(Debug, Clone, PartialEq)]
pub enum Data {
    String(String),
    Int(i64),
    Float(f64),
    Bool(bool),
    Array(Vec<Data>),
    Object(Vec<DataField>),
}

#[derive(Debug, Clone, PartialEq)]
pub struct DataField {
    pub name: String,
    pub value: Data,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ThunkId(pub u32);

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct EnvId(pub u32);

#[derive(Debug, Clone)]
pub struct Thunk {
    pub kind: ThunkKind,
    pub state: ThunkState,
    pub span: Span,
}

#[derive(Debug, Clone)]
pub enum ThunkKind {
    Expr {
        expr: ExprRef,
        env: EnvId,
    },
    Constrained {
        constraint: ExprRef,
        constraint_env: EnvId,
        value: ExprRef,
        value_env: EnvId,
    },
    Value(RuntimeValue),
}

#[derive(Debug, Clone)]
pub enum ThunkState {
    Unevaluated,
    Evaluating,
    Evaluated(RuntimeValue),
    Error,
}

#[derive(Debug, Clone)]
pub struct Env {
    pub parent: Option<EnvId>,
    pub bindings: Vec<Binding>,
}

#[derive(Debug, Clone)]
pub struct Binding {
    pub name: String,
    pub value: ThunkId,
}
