import { Component, computed, effect, inject, input, untracked } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { FormFieldComponent } from '@shared/ui/form-field/form-field.component';
import { TextInputComponent } from '@shared/ui/text-input/text-input.component';
import { RadioGroupComponent, JA_NEE } from '@shared/ui/radio-group/radio-group.component';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component';
import { DataRowComponent } from '@shared/ui/data-row/data-row.component';
import { ReviewSectionComponent } from '@shared/ui/review-section/review-section.component';
import { ConfirmationComponent } from '@shared/ui/confirmation/confirmation.component';
import {
  WizardShellComponent,
  WizardError,
  WizardStatus,
  naarStapLabel,
} from '@shared/layout/wizard-shell/wizard-shell.component';
import { ASYNC } from '@shared/ui/async/async.component';
import { AddressFieldsComponent } from '@registratie/ui/address-fields/address-fields.component';
import { createStore } from '@shared/application/store';
import { whenTag } from '@shared/kernel/fp';
import { RemoteData } from '@shared/application/remote-data';
import { RegistratieLookupStore } from '@registratie/application/registratie-lookup.store';
import { DuoLookupDto, PolicyQuestionDto } from '@registratie/contracts/duo-diplomas.dto';
import {
  RegistratieState,
  RegistratieMsg,
  Draft,
  DraftField,
  Correspondentie,
  StepId,
  initial,
  reduce,
  hasProgress,
  STEPS,
} from '@registratie/domain/registratie-wizard.machine';
import { createDraftSync } from '@registratie/application/draft-sync';
import { DocumentUploadComponent } from '@shared/ui/upload/document-upload/document-upload.component';
import { createUploadController } from '@shared/upload/upload-controller';
import { UploadAdapter } from '@shared/upload/upload.adapter';
import { UploadState, initialUpload, deliveryRefs } from '@shared/upload/upload.machine';

const KANALEN = [
  { value: 'email', label: $localize`:@@registratie.kanaalEmail:E-mail` },
  { value: 'post', label: $localize`:@@registratie.kanaalPost:Post` },
];
const HANDMATIG = '__handmatig__'; // sentinel option:"my diploma isn't listed"
/** The server-owned geldigheidsvraag whose"ja" answer requires a Dutch-taalvaardigheid
    upload (proof of the confirmed B2 level). Stable id shared with the backend. */
const NL_TAALVAARDIGHEID_VRAAG = 'nl-taalvaardigheid';

/** Organism: the BIG-registration wizard. All state lives in one signal driven by
    the pure `reduce` (registratie-wizard.machine.ts). The BRP address prefills the
    draft via an effect; the DUO diploma list renders through <app-async>; choosing
    a diploma reveals its server-derived beroep. The draft is persisted to the
    backend as a Concept aanvraag (createDraftSync) so a reload — or a"Verder gaan"
    from the dashboard via `?aanvraag=<id>` — resumes progress. Built from existing
    atoms/molecules. */
