///|
/// Represents a vCard property.
pub struct Property {
  name : String
  types : Array[String]
  value : String
} derive(Show, Eq, ToJson)

///|
/// Represents a complete vCard.
pub struct VCard {
  properties : Array[Property]
} derive(Show, Eq, ToJson)

///|
pub fn VCard::new() -> VCard {
  let properties = [
    Property::{
      name: "VERSION".to_string(),
      types: [],
      value: "3.0".to_string(),
    },
  ]
  VCard::{ properties, }
}

///|
pub fn VCard::add_property(
  self : VCard,
  name : String,
  types : Array[String],
  value : String,
) -> VCard {
  let properties = self.properties
  properties.push(Property::{ name, types, value })
  VCard::{ properties, }
}

///|
pub fn VCard::get_property(self : VCard, name : String) -> Property? {
  for prop in self.properties {
    if prop.name == name {
      return Some(prop)
    }
  }
  None
}

///|
pub fn VCard::get_property_value(self : VCard, name : String) -> String? {
  for prop in self.properties {
    if prop.name == name {
      return Some(prop.value)
    }
  }
  None
}

///|
pub fn VCard::stringify(self : VCard) -> String {
  let lines = Array::new()
  lines.push("BEGIN:VCARD")
  for prop in self.properties {
    let types = prop.types.join(";")
    if types.is_empty() {
      lines.push(prop.name + ":" + prop.value)
    } else {
      lines.push(prop.name + ";" + types + ":" + prop.value)
    }
  }
  lines.push("END:VCARD")
  lines.join("\n")
}

///|
pub fn VCard::validate(self : VCard) -> Result[VCard, String] {
  // Validate required properties
  let mut has_version = false
  let mut has_fn = false
  let mut has_n = false
  for prop in self.properties {
    println("Checking property: \{prop.name}")
    if prop.name == "VERSION" {
      has_version = true
    }
    if prop.name == "FN" {
      has_fn = true
    }
    if prop.name == "N" {
      has_n = true
    }
  }
  if !has_version {
    return Err("Missing VERSION property")
  }
  if !has_fn {
    return Err("Missing FN property")
  }
  if !has_n {
    return Err("Missing N property")
  }
  Ok(self)
}

///|
pub fn VCard::parse(input : String) -> Result[VCard, String] {
  let lines = input.split("\n").map(fn(s) { s.trim() })
  let properties = Array::new()
  let mut in_vcard = false
  let mut vcard_count = 0
  for line in lines {
    if line.is_empty() {
      continue
    }
    println("LINE: \{line}")
    if line.contains(":") {
      match line.split(":").to_array() {
        ["BEGIN", "VCARD"] => {
          if in_vcard {
            return Err("Multiple vCards found")
          }
          in_vcard = true
          vcard_count = vcard_count + 1
          println("BEGIN VCARD")
        }
        ["END", "VCARD"] => {
          if !in_vcard {
            return Err("Unexpected END VCARD")
          }
          in_vcard = false
        }
        ["VERSION", version_value] =>
          if version_value != "3.0" {
            return Err("Unsupported vCard version: \{version_value}")
          } else {
            properties.push(Property::{
              name: "VERSION".to_string(),
              types: [],
              value: version_value.to_string(),
            })
          }
        [name_type, value] => {
          println("ddt \{name_type}")
          println(name_type.split(";"))
          match name_type.split(";").to_array() {
            [name, .. vals] => {
              let types = vals.map(fn(s) { s.to_string() })
              properties.push(Property::{
                name: name.to_string(),
                types,
                value: value.to_string(),
              })
            }
            _ => continue
          }
        }
        _ => continue
      }
    } else {
      continue
    }
  }
  if vcard_count == 0 {
    return Err("No vCard found")
  }
  if in_vcard {
    return Err("Missing END:VCARD")
  }
  let vcard = VCard::{ properties, }
  if vcard.validate() is Err(e) {
    return Err("vCard validation failed \{e}")
  }
  Ok(vcard)
}

///|
pub struct Contacts {
  vcards : Array[VCard]
}

///|
pub fn Contacts::new() -> Contacts {
  Contacts::{ vcards: Array::new() }
}

///|
pub fn Contacts::add_vcard(self : Contacts, vcard : VCard) -> Contacts {
  self.vcards.push(vcard)
  self
}

///|
pub fn Contacts::stringify(self : Contacts) -> String {
  let vcard_strings = self.vcards.map(fn(vcard) { vcard.stringify() })
  vcard_strings.join("\n")
}

// -----------------------------------------------------------

///|
pub fn fib(n : Int) -> Int64 {
  loop (n, 0L, 1L) {
    (0, _, b) => b
    (i, a, b) => continue (i - 1, b, a + b)
  }
}

///|
/// data is a labelled argument without default value having type Array[Int]
/// start is an optional labelled argument with default value 0 having type Int
/// length is an optional labelled argument without default value having type Option[Int]
pub fn sum(data~ : Array[Int], start? : Int = 0, length? : Int) -> Int {
  let end = if length is Some(length) { start + length } else { data.length() }
  for i = start, sum = 0; i < end; i = i + 1, sum = sum + data[i] {

  } else {
    sum
  }
}