object {{ name }} {
{% if codecs == "inline" and has_validation %}
  private val rawDecoder: Decoder[{{ name }}] = deriveDecoder[{{ name }}]
  implicit val decoder: Decoder[{{ name }}] = rawDecoder.emap { instance =>
    validate(instance) match {
      case Nil    => Right(instance)
      case errors => Left(errors.mkString("; "))
    }
  }
  implicit val encoder: Encoder[{{ name }}] = deriveEncoder[{{ name }}]

  def fromJson(json: String): Either[io.circe.Error, {{ name }}] =
    io.circe.parser.decode[{{ name }}](json)
  def toJson(instance: {{ name }}): String =
    encoder(instance).noSpaces
  def fromYaml(yaml: String): Either[io.circe.Error, {{ name }}] =
    io.circe.yaml.parser.parse(yaml).flatMap(_.as[{{ name }}])
  def toYaml(instance: {{ name }}): String =
    io.circe.yaml.Printer().pretty(encoder(instance))
{% elif codecs == "inline" %}
  implicit val decoder: Decoder[{{ name }}] = deriveDecoder[{{ name }}]
  implicit val encoder: Encoder[{{ name }}] = deriveEncoder[{{ name }}]

  def fromJson(json: String): Either[io.circe.Error, {{ name }}] =
    io.circe.parser.decode[{{ name }}](json)
  def toJson(instance: {{ name }}): String =
    encoder(instance).noSpaces
  def fromYaml(yaml: String): Either[io.circe.Error, {{ name }}] =
    io.circe.yaml.parser.parse(yaml).flatMap(_.as[{{ name }}])
  def toYaml(instance: {{ name }}): String =
    io.circe.yaml.Printer().pretty(encoder(instance))
{% endif %}
{% if has_validation %}
  def validate(instance: {{ name }}): List[String] = {
    val errors = List.newBuilder[String]
{% for field in fields %}
{% if field.pattern %}
{% if "Option[" in field.scala_type %}
    if (!instance.{{ field.name }}.forall(_.matches("{{ field.pattern }}")))
      errors += "{{ field.name }} must match {{ field.pattern }}"
{% else %}
    if (!instance.{{ field.name }}.matches("{{ field.pattern }}"))
      errors += "{{ field.name }} must match {{ field.pattern }}"
{% endif %}
{% endif %}
{% if field.minimum_value is not none and field.maximum_value is not none %}
{% if "Option[" in field.scala_type %}
    if (!instance.{{ field.name }}.forall(v => v >= {{ field.minimum_value | int }} && v <= {{ field.maximum_value | int }}))
      errors += "{{ field.name }} must be between {{ field.minimum_value | int }} and {{ field.maximum_value | int }}"
{% else %}
    if (!(instance.{{ field.name }} >= {{ field.minimum_value | int }} && instance.{{ field.name }} <= {{ field.maximum_value | int }}))
      errors += "{{ field.name }} must be between {{ field.minimum_value | int }} and {{ field.maximum_value | int }}"
{% endif %}
{% elif field.minimum_value is not none %}
{% if "Option[" in field.scala_type %}
    if (!instance.{{ field.name }}.forall(_ >= {{ field.minimum_value | int }}))
      errors += "{{ field.name }} must be >= {{ field.minimum_value | int }}"
{% else %}
    if (!(instance.{{ field.name }} >= {{ field.minimum_value | int }}))
      errors += "{{ field.name }} must be >= {{ field.minimum_value | int }}"
{% endif %}
{% elif field.maximum_value is not none %}
{% if "Option[" in field.scala_type %}
    if (!instance.{{ field.name }}.forall(_ <= {{ field.maximum_value | int }}))
      errors += "{{ field.name }} must be <= {{ field.maximum_value | int }}"
{% else %}
    if (!(instance.{{ field.name }} <= {{ field.maximum_value | int }}))
      errors += "{{ field.name }} must be <= {{ field.maximum_value | int }}"
{% endif %}
{% endif %}
{% if field.maximum_cardinality is not none %}
{% if "List[" in field.scala_type %}
    if (instance.{{ field.name }}.size > {{ field.maximum_cardinality }})
      errors += "{{ field.name }} must have at most {{ field.maximum_cardinality }} elements"
{% endif %}
{% endif %}
{% if field.minimum_cardinality is not none and field.minimum_cardinality > 0 %}
{% if "List[" in field.scala_type %}
    if (instance.{{ field.name }}.size < {{ field.minimum_cardinality }})
      errors += "{{ field.name }} must have at least {{ field.minimum_cardinality }} elements"
{% endif %}
{% endif %}
{% if field.equals_string %}
{% if "Option[" in field.scala_type %}
    if (!instance.{{ field.name }}.forall(_ == "{{ field.equals_string }}"))
      errors += "{{ field.name }} must equal '{{ field.equals_string }}'"
{% else %}
    if (instance.{{ field.name }} != "{{ field.equals_string }}")
      errors += "{{ field.name }} must equal '{{ field.equals_string }}'"
{% endif %}
{% endif %}
{% if field.equals_string_in %}
{% if "Option[" in field.scala_type %}
    if (!instance.{{ field.name }}.forall(v => Set({{ field.equals_string_in | map('tojson') | join(', ') }}).contains(v)))
      errors += "{{ field.name }} must be one of: {{ field.equals_string_in | join(', ') }}"
{% else %}
    if (!Set({{ field.equals_string_in | map('tojson') | join(', ') }}).contains(instance.{{ field.name }}))
      errors += "{{ field.name }} must be one of: {{ field.equals_string_in | join(', ') }}"
{% endif %}
{% endif %}
{% if field.equals_number is not none %}
{% if "Option[" in field.scala_type %}
    if (!instance.{{ field.name }}.forall(_ == {{ field.equals_number }}))
      errors += "{{ field.name }} must equal {{ field.equals_number }}"
{% else %}
    if (instance.{{ field.name }} != {{ field.equals_number }})
      errors += "{{ field.name }} must equal {{ field.equals_number }}"
{% endif %}
{% endif %}
{% if field.exact_cardinality is not none %}
{% if "List[" in field.scala_type %}
    if (instance.{{ field.name }}.size != {{ field.exact_cardinality }})
      errors += "{{ field.name }} must have exactly {{ field.exact_cardinality }} elements"
{% endif %}
{% endif %}
{% if field.value_presence == "PRESENT" %}
{% if "Option[" in field.scala_type %}
    if (instance.{{ field.name }}.isEmpty)
      errors += "{{ field.name }} must be present"
{% endif %}
{% elif field.value_presence == "ABSENT" %}
{% if "Option[" in field.scala_type %}
    if (instance.{{ field.name }}.isDefined)
      errors += "{{ field.name }} must be absent"
{% endif %}
{% endif %}
{% endfor %}
    errors.result()
  }
{% endif %}
{% for rule in rules %}

{% if rule.open_world %}
  // Note: open world assumption — {{ rule.description }}
{% endif %}
  /** {{ rule.description }} */
  def {{ rule.name }}(instance: {{ name }}): List[String] = {
    val errors = List.newBuilder[String]
{% if rule.preconditions %}
    val preconditionsMet =
{% for cond in rule.preconditions %}
{% if cond.is_optional %}
      instance.{{ cond.field }} match {
        case Some(v) => v {{ cond.op }} {{ cond.value }}
        case None => false
      }{% if not loop.last %} &&{% endif %}
{% else %}
      (instance.{{ cond.field }} {{ cond.op }} {{ cond.value }}){% if not loop.last %} &&{% endif %}
{% endif %}

{% endfor %}
    if (preconditionsMet) {
{% for cond in rule.postconditions %}
{% if cond.is_optional %}
      instance.{{ cond.field }} match {
        case Some(v) if !(v {{ cond.op }} {{ cond.value }}) =>
          errors += "{{ rule.description }}: {{ cond.field }} must {{ cond.op }} {{ cond.error_value }}"
        case None =>
          errors += "{{ rule.description }}: {{ cond.field }} is required"
        case _ => ()
      }
{% else %}
      if (!(instance.{{ cond.field }} {{ cond.op }} {{ cond.value }}))
        errors += "{{ rule.description }}: {{ cond.field }} must {{ cond.op }} {{ cond.error_value }}"
{% endif %}
{% endfor %}
{% if rule.elseconditions %}
    } else {
{% for cond in rule.elseconditions %}
{% if cond.is_optional %}
      instance.{{ cond.field }} match {
        case Some(v) if !(v {{ cond.op }} {{ cond.value }}) =>
          errors += "{{ rule.description }}: {{ cond.field }} must {{ cond.op }} {{ cond.error_value }}"
        case None =>
          errors += "{{ rule.description }}: {{ cond.field }} is required"
        case _ => ()
      }
{% else %}
      if (!(instance.{{ cond.field }} {{ cond.op }} {{ cond.value }}))
        errors += "{{ rule.description }}: {{ cond.field }} must {{ cond.op }} {{ cond.error_value }}"
{% endif %}
{% endfor %}
    }
{% else %}
    }
{% endif %}
{% else %}
{% for cond in rule.postconditions %}
{% if cond.is_optional %}
    instance.{{ cond.field }} match {
      case Some(v) if !(v {{ cond.op }} {{ cond.value }}) =>
        errors += "{{ rule.description }}: {{ cond.field }} must {{ cond.op }} {{ cond.error_value }}"
      case None =>
        errors += "{{ rule.description }}: {{ cond.field }} is required"
      case _ => ()
    }
{% else %}
    if (!(instance.{{ cond.field }} {{ cond.op }} {{ cond.value }}))
      errors += "{{ rule.description }}: {{ cond.field }} must {{ cond.op }} {{ cond.error_value }}"
{% endif %}
{% endfor %}
{% endif %}
    errors.result()
  }
{% if rule.bidirectional %}

  /** {{ rule.description }} (reverse) */
  def {{ rule.name }}Reverse(instance: {{ name }}): List[String] = {
    val errors = List.newBuilder[String]
{% if rule.postconditions %}
    val preconditionsMet =
{% for cond in rule.postconditions %}
{% if cond.is_optional %}
      instance.{{ cond.field }} match {
        case Some(v) => v {{ cond.op }} {{ cond.value }}
        case None => false
      }{% if not loop.last %} &&{% endif %}
{% else %}
      (instance.{{ cond.field }} {{ cond.op }} {{ cond.value }}){% if not loop.last %} &&{% endif %}
{% endif %}

{% endfor %}
    if (preconditionsMet) {
{% for cond in rule.preconditions %}
{% if cond.is_optional %}
      instance.{{ cond.field }} match {
        case Some(v) if !(v {{ cond.op }} {{ cond.value }}) =>
          errors += "{{ rule.description }}: {{ cond.field }} must {{ cond.op }} {{ cond.error_value }}"
        case None =>
          errors += "{{ rule.description }}: {{ cond.field }} is required"
        case _ => ()
      }
{% else %}
      if (!(instance.{{ cond.field }} {{ cond.op }} {{ cond.value }}))
        errors += "{{ rule.description }}: {{ cond.field }} must {{ cond.op }} {{ cond.error_value }}"
{% endif %}
{% endfor %}
    }
{% else %}
{% for cond in rule.preconditions %}
{% if cond.is_optional %}
    instance.{{ cond.field }} match {
      case Some(v) if !(v {{ cond.op }} {{ cond.value }}) =>
        errors += "{{ rule.description }}: {{ cond.field }} must {{ cond.op }} {{ cond.error_value }}"
      case None =>
        errors += "{{ rule.description }}: {{ cond.field }} is required"
      case _ => ()
    }
{% else %}
    if (!(instance.{{ cond.field }} {{ cond.op }} {{ cond.value }}))
      errors += "{{ rule.description }}: {{ cond.field }} must {{ cond.op }} {{ cond.error_value }}"
{% endif %}
{% endfor %}
{% endif %}
    errors.result()
  }
{% endif %}
{% endfor %}
}