@Component({
  selector: 'app-registratie-wizard',
  imports: [
    FormsModule,
    FormFieldComponent,
    TextInputComponent,
    RadioGroupComponent,
    ButtonComponent,
    AlertComponent,
    SkeletonComponent,
    DataRowComponent,
    ReviewSectionComponent,
    ConfirmationComponent,
    WizardShellComponent,
    AddressFieldsComponent,
    DocumentUploadComponent,
    ...ASYNC,
  ],
  template: `
    <app-wizard-shell
      [steps]="stepLabels"
      [current]="cursor()"
      [stepTitle]="stepTitle()"
      i18n-processName="@@regWizard.processName"
      processName="Inschrijven in het BIG-register"
      [status]="shellStatus()"
      [primaryLabel]="primaryLabel()"
      [canGoBack]="cursor() > 0"
      [errors]="errorList()"
      [errorMessage]="errorMessage()"
      i18n-submittingLabel="@@regWizard.submitting"
      submittingLabel="Uw registratie wordt verwerkt…"
      (primary)="onPrimary()"
      (back)="dispatch({ tag: 'Back' })"
      (cancel)="restart()"
      (retry)="onRetry()"
      (goToStep)="dispatch({ tag: 'GaNaarStap', cursor: $event })"
    >
      @switch (step()) {
        @case ('adres') {
          @if (adresStatus() === 'laden') {
            <app-skeleton height="2.5rem" [count]="4" />
          } @else {
            @switch (adresStatus()) {
              @case ('gevonden') {
                <app-alert type="info" i18n="@@regWizard.brpGevonden"
                  >Vooraf ingevuld op basis van de BRP. Controleer en pas zo nodig aan.</app-alert
                >
              }
              @case ('geen') {
                <app-alert type="warning" i18n="@@regWizard.brpGeen"
                  >We vonden geen adres in de BRP. Vul uw adres hieronder handmatig in.</app-alert
                >
              }
              @case ('fout') {
                <app-alert type="warning" i18n="@@regWizard.brpFout"
                  >We konden de BRP nu niet bereiken. Vul uw adres hieronder handmatig
                  in.</app-alert
                >
              }
            }
            <app-address-fields
              [value]="{
                straat: draft().straat ?? '',
                postcode: draft().postcode ?? '',
                woonplaats: draft().woonplaats ?? '',
              }"
              [errors]="{
                straat: err('straat'),
                postcode: err('postcode'),
                woonplaats: err('woonplaats'),
              }"
              (fieldChange)="set($event.key, $event.value)"
            />
            <app-form-field
              i18n-label="@@regWizard.correspondentieLabel"
              label="Hoe wilt u correspondentie ontvangen?"
              fieldId="correspondentie"
              required
              [error]="err('correspondentie')"
            >
              <app-radio-group
                name="correspondentie"
                [options]="kanalen"
                [invalid]="!!err('correspondentie')"
                [ngModel]="draft().correspondentie ?? ''"
                (ngModelChange)="setKanaal($event)"
              />
            </app-form-field>
            @if (draft().correspondentie === 'email') {
              <app-form-field
                i18n-label="@@regWizard.emailLabel"
                label="E-mailadres"
                fieldId="email"
                required
                [error]="err('email')"
              >
                <app-text-input
                  inputId="email"
                  type="email"
                  [invalid]="!!err('email')"
                  [ngModel]="draft().email ?? ''"
                  (ngModelChange)="set('email', $event)"
                  name="email"
                  i18n-placeholder="@@regWizard.emailPlaceholder"
                  placeholder="naam@voorbeeld.nl"
                />
              </app-form-field>
            }
          }
        }
        @case ('beroep') {
          <app-async [data]="lookupRd()">
            <ng-template appAsyncLoaded>
              @if (duoData(); as data) {
                <app-form-field
                  i18n-label="@@regWizard.diplomaLabel"
                  label="Kies het diploma waarmee u zich wilt registreren"
                  fieldId="diploma"
                  required
                  [error]="err('diploma')"
                >
                  <app-radio-group
                    name="diploma"
                    [options]="diplomaOptions(data)"
                    [invalid]="!!err('diploma')"
                    [ngModel]="diplomaKeuze()"
                    (ngModelChange)="onDiplomaKeuze(data, $event)"
                  />
                </app-form-field>

                @if (handmatigActief()) {
                  <app-alert type="warning" i18n="@@regWizard.handmatigWaarschuwing"
                    >Een handmatig ingevoerd diploma kan niet automatisch worden geverifieerd. Kies
                    uw beroep en beantwoord de aanvullende vragen; uw aanvraag wordt daarna
                    handmatig beoordeeld.</app-alert
                  >
                  <app-form-field
                    i18n-label="@@regWizard.beroepLabel"
                    label="Voor welk beroep wilt u zich registreren?"
                    fieldId="hm-beroep"
                    [error]="err('diploma')"
                  >
                    <app-radio-group
                      name="hm-beroep"
                      [options]="beroepOptions(data)"
                      [invalid]="!!err('diploma')"
                      [ngModel]="draft().beroep ?? ''"
                      (ngModelChange)="dispatch({ tag: 'DeclareerBeroep', beroep: $event })"
                    />
                  </app-form-field>
                } @else if (draft().beroep) {
                  <dl class="mb-0 app-section">
                    <div
                      app-data-row
                      i18n-key="@@regWizard.beroepAfgeleid"
                      key="Beroep (afgeleid uit diploma)"
                      [value]="draft().beroep ?? ''"
                    ></div>
                  </dl>
                }

                @for (q of actieveVragen(data); track q.id) {
                  <app-form-field
                    [label]="q.vraag"
                    [fieldId]="'vraag-' + q.id"
                    [error]="vraagErr(q.id)"
                  >
                    @if (q.type === 'ja-nee') {
                      <app-radio-group
                        [name]="'vraag-' + q.id"
                        [options]="jaNee"
                        [invalid]="!!vraagErr(q.id)"
                        [ngModel]="antwoord(q.id)"
                        (ngModelChange)="
                          dispatch({ tag: 'SetAntwoord', vraagId: q.id, value: $event })
                        "
                        [ngModelOptions]="{ standalone: true }"
                      />
                    } @else {
                      <app-text-input
                        [inputId]="'vraag-' + q.id"
                        [invalid]="!!vraagErr(q.id)"
                        [ngModel]="antwoord(q.id)"
                        (ngModelChange)="
                          dispatch({ tag: 'SetAntwoord', vraagId: q.id, value: $event })
                        "
                        [ngModelOptions]="{ standalone: true }"
                      />
                    }
                  </app-form-field>
                }
              }
            </ng-template>
            <ng-template appAsyncLoading>
              <app-skeleton height="2.5rem" [count]="3" />
            </ng-template>
          </app-async>

          <app-document-upload
            class="app-section"
            [state]="upload()"
            [previewUrlFor]="previewUrlFor"
            (fileSelected)="uploadCtl.onFileSelected($event.categoryId, $event.files)"
            (removeUpload)="uploadCtl.onRemove($event)"
            (retryUpload)="uploadCtl.onRetry($event)"
            (deleteUpload)="uploadCtl.onDelete($event)"
            (channelChange)="uploadCtl.onChannelChange($event.categoryId, $event.channel)"
          />
          @if (err('documenten')) {
            <app-alert type="warning">{{ err('documenten') }}</app-alert>
          }
        }
        @case ('controle') {
          <app-alert type="info" i18n="@@regWizard.controleer"
            >Controleer uw gegevens en dien de registratie in.</app-alert
          >
          <app-review-section
            i18n-heading="@@regWizard.sectie.adres"
            heading="Adres en correspondentie"
            i18n-editAriaLabel="@@regWizard.adresWijzigenAria"
            editAriaLabel="Wijzigen adresgegevens"
            (edit)="dispatch({ tag: 'GaNaarStap', cursor: 0 })"
          >
            <div
              app-data-row
              i18n-key="@@regWizard.summary.adres"
              key="Adres"
              [value]="adresSamenvatting()"
            ></div>
            <div
              app-data-row
              i18n-key="@@regWizard.summary.herkomstAdres"
              key="Herkomst adres"
              [value]="adresHerkomstLabel()"
            ></div>
            <div
              app-data-row
              i18n-key="@@regWizard.summary.correspondentie"
              key="Correspondentie"
              [value]="correspondentieLabel()"
            ></div>
            @if (draft().correspondentie === 'email') {
              <div
                app-data-row
                i18n-key="@@regWizard.summary.email"
                key="E-mailadres"
                [value]="draft().email ?? ''"
              ></div>
            }
          </app-review-section>
          <app-review-section
            class="app-section"
            i18n-heading="@@regWizard.sectie.beroep"
            heading="Beroep en diploma"
            i18n-editAriaLabel="@@regWizard.diplomaWijzigenAria"
            editAriaLabel="Wijzigen beroep en diploma"
            (edit)="dispatch({ tag: 'GaNaarStap', cursor: 1 })"
          >
            <div
              app-data-row
              i18n-key="@@regWizard.summary.beroep"
              key="Beroep"
              [value]="draft().beroep ?? ''"
            ></div>
            <div
              app-data-row
              i18n-key="@@regWizard.summary.herkomstDiploma"
              key="Herkomst diploma"
              [value]="diplomaHerkomstLabel()"
            ></div>
            @for (item of samenvattingVragen(); track item.vraag) {
              <div app-data-row [key]="item.vraag" [value]="item.antwoord"></div>
            }
          </app-review-section>
        }
      }

      <div wizardSuccess>
        <app-confirmation
          i18n-title="@@regWizard.success.title"
          title="Uw registratie is ontvangen"
        >
          <p class="app-section" i18n="@@regWizard.success.referentie">
            Uw referentienummer is {{ referentie() }}. Bewaar dit nummer voor uw administratie.
          </p>
          <div class="app-section">
            <app-button variant="secondary" (click)="restart()" i18n="@@regWizard.nieuweRegistratie"
              >Nieuwe registratie starten</app-button
            >
          </div>
        </app-confirmation>
      </div>
    </app-wizard-shell>
  `,
})
export class RegistratieWizardComponent {
  private lookup = inject(RegistratieLookupStore);
  private uploadAdapter = inject(UploadAdapter);
  private store = createStore<RegistratieState, RegistratieMsg>(initial, reduce);

