///|
/// A regex pattern used for matching parameter types in cucumber expressions.
pub(all) struct RegexPattern {
  priv value : String

  fn new(value : String) -> RegexPattern
} derive(Show, Eq)

///|
fn RegexPattern::new(value : String) -> RegexPattern {
  { value, }
}

///|
pub fn RegexPattern::to_string(self : RegexPattern) -> String {
  self.value
}

///|
/// Parameter types supported by cucumber expressions.
pub(all) enum ParamType {
  Int
  Float
  String_
  Word
  Anonymous
  Double_
  Long
  Byte
  Short
  BigDecimal
  BigInteger
  Custom(String)
} derive(Show, Eq, ToJson, FromJson)

///|
/// A registered parameter type entry with name, type, regex patterns, and transformer.
pub(all) struct ParamTypeEntry {
  name : String
  type_ : ParamType
  patterns : Array[RegexPattern]
  transformer : Transformer
}

///|
impl Show for ParamTypeEntry with output(self, logger) {
  logger.write_string("ParamTypeEntry { name: \{self.name}, type_: \{self.type_}, patterns: \{self.patterns} }")
}

///|
impl Eq for ParamTypeEntry with equal(self, other) -> Bool {
  self.name == other.name && self.type_ == other.type_ && self.patterns == other.patterns
}

///|
/// Registry mapping parameter type names to their regex patterns.
pub(all) struct ParamTypeRegistry {
  priv entries : Array[ParamTypeEntry]
}

///|
pub fn ParamTypeRegistry::new() -> ParamTypeRegistry {
  { entries: [] }
}

///|
/// Create a registry with the 11 built-in parameter types pre-registered.
pub fn ParamTypeRegistry::default() -> ParamTypeRegistry {
  let reg = ParamTypeRegistry::new()
  // Integer types (all share the same regex patterns)
  let int_patterns = [
    RegexPattern("(?:-?\\d+)"),
    RegexPattern("(?:\\d+)"),
  ]
  reg.register("int", ParamType::Int, int_patterns,
    transformer=Transformer::new(fn(groups) raise { IntVal(@strconv.parse_int(groups[0])) }),
  )
  reg.register("long", ParamType::Long, int_patterns,
    transformer=Transformer::new(fn(groups) raise { LongVal(@strconv.parse_int64(groups[0])) }),
  )
  reg.register("byte", ParamType::Byte, int_patterns,
    transformer=Transformer::new(fn(groups) raise { ByteVal(@strconv.parse_int(groups[0]).to_byte()) }),
  )
  reg.register("short", ParamType::Short, int_patterns,
    transformer=Transformer::new(fn(groups) raise { ShortVal(@strconv.parse_int(groups[0])) }),
  )
  reg.register("biginteger", ParamType::BigInteger, int_patterns,
    transformer=Transformer::new(fn(groups) { BigIntegerVal(BigInt::from_string(groups[0])) }),
  )
  // Float types (all share the same regex pattern)
  let float_patterns = [
    RegexPattern("(?:[+-]?(?:\\d+|\\d+\\.\\d*|\\d*\\.\\d+)(?:[eE][+-]?\\d+)?)"),
  ]
  reg.register("float", ParamType::Float, float_patterns,
    transformer=Transformer::new(fn(groups) raise { FloatVal(@strconv.parse_double(groups[0])) }),
  )
  reg.register("double", ParamType::Double_, float_patterns,
    transformer=Transformer::new(fn(groups) raise { DoubleVal(@strconv.parse_double(groups[0])) }),
  )
  reg.register("bigdecimal", ParamType::BigDecimal, float_patterns,
    transformer=Transformer::new(fn(groups) raise {
      match @decimal.Decimal::from_string(groups[0]) {
        Some(d) => BigDecimalVal(d)
        None => raise Failure::Failure("Invalid bigdecimal: " + groups[0])
      }
    }),
  )
  // String and word (identity transformers)
  reg.register("string", ParamType::String_, [
    RegexPattern("\"([^\"\\\\]*(\\\\.[^\"\\\\]*)*)\""),
    RegexPattern("'([^'\\\\]*(\\\\.[^'\\\\]*)*)'"),
  ],
    transformer=Transformer::new(fn(groups) { StringVal(groups[0]) }),
  )
  reg.register("word", ParamType::Word, [RegexPattern("[^\\s]+")],
    transformer=Transformer::new(fn(groups) { WordVal(groups[0]) }),
  )
  reg.register("", ParamType::Anonymous, [RegexPattern(".*")],
    transformer=Transformer::new(fn(groups) { AnonymousVal(groups[0]) }),
  )
  reg
}

///|
pub fn ParamTypeRegistry::register(
  self : ParamTypeRegistry,
  name : String,
  type_ : ParamType,
  patterns : Array[RegexPattern],
  transformer~ : Transformer = Transformer::new(fn(groups) { ParamValue::CustomVal(@any.of(groups[0])) }),
) -> Unit {
  self.entries.push({ name, type_, patterns, transformer })
}

///|
pub fn ParamTypeRegistry::get(
  self : ParamTypeRegistry,
  name : String,
) -> ParamTypeEntry? {
  for entry in self.entries {
    if entry.name == name {
      return Some(entry)
    }
  }
  None
}

///|
/// Read-only view of all registered parameter type entries.
pub fn ParamTypeRegistry::entries_view(
  self : ParamTypeRegistry,
) -> ArrayView[ParamTypeEntry] {
  self.entries[:]
}