import { Component, computed, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import type { Resource } from '@angular/core';
import { PageShellComponent } from '@shared/layout/page-shell/page-shell.component';
import { HeadingComponent } from '@shared/ui/heading/heading.component';
import { TextInputComponent } from '@shared/ui/text-input/text-input.component';
import { ASYNC } from '@shared/ui/async/async.component';
import { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component';
import { RegistrationSummaryComponent } from '@registratie/ui/registration-summary/registration-summary.component';
import { HerregistratieWizardComponent } from '@herregistratie/ui/herregistratie-wizard/herregistratie-wizard.component';
import { IntakeWizardComponent } from '@herregistratie/ui/intake-wizard/intake-wizard.component';
import { Registration } from '@registratie/domain/registration';
import { parsePostcode } from '@registratie/domain/value-objects/postcode';

/** Minimal fake Resource so <app-async> can be driven through every state without HTTP. */
function fakeResource<T>(status: string, value?: T, error?: Error): Resource<T> {
  return {
    value: () => value as T,
    status: () => status,
    error: () => error,
    hasValue: () => value !== undefined,
    reload: () => {},
  } as unknown as Resource<T>;
}

/** Teaching showcase: each section pairs the impossible-state-permitting"before"
    with the"after" where the type system rules it out. Composition-only. */
@Component({
  selector: 'app-concepts-page',
  imports: [
    FormsModule,
    PageShellComponent,
    HeadingComponent,
    TextInputComponent,
    ...ASYNC,
    SkeletonComponent,
    RegistrationSummaryComponent,
    HerregistratieWizardComponent,
    IntakeWizardComponent,
  ],
  styles: [
    `
      .section {
        margin: 0 0 3rem;
      }
      .lead {
        color: var(--rhc-color-grijs-700);
        max-width: 46rem;
        margin: 0.25rem 0 1.25rem;
      }
      .cols {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
        gap: 1.5rem;
        align-items: start;
      }
      .card {
        border: 1px solid var(--rhc-color-grijs-200, #e5e5e5);
        border-radius: 10px;
        padding: 1.25rem;
        background: #fff;
      }
      .card--bad {
        border-color: var(--rhc-color-rood-300, #f0b4b4);
      }
      .card--good {
        border-color: var(--rhc-color-groen-300, #b4e0b4);
      }
      .tag {
        display: inline-flex;
        align-items: center;
        gap: 0.4rem;
        font-weight: 700;
        font-size: 0.72rem;
        text-transform: uppercase;
        letter-spacing: 0.05em;
        margin: 0 0 0.75rem;
      }
      .tag::before {
        content: '';
        width: 0.6rem;
        height: 0.6rem;
        border-radius: 50%;
      }
      .tag.bad {
        color: var(--rhc-color-rood-600, #a30000);
      }
      .tag.bad::before {
        background: var(--rhc-color-rood-500, #d52b1e);
      }
      .tag.good {
        color: var(--rhc-color-groen-700, #277337);
      }
      .tag.good::before {
        background: var(--rhc-color-groen-500, #39870c);
      }
      .tag.plain {
        color: var(--rhc-color-grijs-700);
      }
      .tag.plain::before {
        display: none;
      }
      pre {
        background: #1e2430;
        color: #e6e9ef;
        padding: 1rem;
        border-radius: 8px;
        overflow: auto;
        font-size: 0.82rem;
        line-height: 1.55;
        margin: 0;
      }
      pre .k {
        color: #c792ea;
      }
      pre .s {
        color: #c3e88d;
      }
      pre .c {
        color: #7e8aa0;
        font-style: italic;
      }
      .note {
        font-size: 0.9rem;
        color: var(--rhc-color-grijs-700);
        margin: 0.75rem 0 0;
      }
      /* live state diagram */
      .machine {
        display: flex;
        flex-wrap: wrap;
        gap: 0.5rem;
        margin: 0 0 1rem;
      }
      .node {
        padding: 0.4rem 0.8rem;
        border-radius: 999px;
        border: 1px solid var(--rhc-color-grijs-300, #ccc);
        font-size: 0.82rem;
        color: var(--rhc-color-grijs-700);
        transition: all 0.15s;
      }
      .node.on {
        background: var(--rhc-color-hemelblauw-100, #e5f1fb);
        border-color: var(--rhc-color-hemelblauw-500, #007bc7);
        color: var(--rhc-color-hemelblauw-700, #00567d);
        font-weight: 700;
      }
      .steplist {
        display: flex;
        flex-wrap: wrap;
        gap: 0.4rem;
        align-items: center;
        margin: 0 0 1rem;
      }
      .pill {
        padding: 0.3rem 0.7rem;
        border-radius: 8px;
        background: var(--rhc-color-grijs-100, #f3f3f3);
        font-size: 0.8rem;
      }
      .pill.extra {
        background: var(--rhc-color-geel-100, #fff6d6);
        border: 1px dashed var(--rhc-color-geel-600, #c79a00);
      }
      .arrow {
        color: var(--rhc-color-grijs-400, #999);
      }
    `,
  ],
  template: `
    <app-page-shell heading="Onmogelijke toestanden onmogelijk maken" backLink="/dashboard">
      <p class="lead">
        Vijf functionele patronen die atomic design makkelijker maakt om te tonen — telkens "fout"
        (de oude vorm liet het toe) naast"goed" (het type maakt het onmogelijk).
      </p>

      <!-- 1. Discriminated unions -->
      <section class="section">
        <app-heading [level]="2">1 · Discriminated unions</app-heading>
        <p class="lead">Laat elke variant precies de gegevens dragen die kloppen — niets meer.</p>
        <div class="cols">
          <div class="card card--bad">
            <p class="tag bad">Fout — vlakke interface</p>
            <pre [innerHTML]="unionBad"></pre>
            <p class="note">
              Een doorgehaalde registratie houdt tóch een herregistratiedatum: onmogelijke toestand.
            </p>
          </div>
          <div class="card card--good">
            <p class="tag good">Goed — sum type</p>
            <app-registration-summary [reg]="doorgehaald" />
            <p class="note">
              De variant <code>Doorgehaald</code> kent geen herregistratiedatum, dus de rij bestaat
              simpelweg niet.
            </p>
          </div>
        </div>
      </section>

      <!-- 2. RemoteData fold -->
      <section class="section">
        <app-heading [level]="2">2 · RemoteData fold</app-heading>
        <p class="lead">
          Eén waarde met vier elkaar uitsluitende toestanden in plaats van drie losse booleans.
        </p>
        <div class="cols">
          <div class="card card--good">
            <p class="tag good">Vier toestanden, één molecuul</p>
            <p class="tag plain">Loading</p>
            <app-async [resource]="loadingRes"
              ><ng-template appAsyncLoaded let-v>{{ v }}</ng-template
              ><ng-template appAsyncLoading
                ><app-skeleton [count]="2" height="1.2rem" [delay]="0" /></ng-template
            ></app-async>
            <p class="tag plain">Empty</p>
            <app-async [resource]="emptyRes" [isEmpty]="isEmpty"
              ><ng-template appAsyncLoaded let-v>{{ v }}</ng-template></app-async
            >
            <p class="tag plain">Failure</p>
            <app-async [resource]="errorRes"
              ><ng-template appAsyncLoaded let-v>{{ v }}</ng-template></app-async
            >
            <p class="tag plain">Success</p>
            <app-async [resource]="successRes" [isEmpty]="isEmpty"
              ><ng-template appAsyncLoaded
                ><ul>
                  @for (i of successRes.value(); track i) {
                    <li>{{ i }}</li>
                  }
                </ul></ng-template
              ></app-async
            >
          </div>
          <div class="card card--good">
            <p class="tag good">De exhaustieve fold</p>
            <pre [innerHTML]="foldCode"></pre>
            <p class="note">
              Een nieuwe variant toevoegen breekt de compile via <code>assertNever</code> tot je hem
              afhandelt.
            </p>
          </div>
        </div>
      </section>

      <!-- 3. Parse, don't validate -->
      <section class="section">
        <app-heading [level]="2">3 · Parse, don't validate</app-heading>
        <p class="lead">Na het parsen onthoudt het <em>type</em> dat de waarde geldig is.</p>
        <div class="cols">
          <div class="card">
            <p class="tag good">Smart constructor → Result</p>
            <app-text-input
              inputId="pc"
              [ngModel]="raw()"
              (ngModelChange)="raw.set($event)"
              name="pc"
              placeholder="Typ een postcode, bijv. 1234 AB"
            />
          </div>
          @let r = parsed();
          <div class="card" [class.card--good]="r.ok" [class.card--bad]="!r.ok">
            @if (r.ok) {
              <p class="tag good">ok</p>
              <pre>Postcode ="{{ r.value }}"</pre>
              <p class="note">
                Een gevalideerde <code>Postcode</code> is een ander type dan een ruwe string.
              </p>
            } @else {
              <p class="tag bad">err</p>
              <pre>{{ r.error }}</pre>
            }
          </div>
        </div>
      </section>

      <!-- 4. State machine / wizard (live state diagram) -->
      <section class="section">
        <app-heading [level]="2">4 · Form als state machine</app-heading>
        <p class="lead">
          Eén tagged union stuurt de UI. Speel met de wizard — de gemarkeerde toestand is de
          huidige.
        </p>
        <div class="cols">
          <div class="card card--bad">
            <p class="tag bad">Fout — losse booleans</p>
            <pre [innerHTML]="machineBad"></pre>
            <p class="note">
              Niets verhindert"submitting" mét validatiefouten of een successcherm met errors.
            </p>
          </div>
          <div class="card card--good">
            <p class="tag good">Goed — één tagged union</p>
            <div class="machine">
              @for (n of ['Editing', 'Submitting', 'Submitted', 'Failed']; track n) {
                <span class="node" [class.on]="w.state().tag === n">{{ n }}</span>
              }
            </div>
            <app-herregistratie-wizard #w />
          </div>
        </div>
      </section>

      <!-- 5. Fixed steps, questions revealed inline -->
      <section class="section">
        <app-heading [level]="2"
          >5 · Vragenlijst met vaste stappen —"vragen tonen, niet stappen toevoegen"</app-heading
        >
        <p class="lead">
          Het aantal stappen ligt vast (<code>STEPS</code>); vervolgvragen verschijnen
          <em>binnen</em> een stap op basis van eerdere antwoorden. Antwoord"ja" op buitenland of
          vul weinig uren in, en er komt een extra vraag bij in dezelfde stap — de voortgang"van N"
          blijft gelijk.
        </p>
        <div class="cols">
          <div class="card card--good">
            <p class="tag good">Vaste stappen</p>
            <div class="steplist">
              @for (s of iw.steps; track s; let last = $last) {
                <span class="pill">{{ s }}</span>
                @if (!last) {
                  <span class="arrow">→</span>
                }
              }
            </div>
            <p class="note">
              De stappen zijn altijd dezelfde; alleen de vragen <em>binnen</em> een stap verschijnen
              of verdwijnen.
            </p>
          </div>
          <div class="card card--good">
            <p class="tag good">De wizard</p>
            <app-intake-wizard #iw />
          </div>
        </div>
      </section>
    </app-page-shell>
  `,
})
export class ConceptsPage {
  isEmpty = (v: string[]) => !v || v.length === 0;

  doorgehaald: Registration = {
    bigNummer: '19012345601',
    naam: 'Dr. A. (Anna) de Vries',
    beroep: 'Arts',
    registratiedatum: '2012-09-01',
    geboortedatum: '1985-03-14',
    status: { tag: 'Doorgehaald', doorgehaaldOp: '2024-05-01', reden: 'Op eigen verzoek' },
  };

  loadingRes = fakeResource<string[]>('loading');
  emptyRes = fakeResource<string[]>('resolved', []);
  errorRes = fakeResource<string[]>('error', undefined, new Error('Demo'));
  successRes = fakeResource<string[]>('resolved', ['Huisartsgeneeskunde', 'Spoedeisende hulp']);

  raw = signal('');
  parsed = computed(() => parsePostcode(this.raw()));

  unionBad = `<span class="k">interface</span> Registration {
  status: <span class="s">'Geregistreerd'</span> | <span class="s">'Doorgehaald'</span>;
  herregistratieDatum: string; <span class="c">// altijd aanwezig 😬</span>
}`;

  foldCode = `<span class="k">foldRemote</span>(rd, {
  loading:  () => spinner,
  empty:    () => <span class="s">'geen data'</span>,
  failure:  (e) => alert(e),
  success:  (v) => render(v),
}); <span class="c">// mist er één → compile-fout</span>`;

  machineBad = `submitting = <span class="k">signal</span>(false);
submitted  = <span class="k">signal</span>(false);
errors     = <span class="k">signal</span>&lt;...&gt;({});
<span class="c">// submitting === true && errors.size > 0 ? 🤷</span>`;
}