  /** Preview/download link for a completed upload; the dev-simulation `demo-*` ids
      have no stored bytes, so they get no link. */
  protected previewUrlFor = (documentId: string): string | undefined =>
    documentId.startsWith('demo-') ? undefined : this.uploadAdapter.contentUrl(documentId);

  /** Optional seed so Storybook / tests can mount any state directly. */
  seed = input<RegistratieState>(initial);

  readonly kanalen = KANALEN;
  readonly stepLabels = [
    $localize`:@@regWizard.step.adres:Adres`,
    $localize`:@@regWizard.step.beroep:Beroep`,
    $localize`:@@regWizard.step.controle:Controle`,
  ]; // short labels for the stepper
  private stepTitles = [
    $localize`:@@regWizard.title.adres:Adres en correspondentievoorkeur`,
    $localize`:@@regWizard.title.beroep:Beroep op basis van uw diploma`,
    $localize`:@@regWizard.title.controle:Controleren en indienen`,
  ];
  readonly state = this.store.model;
  readonly dispatch = this.store.dispatch;

  private invullen = computed(() => whenTag(this.state(), 'Invullen'));
  protected cursor = computed(() => this.invullen()?.cursor ?? 0);
  protected draft = computed<Draft>(() => this.invullen()?.draft ?? { antwoorden: {} });
  protected upload = computed<UploadState>(() => this.invullen()?.upload ?? initialUpload);
  protected uploadCtl = createUploadController({
    wizardId: 'registratie',
    getUpload: () => this.upload(),
    dispatch: (msg) => this.dispatch({ tag: 'Upload', msg }),
    // Required documents depend on answers (server decides): a diploma upload only for a
    // manual diploma; a Dutch-taalvaardigheid upload only once the applicant confirms
    // ("ja") the B2 language requirement.
    getCategoryParams: () => ({
      diplomaHerkomst: this.draft().diplomaHerkomst,
      taalvaardigheid: this.draft().antwoorden[NL_TAALVAARDIGHEID_VRAAG],
    }),
  });
  // Backend draft-sync (replaces sessionStorage): create a Concept once the user has
  // made progress, then debounced-sync the whole machine snapshot; resume by `?aanvraag`.
  private draftSync = createDraftSync({
    type: 'registratie',
    snapshot: () => {
      const s = this.state();
      if (s.tag !== 'Invullen' || !hasProgress(s)) return null;
      const documentIds = deliveryRefs(s.upload)
        .filter((r) => r.channel === 'digital' && r.documentId)
        .map((r) => r.documentId!);
      return { draft: s, stepIndex: s.cursor, stepCount: STEPS.length, documentIds };
    },
    onResume: (draft) => this.dispatch({ tag: 'Seed', state: draft as RegistratieState }),
    enabled: () => this.seed() === initial,
  });
  protected step = computed<StepId>(() => STEPS[Math.min(this.cursor(), STEPS.length - 1)]);
  protected stepTitle = computed(
    () => this.stepTitles[Math.min(this.cursor(), this.stepTitles.length - 1)],
  );
  protected referentie = computed(() => whenTag(this.state(), 'Ingediend')?.referentie ?? '');
  protected failedError = computed(() => whenTag(this.state(), 'Mislukt')?.error ?? '');

