// Based on finos/morphir's v4 semantic model and schema.
// Copyright 2026 FINOS. SPDX-License-Identifier: Apache-2.0

///|
/// Integers and decimals retain their original lexemes, including arbitrary precision.
#derive(Eq, Debug)
pub(all) enum Literal {
  BoolLiteral(Bool)
  CharLiteral(Char)
  StringLiteral(String)
  IntegerLiteral(String)
  FloatLiteral(Double)
  DecimalLiteral(String)
  DocumentLiteral(Data)
}

///|
/// Document literals cannot occur in a literal pattern.
#derive(Eq, Debug)
pub(all) enum PatternLiteral {
  BoolLiteral(Bool)
  CharLiteral(Char)
  StringLiteral(String)
  IntegerLiteral(String)
  FloatLiteral(Double)
  DecimalLiteral(String)
}

///|
#derive(Eq, Debug)
pub(all) enum Pattern {
  WildcardPattern(ValueAttributes)
  AsPattern(ValueAttributes, Pattern, Name)
  TuplePattern(ValueAttributes, Array[Pattern])
  ConstructorPattern(ValueAttributes, FQName, Array[Pattern])
  EmptyListPattern(ValueAttributes)
  HeadTailPattern(ValueAttributes, Pattern, Pattern)
  LiteralPattern(ValueAttributes, PatternLiteral)
  UnitPattern(ValueAttributes)
}

///|
#derive(Eq, Debug)
pub(all) enum Value {
  Literal(ValueAttributes, Literal)
  Constructor(ValueAttributes, FQName)
  Tuple(ValueAttributes, Array[Value])
  List(ValueAttributes, Array[Value])
  Record(ValueAttributes, Map[Name, Value])
  Variable(ValueAttributes, Name)
  Reference(ValueAttributes, FQName)
  Field(ValueAttributes, Value, Name)
  FieldFunction(ValueAttributes, Name)
  Apply(ValueAttributes, Value, Value)
  Lambda(ValueAttributes, Pattern, Value)
  LetDefinition(ValueAttributes, Name, ValueDefinition, Value)
  LetRecursion(ValueAttributes, Map[Name, ValueDefinition], Value)
  Destructure(ValueAttributes, Pattern, Value, Value)
  IfThenElse(ValueAttributes, Value, Value, Value)
  PatternMatch(ValueAttributes, Value, Array[(Pattern, Value)])
  UpdateRecord(ValueAttributes, Value, Map[Name, Value])
  Unit(ValueAttributes)
  Hole(ValueAttributes, HoleReason, Type?)
}

///|
#derive(Eq, Debug)
pub(all) struct ValueSpecification {
  annotations : Array[Annotation]
  inputs : Array[(Name, Type)]
  output : Type
}

///|
/// Ordered inputs preserve parameter order independently of any wire map spelling.
#derive(Eq, Debug)
pub(all) enum ValueDefinition {
  ExpressionBody(Array[(Name, Type)], Type, Value)
  NativeBody(Array[(Name, Type)], Type, NativeInfo)
  ExternalBody(Array[(Name, Type)], Type, Array[ExternalBinding], Value?)
  IncompleteBody(Array[(Name, Type)], Type?, Incompleteness, Value?)
}

///|
#derive(Eq, Debug)
pub(all) struct ExternalBinding {
  target_platform : String
  external_name : String
}

///|
#derive(Eq, Debug)
pub(all) struct NativeInfo {
  hint : NativeHint
  description : String?
}

///|
#derive(Eq, Debug)
pub(all) enum NativeHint {
  Arithmetic
  Comparison
  StringOp
  CollectionOp
  PlatformSpecific(String)
}

///|
/// Incomplete definitions without an output type do not yet have a specification.
pub fn ValueDefinition::to_specification(
  self : ValueDefinition,
) -> ValueSpecification? {
  match self {
    ExpressionBody(inputs, output, _)
    | NativeBody(inputs, output, _)
    | ExternalBody(inputs, output, _, _)
    | IncompleteBody(inputs, Some(output), _, _) =>
      Some({ annotations: [], inputs, output, })
    IncompleteBody(_, None, _, _) => None
  }
}