import {
  Component,
  DestroyRef,
  ElementRef,
  computed,
  effect,
  inject,
  input,
  output,
  signal,
  viewChild,
} from '@angular/core';
import { NgTemplateOutlet } from '@angular/common';
import { ButtonComponent } from '@shared/ui/button/button.component';
import { PlaceholderChipComponent } from '@shared/ui/placeholder-chip/placeholder-chip.component';
import { PlaceholderOption } from '@shared/ui/rich-text-editor/rich-text-editor.component';
import { formatDatumNl } from '@shared/kernel/datum';
import { Paragraph } from '@shared/kernel/rich-text';
import { Brief, LetterBlock, LibraryPassage } from '@brief/domain/brief';
import { OrgTemplate } from '@brief/domain/org-template';
import { Diagnostic } from '@brief/domain/placeholders';
import { BriefMsg } from '@brief/domain/brief.machine';
import { LetterSectionComponent } from '@brief/ui/letter-section/letter-section.component';

/** A run of consecutive lines to render together: a list (bullet/number) or a single plain line. */
type PreviewSegment = {
  readonly list: 'bullet' | 'number' | null;
  readonly items: readonly Paragraph[];
};

function groupParagraphs(paras: readonly Paragraph[]): PreviewSegment[] {
  const out: { list: 'bullet' | 'number' | null; items: Paragraph[] }[] = [];
  for (const para of paras) {
    const kind = para.list ?? null;
    const last = out[out.length - 1];
    if (kind && last && last.list === kind) last.items.push(para);
    else out.push({ list: kind, items: [para] });
  }
  return out;
}

// Illustrative values for the "Voorbeeld" toggle — what send resolves server-side.
const SAMPLE_VALUES: Record<string, string> = {
  naam_zorgverlener: 'J. Jansen',
  big_nummer: '12345678901',
};

/** A4 height in CSS px (1in = 96px = 25.4mm) — for the approximate page-break marks. */
const A4_HEIGHT_PX = (297 * 96) / 25.4;

/** Organism: the letter as one surface — the org template's letterhead, signature and
    footer around the case-type template's sections. `editableRegions` picks who edits
    what: `'content'` hosts the editable letter-sections in place (drafter), `'none'`
    renders everything read-only (approver/locked, absorbs the old letter-preview),
    `'template'` reserves the org-identity regions for the admin editor (WP-26).
    Letter typography/geometry come from the shared `public/letter.css` contract —
    the same file the backend preview renderer inlines (WP-25). */