  // --- Presentational wiring for the shared wizard shell ---------------------
  protected primaryLabel = computed(() => {
    if (this.step() === 'controle') return $localize`:@@regWizard.indienen:Registratie indienen`;
    const next = this.cursor() + 1;
    return naarStapLabel(next + 1, this.stepLabels[next]);
  });
  protected errorMessage = computed(
    () =>
      $localize`:@@regWizard.indienenMislukt:Het indienen is niet gelukt:` +
      ` ${this.failedError()}`,
  );
  protected shellStatus = computed<WizardStatus>(() => {
    switch (this.state().tag) {
      case 'Invullen':
        return 'editing';
      case 'Indienen':
        return 'submitting';
      case 'Ingediend':
        return 'submitted';
      case 'Mislukt':
        return 'failed';
    }
  });
  /** Current step's errors (incl. per-question), flattened for the error summary. */
  protected errorList = computed<WizardError[]>(() => {
    const e = this.invullen()?.errors ?? {};
    const out: WizardError[] = [];
    for (const [k, v] of Object.entries(e)) {
      if (k !== 'antwoorden' && typeof v === 'string' && v) out.push({ id: k, message: v });
    }
    for (const [qid, msg] of Object.entries(e.antwoorden ?? {})) {
      if (msg) out.push({ id: 'vraag-' + qid, message: msg });
    }
    return out;
  });
  protected adresSamenvatting = computed(() => {
    const d = this.draft();
    return [d.straat, [d.postcode, d.woonplaats].filter(Boolean).join(' ')]
      .filter(Boolean)
      .join(', ');
  });
  // Readable labels for the controle summary (instead of raw enum values).
  protected adresHerkomstLabel = computed(
    () =>
      ({
        brp: $localize`:@@regWizard.herkomst.adresBrp:Automatisch uit de BRP`,
        handmatig: $localize`:@@regWizard.herkomst.adresHandmatig:Handmatig ingevoerd`,
      })[this.draft().adresHerkomst ?? 'handmatig'],
  );
  protected correspondentieLabel = computed(
    () =>
      ({
        email: $localize`:@@regWizard.corr.email:Per e-mail`,
        post: $localize`:@@regWizard.corr.post:Per post`,
      })[this.draft().correspondentie ?? 'post'],
  );
  protected diplomaHerkomstLabel = computed(
    () =>
      ({
        duo: $localize`:@@regWizard.herkomst.diplomaDuo:Geverifieerd via DUO`,
        handmatig: $localize`:@@regWizard.herkomst.diplomaHandmatig:Handmatig ingevoerd (wordt beoordeeld)`,
      })[this.draft().diplomaHerkomst ?? 'handmatig'],
  );

