import { Component, computed, inject, input } 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 { AlertComponent } from '@shared/ui/alert/alert.component';
import { WizardShellComponent, WizardError, WizardStatus } from '@shared/layout/wizard-shell/wizard-shell.component';
import { createStore } from '@shared/application/store';
import { whenTag } from '@shared/kernel/fp';
import { BigProfileStore } from '@registratie/application/big-profile.store';
import { WizardState, WizardMsg, Draft, initial, reduce, hasProgress } from '@herregistratie/domain/herregistratie.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';

/** Organism: multi-step herregistratie wizard. ALL state lives in one signal
    driven by the pure `reduce` function (see herregistratie.machine.ts) via an
    Elm-style store. The UI just sends messages and folds over the state's tag —
    no booleans like `submitting`/`submitted` that could contradict each other.
    Submitting also flips an optimistic flag on the shared BigProfileStore, so
    the dashboard shows "in behandeling" immediately. */
@Component({
  selector: 'app-herregistratie-wizard',
  imports: [FormsModule, FormFieldComponent, TextInputComponent, AlertComponent, WizardShellComponent, DocumentUploadComponent],
  template: `
    <app-wizard-shell
      [steps]="stepLabels"
      [current]="step() - 1"
      [stepTitle]="stepTitle()"
      [status]="shellStatus()"
      [primaryLabel]="primaryLabel()"
      [canGoBack]="step() > 1"
      [errors]="errorList()"
      [errorMessage]="errorMessage()"
      (primary)="onPrimary()"
      (back)="dispatch({ tag: 'Back' })"
      (cancel)="restart()"
      (retry)="onRetry()">

      @switch (step()) {
        @case (1) {
          <app-form-field i18n-label="@@herregWizard.urenLabel" label="Gewerkte uren (afgelopen 5 jaar)" fieldId="uren" required [error]="errUren()">
            <app-text-input inputId="uren" [ngModel]="draft().uren" (ngModelChange)="dispatch({ tag: 'SetField', key: 'uren', value: $event })"
              name="uren" [invalid]="!!errUren()" i18n-placeholder="@@herregWizard.urenPlaceholder" placeholder="bijv. 4160" />
          </app-form-field>
          <app-form-field i18n-label="@@herregWizard.jarenLabel" label="Aantal jaren werkzaam" fieldId="jaren" required [error]="errJaren()">
            <app-text-input inputId="jaren" [ngModel]="draft().jaren" (ngModelChange)="dispatch({ tag: 'SetField', key: 'jaren', value: $event })"
              name="jaren" [invalid]="!!errJaren()" i18n-placeholder="@@herregWizard.jarenPlaceholder" placeholder="bijv. 5" />
          </app-form-field>
        }
        @case (2) {
          <app-form-field i18n-label="@@herregWizard.puntenLabel" label="Behaalde nascholingspunten" fieldId="punten" required [error]="errPunten()">
            <app-text-input inputId="punten" [ngModel]="draft().punten" (ngModelChange)="dispatch({ tag: 'SetField', key: 'punten', value: $event })"
              name="punten" [invalid]="!!errPunten()" i18n-placeholder="@@herregWizard.puntenPlaceholder" placeholder="bijv. 200" />
          </app-form-field>
        }
        @case (3) {
          <app-document-upload
            [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 (errDocumenten()) {
            <app-alert type="warning">{{ errDocumenten() }}</app-alert>
          }
        }
      }

      <div wizardSuccess>
        <app-alert type="ok" i18n="@@herregWizard.success">Uw aanvraag tot herregistratie is ontvangen.</app-alert>
      </div>
    </app-wizard-shell>
  `,
})
export class HerregistratieWizardComponent {
  private profile = inject(BigProfileStore);
  private uploadAdapter = inject(UploadAdapter);
  private store = createStore<WizardState, WizardMsg>(initial, reduce);

  /** Preview/download link for a completed upload; 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 / the showcase can mount any state directly. */
  seed = input<WizardState>(initial);

  readonly state = this.store.model; // public so the showcase can highlight the live state
  protected dispatch = this.store.dispatch;

