<?xml version="1.0" encoding="UTF-8"?>
<!--
  ProcessSchema.v1.0

  Declarative back-end pipeline. One <Process> XML maps onto the runtime
  ProcessRunner / ProcessBase / IProcess triad in ModelGeneratorCsRuntime.Processes
  (C#) and model_generator_python_runtime.processes (Python).

  The generator turns one of these XMLs into:
    * a typed Input record/dataclass (immutable caller input)
    * a typed Data class (mutable working state shared across steps)
    * a typed Result record/dataclass (read-only output)
    * an abstract Base{Name}Process that wires up the runner and declares one
      abstract method per <Step> (the AI-implement step or the dev fills the bodies)
    * a {Name}Process stub (user-owned, WriteIfAbsent) that overrides each step
      with `// AI GENERATION REQUIRED` markers

  Step modes mirror the runtime StepMode enum: Sync, Async, Parallel (consecutive
  Parallel steps are batched into a single Task.WhenAll / asyncio.gather), and
  Background (runs after the caller has received the Result).
-->
<xs:schema
  xmlns="https://tnventures.fi/modelgenerator/xmlschemas/process/v1"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  targetNamespace="https://tnventures.fi/modelgenerator/xmlschemas/process/v1"
  elementFormDefault="qualified">

  <!-- ============================================================================
       Root element
       ============================================================================ -->
  <xs:element name="Process">
    <xs:complexType>
      <xs:annotation>
        <xs:documentation>A back-end pipeline: an immutable Input projected into a mutable Data, walked by a list of named Steps grouped by execution mode, finally projected into a read-only Result handed back to the caller. Maps onto ProcessRunner&lt;TInput, TData, TResult&gt; at runtime.</xs:documentation>
      </xs:annotation>
      <xs:sequence>
        <xs:element name="Input"  type="PropertyListType">
          <xs:annotation><xs:documentation>Fields the caller hands to RunAsync. Emitted as an immutable record (C#) / frozen dataclass (Python).</xs:documentation></xs:annotation>
        </xs:element>
        <xs:element name="Data"   type="PropertyListType">
          <xs:annotation><xs:documentation>Mutable working object shared across steps. Step methods read + write Data via the protected `Data` field on the generated base class. Init from Input is auto-generated for any same-named field; extra fields stay default-initialised.</xs:documentation></xs:annotation>
        </xs:element>
        <xs:element name="Result" type="PropertyListType">
          <xs:annotation><xs:documentation>Fields handed back to the caller. Emitted as a read-only record (C#) / dataclass (Python). Auto-projected from Data by the generated BuildResult.</xs:documentation></xs:annotation>
        </xs:element>
        <xs:element name="Steps">
          <xs:annotation>
            <xs:documentation>The pipeline. Steps run in declaration order; consecutive Parallel ones are batched. Each Step becomes an abstract method on the generated base + an AI-stub override on the user-owned impl class.</xs:documentation>
          </xs:annotation>
          <xs:complexType>
            <xs:sequence>
              <xs:element name="Step" type="StepType" minOccurs="1" maxOccurs="unbounded"/>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
      <xs:attribute name="name"          type="IdentifierType" use="required"/>
      <xs:attribute name="namespace"     type="xs:string"      use="required"/>
      <xs:attribute name="documentation" type="xs:string"      use="optional"/>
      <xs:attribute name="tracking"      type="xs:boolean"     use="optional" default="true">
        <xs:annotation>
          <xs:documentation>When true (the default), the runtime auto-resolves a tracking store via ProcessTrackingResolver — DB-backed when AppSettings:Process:Tracking:ConnectionKey is configured, in-memory otherwise. No ctor parameter at the call site: derived classes need NO constructor; callers stay on `new OrderProcess().RunAsync(input)`. Set to false only when you want the resolver bypass to be the type-level default (rare — usually leaving the config key unset gives the same effect).</xs:documentation>
        </xs:annotation>
      </xs:attribute>
      <xs:attribute name="key1"          type="xs:string"      use="optional">
        <xs:annotation>
          <xs:documentation>Optional: names the Input property whose value populates the tracking row's key1_name/key1_value columns at run start. Generator emits `SetKey1("X", input.X?.ToString() ?? "")` at the top of InitData. Lets operators find this run via IProcessTrackingStore.FindByKeyAsync("X", "value") without remembering the system run_id. Validated at parse time — must reference an existing Input property name.</xs:documentation>
        </xs:annotation>
      </xs:attribute>
      <xs:attribute name="key2"          type="xs:string"      use="optional">
        <xs:annotation>
          <xs:documentation>Optional second key slot — same semantics as key1. Use when one run is queryable by more than one business handle.</xs:documentation>
        </xs:annotation>
      </xs:attribute>
      <xs:attribute name="key3"          type="xs:string"      use="optional">
        <xs:annotation>
          <xs:documentation>Optional third (and last) key slot — same semantics as key1. Further dimensions belong in a separate audit table joined on run_id.</xs:documentation>
        </xs:annotation>
      </xs:attribute>
    </xs:complexType>
  </xs:element>

  <!-- ============================================================================
       Property list (used by Input / Data / Result)
       ============================================================================ -->
  <xs:complexType name="PropertyListType">
    <xs:sequence>
      <xs:element name="Property" type="PropertyType" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="PropertyType">
    <xs:attribute name="name"            type="IdentifierType"  use="required"/>
    <xs:attribute name="type"            type="VariableTypeEnum" use="required"/>
    <xs:attribute name="model"           type="IdentifierType"  use="optional"/>
    <xs:attribute name="object"          type="IdentifierType"  use="optional"/>
    <xs:attribute name="objectNamespace" type="xs:string"       use="optional"/>
    <xs:attribute name="collection"      type="CollectionTypeEnum" use="optional" default="None"/>
    <xs:attribute name="mandatory"       type="xs:boolean"      use="optional" default="false"/>
    <xs:attribute name="documentation"   type="xs:string"       use="optional"/>
  </xs:complexType>

  <!-- ============================================================================
       Step
       ============================================================================ -->
  <xs:complexType name="StepType">
    <xs:annotation>
      <xs:documentation>One step in the pipeline. The generator emits a Base{Name} class with one abstract method per step, plus a user-owned {Name} class that overrides each. The override body is either a deterministic forward (when &lt;Implementation kind="DomainMethod"…/&gt; is present — same shape Service methods use) or an `// AI GENERATION REQUIRED` stub for the implement step to fill, using the step's `documentation` attribute as the spec.</xs:documentation>
    </xs:annotation>
    <xs:sequence>
      <xs:element name="Implementation" type="StepImplementationType" minOccurs="0">
        <xs:annotation>
          <xs:documentation>Optional declarative binding to a hand-authored domain-service method. When present, the generator emits the impl class's override body as `new {DomainService}().{Method}(Data)` (await-prefixed for non-Sync modes) instead of an AI stub. Omit to keep the step AI-implemented.</xs:documentation>
        </xs:annotation>
      </xs:element>
    </xs:sequence>
    <xs:attribute name="name"          type="IdentifierType" use="required"/>
    <xs:attribute name="mode"          type="StepModeEnum"   use="required"/>
    <xs:attribute name="documentation" type="xs:string"      use="optional">
      <xs:annotation>
        <xs:documentation>One- or two-line description of what the step does. Surfaced verbatim in the generated abstract method's XML doc, so the AI-implement step (and any human reader) sees it as the spec for the body. Required in spirit for AI-implemented steps; ignored for declarative-binding steps.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
  </xs:complexType>

  <xs:complexType name="StepImplementationType">
    <xs:attribute name="kind"          type="StepImplementationKindEnum" use="required"/>
    <xs:attribute name="domainService" type="IdentifierType"             use="optional">
      <xs:annotation>
        <xs:documentation>Set when `kind="DomainMethod"`. Name of the hand-authored domain-service class the impl class instantiates. Mutually exclusive with `service`. C# emits `new {DomainService}()`; Python emits `{DomainService}()` (paired with the import line driven by `pythonModule`).</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="service"       type="IdentifierType"             use="optional">
      <xs:annotation>
        <xs:documentation>Set when `kind="ServiceMethod"`. Name of a sibling generated service class. Mutually exclusive with `domainService`. Use this when the step's work is best expressed as a call into another Service in the same backend (a generated REST handler, an existing CRUD endpoint, etc.).</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="process"       type="IdentifierType"             use="optional">
      <xs:annotation>
        <xs:documentation>Set when `kind="ProcessMethod"`. Name of a sibling generated Process. The parent's step runs the entire sub-Process via `RunAsync`: same-named fields from the parent's Data flow into the sub's Input automatically, the call is awaited, the parent throws on sub-failure, and any same-named fields from the sub's Result are copied back into the parent's Data.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="method"        type="IdentifierType"             use="required">
      <xs:annotation>
        <xs:documentation>Method on the domain-service class to call. Signature contract: takes the generated Data type as its single argument (`void Method(MyProcessData data)` for Sync mode; `Task Method(MyProcessData data)` otherwise). The method mutates Data in place.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="namespace"     type="xs:string"                  use="optional">
      <xs:annotation>
        <xs:documentation>Optional C# namespace of the target class. When set the C# generator emits a `using` line at the top of the impl file so the bare class name resolves. Python is unaffected — Python consumers add the `from … import …` manually in their impl file (which is WriteIfAbsent-preserved). Kept implementation-agnostic at the XML level: this is the same `namespace` attribute every Service / Process XML root carries, just re-used to name a reference's target.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
  </xs:complexType>

  <xs:simpleType name="StepImplementationKindEnum">
    <xs:restriction base="xs:string">
      <xs:enumeration value="DomainMethod">
        <xs:annotation><xs:documentation>Forward the step to a hand-authored domain-service method. Pair with `domainService=` + `method=`.</xs:documentation></xs:annotation>
      </xs:enumeration>
      <xs:enumeration value="ServiceMethod">
        <xs:annotation><xs:documentation>Forward the step to a sibling generated service method. Pair with `service=` + `method=`.</xs:documentation></xs:annotation>
      </xs:enumeration>
      <xs:enumeration value="ProcessMethod">
        <xs:annotation><xs:documentation>Forward the step to a sibling generated Process. Pair with `process=`. The parent's step runs the sub-Process as one atomic unit; `method=` is ignored (sub-Process entry point is always `RunAsync`).</xs:documentation></xs:annotation>
      </xs:enumeration>
    </xs:restriction>
  </xs:simpleType>

  <!-- ============================================================================
       Enums
       ============================================================================ -->
  <xs:simpleType name="StepModeEnum">
    <xs:restriction base="xs:string">
      <xs:enumeration value="Sync">       <xs:annotation><xs:documentation>Run synchronously inline; block the caller thread until the step returns.</xs:documentation></xs:annotation></xs:enumeration>
      <xs:enumeration value="Async">      <xs:annotation><xs:documentation>Awaited normally — sequential, non-blocking.</xs:documentation></xs:annotation></xs:enumeration>
      <xs:enumeration value="Parallel">   <xs:annotation><xs:documentation>Batched with consecutive Parallel steps into a single concurrent fan-out. Steps in the batch must write to distinct Data fields to avoid races.</xs:documentation></xs:annotation></xs:enumeration>
      <xs:enumeration value="Background"> <xs:annotation><xs:documentation>Runs after Result has been returned to the caller. Failures are observed (via the runtime's step observer) but don't propagate.</xs:documentation></xs:annotation></xs:enumeration>
    </xs:restriction>
  </xs:simpleType>

  <!-- Mirrors the subset of ModelSchema's variable-type vocabulary that's meaningful inside a
       process's typed Input/Data/Result. No File/FilePath/Enum here yet (out of scope for the
       first pass); add them when a real use case shows up. Json was added to carry raw wizard
       step payloads (WizardSessionStepData.Data is `type="Json"`) so Processes that consume
       step data don't have to wrap it in a Model shim. Object covers framework types whose
       definition lives outside the Model registry (e.g. AuthenticationReply, ApplicationContext)
       — same `object="..."` + optional `objectNamespace="..."` attribute pair that the Service
       schema uses for parameters. -->
  <xs:simpleType name="VariableTypeEnum">
    <xs:restriction base="xs:string">
      <xs:enumeration value="String"/>
      <xs:enumeration value="Int"/>
      <xs:enumeration value="Decimal"/>
      <xs:enumeration value="Money"/>
      <xs:enumeration value="Bool"/>
      <xs:enumeration value="DateTime"/>
      <xs:enumeration value="Date"/>
      <xs:enumeration value="Id"/>
      <xs:enumeration value="Model"/>
      <xs:enumeration value="Json"/>
      <xs:enumeration value="Object"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="CollectionTypeEnum">
    <xs:restriction base="xs:string">
      <xs:enumeration value="None"/>
      <xs:enumeration value="List"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="IdentifierType">
    <xs:restriction base="xs:string">
      <xs:pattern value="[A-Za-z_][A-Za-z0-9_]*"/>
    </xs:restriction>
  </xs:simpleType>
</xs:schema>
