import { Component, computed, inject, input } from '@angular/core';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { AddressFieldsComponent, AdresValue, AdresErrors } from '@registratie/ui/address-fields/address-fields.component';
import { createStore } from '@shared/application/store';
import { whenTag } from '@shared/kernel/fp';
import { State, Msg, initial, reduce } from '@registratie/domain/change-request.machine';
import { submitChangeRequest } from '@registratie/application/submit-change-request';
import { ApiClient } from '@shared/infrastructure/api-client';

/**
 * Organism: change-request (adreswijziging) form. Uses the SAME idiom as the
 * wizards — all state in one signal driven by the pure `reduce`
 * (change-request.machine.ts), submitted via a `submit-*` command returning
 * `Result`. Renders the shared `<app-address-fields>`; the server re-validates.
 */
@Component({
  selector: 'app-change-request-form',
  imports: [ButtonComponent, HeadingComponent, AlertComponent, AddressFieldsComponent],
  template: `
    @if (state().tag === 'Submitted') {
      <app-alert type="ok" i18n="@@changeRequest.success">
        Uw adreswijziging is ontvangen (referentie {{ referentie() }}). U ontvangt binnen 5 werkdagen bericht.
      </app-alert>
      <div class="app-section">
        <app-button variant="secondary" (click)="dispatch({ tag: 'Reset' })" i18n="@@changeRequest.nieuwe">Nieuwe wijziging doorgeven</app-button>
      </div>
    } @else {
      <app-heading [level]="2" i18n="@@changeRequest.heading">Adreswijziging doorgeven</app-heading>
      <form (ngSubmit)="onSubmit()" class="app-form app-form-panel app-section">
        <app-address-fields
          idPrefix="cr"
          [value]="adres()"
          [errors]="errors()"
          (fieldChange)="dispatch({ tag: 'SetField', key: $event.key, value: $event.value })" />

        @if (failedError()) {
          <app-alert type="error"><ng-container i18n="@@changeRequest.failed">Het indienen is niet gelukt:</ng-container> {{ failedError() }}</app-alert>
        }

        <app-button type="submit" variant="primary" [disabled]="state().tag === 'Submitting'">
          {{ state().tag === 'Submitting' ? submitBezigLabel : submitLabel }}
        </app-button>
      </form>
    }
  `,
})
export class ChangeRequestFormComponent {
  private apiClient = inject(ApiClient);
  private store = createStore<State, Msg>(initial, reduce);

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

  readonly state = this.store.model;
  protected dispatch = this.store.dispatch;

  protected readonly submitLabel = $localize`:@@changeRequest.submit:Wijziging indienen`;
  protected readonly submitBezigLabel = $localize`:@@changeRequest.submitBezig:Bezig met indienen…`;

  private editing = computed(() => whenTag(this.state(), 'Editing'));
  protected errors = computed<AdresErrors>(() => this.editing()?.errors ?? {});
  protected failedError = computed(() => whenTag(this.state(), 'Failed')?.error ?? '');
  protected referentie = computed(() => whenTag(this.state(), 'Submitted')?.referentie ?? '');

  /** The address shown in the fields — the live draft while editing, the parsed
      data while submitting/failed (so the user sees what they sent). */
  protected adres = computed<AdresValue>(() => {
    const s = this.state();
    if (s.tag === 'Editing') return s.draft;
    if (s.tag === 'Submitting' || s.tag === 'Failed') {
      return { straat: s.data.straat, postcode: s.data.postcode, woonplaats: s.data.woonplaats };
    }
    return { straat: '', postcode: '', woonplaats: '' }; // Submitted shows the success alert, not the fields
  });

  constructor() {
    queueMicrotask(() => this.dispatch({ tag: 'Seed', state: this.seed() }));
  }

  onSubmit() {
    this.dispatch({ tag: 'Submit' });
    this.runIfSubmitting();
  }

  /** Effect: when we entered Submitting, call the command, then dispatch the outcome. */
  private async runIfSubmitting() {
    const s = this.state();
    if (s.tag !== 'Submitting') return;
    const r = await submitChangeRequest(this.apiClient, s.data);
    if (r.ok) this.dispatch({ tag: 'SubmitConfirmed', referentie: r.value });
    else this.dispatch({ tag: 'SubmitFailed', error: r.error });
  }
}