@Component({
  selector: 'app-letter-canvas',
  imports: [NgTemplateOutlet, ButtonComponent, PlaceholderChipComponent, LetterSectionComponent],
  styles: [
    `
      :host {
        display: block;
      }
      .toolbar {
        display: flex;
        justify-content: flex-end;
        margin-block-end: var(--rhc-space-max-sm);
      }
      /* Portal-side chrome around the letter surface (not part of the contract file). */
      .surface {
        background: var(--rhc-color-grijs-100, #f0f0f0);
        border: var(--rhc-border-width-sm) solid var(--rhc-color-border-default);
        border-radius: var(--rhc-border-radius-md);
        padding: var(--rhc-space-max-lg);
        overflow-x: auto;
      }
      .surface .letter {
        box-shadow: 0 1px 4px rgb(0 0 0 / 0.15);
      }
      /* Org-identity regions: visibly not the drafter's to edit. */
      .from-template {
        background: var(--rhc-color-grijs-100, #f5f5f5);
        outline: 2mm solid var(--rhc-color-grijs-100, #f5f5f5);
      }
      .from-template-caption {
        font-size: 7.5pt;
        /* grijs-700, not foreground-subtle: subtle misses AA contrast on the tint. */
        color: var(--rhc-color-grijs-700, #334155);
        margin: 0 0 2mm;
      }
    `,
  ],
  template: `
    <ng-template #line let-nodes>
      @for (node of nodes; track $index) {
        @switch (node.type) {
          @case ('text') {
            <span>{{ node.text }}</span>
          }
          @case ('lineBreak') {
            <br />
          }
          @case ('placeholder') {
            @if (showSample() && autoFor(node.key)) {
              <span>{{ sampleFor(node.key) }}</span>
            } @else {
              <app-placeholder-chip
                [label]="labelFor(node.key)"
                [autoResolvable]="autoFor(node.key)"
                [state]="stateFor(node.key)"
              />
            }
          }
        }
      }
    </ng-template>

    @if (editableRegions() === 'none') {
      <div class="toolbar">
        <app-button
          variant="subtle"
          (click)="showSample.set(!showSample())"
          [attr.aria-pressed]="showSample()"
        >
          {{ showSample() ? hideSampleLabel() : showSampleLabel() }}
        </app-button>
      </div>
    }

    <div class="surface">
      <div class="letter" #page [style]="marginStyle()" [style.zoom]="zoom()">
        <!-- div, not <header>/<footer>: the CIBG huisstijl styles those bare elements
             (robijn footer background) — the letter surface must stay letter.css-only. -->
        <div class="letter__letterhead" [class.from-template]="tintTemplate()">
          @if (tintTemplate()) {
            <p class="from-template-caption">{{ fromTemplateCaption() }}</p>
          }
          <p class="org-wordmark">{{ orgTemplate().orgName }}</p>
          <address class="return-address">{{ orgTemplate().returnAddress }}</address>
          <address class="address-window">{{ recipientText() }}</address>
          <dl class="reference">
            <div>
              <dt>{{ referenceLabel() }}</dt>
              <dd>{{ brief().briefId }}</dd>
            </div>
            <div>
              <dt>{{ dateLabel() }}</dt>
              <dd>{{ letterDate }}</dd>
            </div>
          </dl>
        </div>

        <div class="letter__body">
          @if (editableRegions() === 'content') {
            @for (section of brief().sections; track section.sectionKey) {
              <section>
                <app-letter-section
                  [section]="section"
                  [availablePassages]="availablePassages()"
                  [placeholders]="placeholders()"
                  [editable]="!section.locked"
                  (edit)="edit.emit($event)"
                />
              </section>
            }
          } @else {
            @for (section of brief().sections; track section.sectionKey) {
              <section>
                <h3>{{ section.title }}</h3>
                @for (block of section.blocks; track block.blockId) {
                  @for (seg of segmentsOf(block); track $index) {
                    @if (seg.list === 'bullet') {
                      <ul>
                        @for (para of seg.items; track $index) {
                          <li>
                            <ng-container
                              [ngTemplateOutlet]="line"
                              [ngTemplateOutletContext]="{ $implicit: para.nodes }"
                            />
                          </li>
                        }
                      </ul>
                    } @else if (seg.list === 'number') {
                      <ol>
                        @for (para of seg.items; track $index) {
                          <li>
                            <ng-container
                              [ngTemplateOutlet]="line"
                              [ngTemplateOutletContext]="{ $implicit: para.nodes }"
                            />
                          </li>
                        }
                      </ol>
                    } @else {
                      <p>
                        <ng-container
                          [ngTemplateOutlet]="line"
                          [ngTemplateOutletContext]="{ $implicit: seg.items[0].nodes }"
                        />
                      </p>
                    }
                  }
                }
              </section>
            }
          }
        </div>

        <div class="letter__signature" [class.from-template]="tintTemplate()">
          <p>{{ orgTemplate().signatureClosing }}</p>
          <p class="signature-name">{{ orgTemplate().signatureName }}</p>
          <p>{{ orgTemplate().signatureRole }}</p>
        </div>

        <div class="letter__footer" [class.from-template]="tintTemplate()">
          <div class="footer-contact">{{ orgTemplate().footerContact }}</div>
          <div class="footer-legal">{{ orgTemplate().footerLegal }}</div>
        </div>

        @for (top of pageBreaks(); track $index) {
          <div class="letter__page-break" [style.top.px]="top" aria-hidden="true">
            <span>{{ pageBreakCaption() }}</span>
          </div>
        }
      </div>
    </div>
  `,
})
export class LetterCanvasComponent {
  brief = input.required<Brief>();
  orgTemplate = input.required<OrgTemplate>();
  /** Who edits what on the surface: drafter ('content'), read-only ('none'),
      admin editor ('template', consumer arrives in WP-26). */
  editableRegions = input<'content' | 'template' | 'none'>('none');
  availablePassages = input<readonly LibraryPassage[]>([]);
  placeholders = input<readonly PlaceholderOption[]>([]);
  diagnostics = input<readonly Diagnostic[]>([]);
  zoom = input(1);
  edit = output<BriefMsg>();