  /** BRP lookup outcome (laden/gevonden/geen/fout) and the parsed DUO lookup, both
      served by the application facade — the wizard renders, it does not fetch/parse. */
  protected adresStatus = this.lookup.adresStatus;
  protected lookupRd: () => RemoteData<Error | undefined, DuoLookupDto> = this.lookup.duoLookup;

  /** Parsed lookup as a plain value (or null) — used outside the beroep step (the
      controle summary) where the <app-async> template variable isn't in scope, and
      inside it too: `<ng-template appAsyncLoaded>`'s own context can't inherit a
      generic from the sibling [data] input (Angular only infers a structural
      directive's type parameter from an input on that same node). */
  protected duoData = computed<DuoLookupDto | null>(() => {
    const rd = this.lookupRd();
    return rd.tag === 'Success' ? rd.value : null;
  });

  readonly jaNee = JA_NEE;

  protected err = (k: DraftField | 'correspondentie' | 'diploma' | 'documenten') =>
    this.invullen()?.errors[k] ?? '';
  protected vraagErr = (id: string) => this.invullen()?.errors.antwoorden?.[id] ?? '';
  protected antwoord = (id: string) => this.draft().antwoorden[id] ?? ''; // runtime guard: missing key → undefined
  protected set = (key: DraftField, value: string) =>
    this.dispatch({ tag: 'SetField', key, value });
  protected setKanaal = (value: string) =>
    this.dispatch({ tag: 'SetCorrespondentie', value: value as Correspondentie });

  /** True while the user is entering a diploma manually (not in the DUO list). */
  protected handmatigActief = computed(() => this.draft().diplomaHerkomst === 'handmatig');
  /** The radio selection: a diploma id, or the"not listed" sentinel in manual mode. */
  protected diplomaKeuze = computed(() =>
    this.handmatigActief() ? HANDMATIG : (this.draft().diplomaId ?? ''),
  );

  protected diplomaOptions = (data: DuoLookupDto) => [
    ...data.diplomas.map((d) => ({
      value: d.id,
      label: `${d.naam} — ${d.instelling} (${d.jaar})`,
    })),
    {
      value: HANDMATIG,
      label: $localize`:@@regWizard.diplomaNietBij:Mijn diploma staat er niet bij`,
    },
  ];

