///|
/// 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[:]
}