  showSampleLabel = input($localize`:@@brief.preview.showSample:Voorbeeld met testwaarden`);
  hideSampleLabel = input($localize`:@@brief.preview.hideSample:Testwaarden verbergen`);
  fromTemplateCaption = input(
    $localize`:@@brief.canvas.fromTemplate:Komt uit de huisstijl van de organisatie — niet bewerkbaar in de brief.`,
  );
  pageBreakCaption = input(
    $localize`:@@brief.canvas.pageBreak:±pagina-einde — afdrukvoorbeeld is leidend`,
  );
  recipientText = input(
    $localize`:@@brief.canvas.recipient:Adres van de geadresseerde\n(wordt ingevuld bij verzending)`,
  );
  referenceLabel = input($localize`:@@brief.canvas.reference:Ons kenmerk`);
  dateLabel = input($localize`:@@brief.canvas.date:Datum`);

  protected showSample = signal(false);
  protected letterDate = formatDatumNl(new Date());

  /** The letterhead/signature/footer are tinted "not yours" only while composing —
      in 'none' the whole surface is read-only, in 'template' they ARE the editable focus. */
  protected tintTemplate = computed(() => this.editableRegions() === 'content');

  protected marginStyle = computed(() => {
    const m = this.orgTemplate().margins;
    return {
      '--letter-margin-top': `${m.topMm}mm`,
      '--letter-margin-right': `${m.rightMm}mm`,
      '--letter-margin-bottom': `${m.bottomMm}mm`,
      '--letter-margin-left': `${m.leftMm}mm`,
    };
  });

  // --- read-only rendering helpers (migrated from the superseded letter-preview) ---

  private defs = computed(() => new Map(this.brief().placeholders.map((p) => [p.key, p])));
  private worst = computed(() => {
    const m = new Map<string, 'error' | 'warning'>();
    for (const d of this.diagnostics()) {
      if (!d.placeholderKey) continue;
      if (d.severity === 'error') m.set(d.placeholderKey, 'error');
      else if (!m.has(d.placeholderKey)) m.set(d.placeholderKey, 'warning');
    }
    return m;
  });

  protected segmentsOf = (block: LetterBlock) => groupParagraphs(block.content.paragraphs);
  protected labelFor = (key: string) => this.defs().get(key)?.label ?? key;
  protected autoFor = (key: string) => this.defs().get(key)?.autoResolvable ?? false;
  protected stateFor = (key: string): 'ok' | 'warning' | 'error' => this.worst().get(key) ?? 'ok';
  protected sampleFor = (key: string) =>
    SAMPLE_VALUES[key] ?? (key === 'datum' ? this.letterDate : this.labelFor(key));

  // --- approximate page-break marks (PRD §2b: honest "±", print preview is leading) ---

  private page = viewChild<ElementRef<HTMLElement>>('page');
  protected pageBreaks = signal<readonly number[]>([]);

  constructor() {
    // ponytail: whole-surface height / A4-interval — ignores that a break never truly
    // falls mid-line; the caption says "±" and WP-25's server preview is authoritative.
    const observer = new ResizeObserver(([entry]) => {
      // ~1cm tolerance so a letter ending on a page boundary gets no edge-hugging mark.
      const pages = Math.ceil((entry.target.scrollHeight - 40) / A4_HEIGHT_PX);
      this.pageBreaks.set(
        Array.from({ length: Math.max(0, pages - 1) }, (_, i) => (i + 1) * A4_HEIGHT_PX),
      );
    });
    effect((onCleanup) => {
      const el = this.page()?.nativeElement;
      if (!el) return;
      observer.observe(el);
      onCleanup(() => observer.unobserve(el));
    });
    inject(DestroyRef).onDestroy(() => observer.disconnect());
  }
}