  protected beroepOptions = (data: DuoLookupDto) =>
    data.handmatig.beroepen.map((b) => ({ value: b, label: b }));

  /** The policy questions that apply to the current choice (server-decided). */
  protected actieveVragen = (data: DuoLookupDto): PolicyQuestionDto[] => {
    if (this.handmatigActief()) return data.handmatig.policyQuestions;
    return data.diplomas.find((d) => d.id === this.draft().diplomaId)?.policyQuestions ?? [];
  };

  /** Answered policy questions for the controle summary (question text + answer). */
  protected samenvattingVragen = computed(() => {
    const data = this.duoData();
    const d = this.draft();
    if (!data) return [] as { vraag: string; antwoord: string }[];
    const alle = [
      ...data.diplomas.flatMap((x) => x.policyQuestions),
      ...data.handmatig.policyQuestions,
    ];
    return (d.vraagIds ?? []).map((id) => ({
      vraag: alle.find((q) => q.id === id)?.vraag ?? id,
      antwoord: d.antwoorden[id] ?? '',
    }));
  });

  protected onDiplomaKeuze(data: DuoLookupDto, id: string) {
    if (id === HANDMATIG) {
      this.dispatch({
        tag: 'KiesHandmatig',
        vraagIds: data.handmatig.policyQuestions.map((q) => q.id),
      });
      return;
    }
    const d = data.diplomas.find((x) => x.id === id);
    if (d)
      this.dispatch({
        tag: 'KiesDiploma',
        diplomaId: d.id,
        beroep: d.beroep,
        vraagIds: d.policyQuestions.map((q) => q.id),
      });
  }

  constructor() {
    // An explicit seed (stories/tests) wins; otherwise resume from the backend draft
    // (`?aanvraag=<id>`), or start fresh. Persistence is the draftSync controller's job.
    const seeded = this.seed();
    queueMicrotask(() =>
      seeded !== initial ? this.dispatch({ tag: 'Seed', state: seeded }) : this.draftSync.resume(),
    );
    // Prefill the address from the BRP lookup as it arrives. Track only the facade's
    // parsed prefill signal; untrack the dispatch (it reads the state signal, which
    // would otherwise make this effect loop on its own write). Don't clobber
    // edits/restored data. A null prefill (loading/error/geen adres) leaves manual entry.
    effect(() => {
      const a = this.lookup.prefillAdres();
      if (!a) return;
      untracked(() => {
        const s = this.state();
        if (s.tag !== 'Invullen' || s.draft.straat) return;
        this.dispatch({
          tag: 'PrefillAdres',
          straat: a.straat,
          postcode: a.postcode,
          woonplaats: a.woonplaats,
        });
      });
    });
    // A11y: focus management (step heading on step change, error summary on a
    // failed submit) now lives in the shared WizardShellComponent.
  }

  onPrimary() {
    const s = this.state();
    if (s.tag !== 'Invullen') return;
    this.dispatch(this.step() === 'controle' ? { tag: 'Submit' } : { tag: 'Next' });
    this.runIfIndienen();
  }

  onRetry() {
    this.dispatch({ tag: 'Retry' });
    this.runIfIndienen();
  }

  /** Reset the wizard to a fresh start. Reload the BRP lookup so the address
      re-prefills, keeping the form and the"vooraf ingevuld" note consistent. */
  restart() {
    this.draftSync.reset(); // discard the current Concept; a fresh one starts on next progress
    this.dispatch({ tag: 'Seed', state: initial });
    this.lookup.reloadAdres();
  }

  /** The effect: when we enter Indienen, submit through the aanvraag lifecycle
      (duo → auto-approve, handmatig → manual), then dispatch the outcome. */
  private async runIfIndienen() {
    const s = this.state();
    if (s.tag !== 'Indienen') return;
    const r = await this.draftSync.submit({
      diplomaHerkomst: s.data.diplomaHerkomst,
      documents: s.data.documents,
    });
    if (r.ok) this.dispatch({ tag: 'SubmitConfirmed', referentie: r.value.referentie ?? '' });
    else this.dispatch({ tag: 'SubmitFailed', error: r.error });
  }
}