  // Backend draft-sync (new persistence for this wizard): create a Concept on first
  // progress, debounced-sync the snapshot, resume by `?aanvraag=<id>`.
  private draftSync = createDraftSync({
    type: 'herregistratie',
    snapshot: () => {
      const s = this.state();
      if (s.tag !== 'Editing' || !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.step - 1, stepCount: this.stepLabels.length, documentIds };
    },
    onResume: (draft) => this.dispatch({ tag: 'Seed', state: draft as WizardState }),
    enabled: () => this.seed() === initial,
  });

  // Stepper labels + per-step heading titles (presentational only).
  readonly stepLabels = [$localize`:@@herregWizard.step.werkervaring:Werkervaring`, $localize`:@@herregWizard.step.nascholing:Nascholing`, $localize`:@@herregWizard.step.documenten:Documenten`];
  private stepTitles = [$localize`:@@herregWizard.title.werkervaring:Werkervaring (afgelopen 5 jaar)`, $localize`:@@herregWizard.title.nascholing:Nascholing`, $localize`:@@herregWizard.title.documenten:Documenten aanleveren`];

  private editing = computed(() => whenTag(this.state(), 'Editing'));
  protected step = computed(() => this.editing()?.step ?? 1);
  protected draft = computed<Draft>(() => this.editing()?.draft ?? { uren: '', jaren: '', punten: '' });
  protected upload = computed<UploadState>(() => this.editing()?.upload ?? initialUpload);
  protected errUren = computed(() => this.editing()?.errors.uren ?? '');
  protected errJaren = computed(() => this.editing()?.errors.jaren ?? '');
  protected errPunten = computed(() => this.editing()?.errors.punten ?? '');
  protected errDocumenten = computed(() => this.editing()?.errors.documenten ?? '');
  protected failedError = computed(() => whenTag(this.state(), 'Failed')?.error ?? '');
  protected uploadCtl = createUploadController({
    wizardId: 'herregistratie',
    getUpload: () => this.upload(),
    dispatch: (msg) => this.dispatch({ tag: 'Upload', msg }),
  });

  // --- Presentational wiring for the shared wizard shell ---------------------
  protected stepTitle = computed(() => this.stepTitles[this.step() - 1]);
  protected primaryLabel = computed(() => (this.step() < 3 ? $localize`:@@wizard.volgende:Volgende` : $localize`:@@herregWizard.indienen:Herregistratie aanvragen`));
  protected errorMessage = computed(() => $localize`:@@wizard.indienenMislukt:Indienen mislukt:` + ` ${this.failedError()}`);
  protected shellStatus = computed<WizardStatus>(() => {
    switch (this.state().tag) {
      case 'Editing': return 'editing';
      case 'Submitting': return 'submitting';
      case 'Submitted': return 'submitted';
      case 'Failed': return 'failed';
    }
  });
  /** Current step's field errors, flattened for the shell's error summary. */
  protected errorList = computed<WizardError[]>(() => {
    const e = this.editing()?.errors ?? {};
    return (Object.keys(e) as (keyof typeof e)[])
      .filter((k) => e[k])
      .map((k) => ({ id: k, message: e[k]! }));
  });

  constructor() {
    // An explicit seed (stories/tests) wins; otherwise resume 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()));
  }

  onPrimary() {
    const s = this.state();
    if (s.tag !== 'Editing') return;
    this.dispatch(s.step < 3 ? { tag: 'Next' } : { tag: 'Submit' });
    this.runIfSubmitting();
  }

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

  /** Reset the wizard to a fresh, empty start. */
  restart() {
    this.draftSync.reset();
    this.dispatch({ tag: 'Seed', state: initial });
  }

  /** The effect: when we entered Submitting, submit through the aanvraag lifecycle,
      flip the optimistic cross-page flag, then dispatch the result (commit/rollback). */
  private async runIfSubmitting() {
    const s = this.state();
    if (s.tag !== 'Submitting') return;
    this.profile.beginHerregistratie();
    const r = await this.draftSync.submit({ uren: s.data.uren, documents: s.data.documents });
    if (r.ok) {
      this.dispatch({ tag: 'SubmitConfirmed' });
      this.profile.confirmHerregistratie();
    } else {
      this.dispatch({ tag: 'SubmitFailed', error: r.error });
      this.profile.rollbackHerregistratie();
    }
  }
}
