import Handlebars from 'handlebars';

/** One numbered clause of a dynamic article, with optional nested bullets. */
export interface RenderedClause {
  text: string;
  /** Computed outline number, e.g. "3" or "2.1.4". */
  number: string;
  /** Nesting level: 1 = clause, 2 = sub-clause (x.y), 3 = x.y.z, … */
  depth: number;
  bullets: string[];
}

/** A dynamic article ready for the Handlebars template. */
export interface RenderedArticle {
  number: number;
  /** Stable article id from the template (e.g. "pricing") — lets the layout
   * inject the live rate schedule table under the pricing article. */
  id: string;
  title: string;
  /** Set (instead of clauses) when the body is a single plain paragraph. */
  paragraph?: string;
  clauses: RenderedClause[];
}

/**
 * Leading outline token on a clause line: "1.", "2)", "1.1", "1.1.1." …
 * The token's segment count sets the clause depth; its digits are ignored —
 * numbering is recomputed sequentially so stale numbers self-heal.
 * A single-segment token requires its "."/")" ("10 tons…" is prose, "10. x"
 * is clause ten); multi-segment tokens ("1.1") may omit it. A token may also
 * end the line — that is an empty clause still being typed in the editor.
 */
const CLAUSE_NUMBER_RE = /^(?:(\d+(?:\.\d+)+)[.)]?|(\d+)[.)])(?:\s+|$)/;

/** Deepest supported sub-clause level (1.1.1.1.1.1). */
const MAX_CLAUSE_DEPTH = 6;

/**
 * Parse a template article body into clauses. Format: one clause per line.
 * A leading outline number ("2. ", "2.1 ", "2.1.3 ") nests the line as a
 * sub-clause at that depth — the typed digits are stripped and renumbered
 * sequentially, so editing order never leaves stale numbers in the document.
 * Lines prefixed with "- " become bullets nested under the preceding clause.
 * A body that reduces to a single un-numbered clause without bullets renders
 * as a plain paragraph rather than a numbered list of one.
 */
export function parseArticleBody(body: string): Pick<RenderedArticle, 'paragraph' | 'clauses'> {
  const lines = (body ?? '')
    .split('\n')
    .map((line) => line.trim())
    .filter((line) => line.length > 0);

  const clauses: RenderedClause[] = [];
  // counters[i] = current number at depth i+1; truncated when a shallower
  // clause arrives so deeper numbering restarts at 1.
  const counters: number[] = [];
  let sawNumberToken = false;

  for (const line of lines) {
    if (line.startsWith('- ')) {
      const bullet = line.slice(2).trim();
      if (clauses.length === 0) {
        counters.splice(0, counters.length, 1);
        clauses.push({ text: bullet, number: '1', depth: 1, bullets: [] });
      } else {
        clauses[clauses.length - 1].bullets.push(bullet);
      }
      continue;
    }

    const match = CLAUSE_NUMBER_RE.exec(line);
    const token = match ? (match[1] ?? match[2]) : null;
    let depth = token ? Math.min(token.split('.').length, MAX_CLAUSE_DEPTH) : 1;
    // A sub-clause can only sit directly under an existing parent — "1.1.1"
    // typed as the first line clamps to whatever level is actually open.
    depth = Math.min(depth, counters.length + 1);
    if (match) sawNumberToken = true;

    counters.splice(depth);
    while (counters.length < depth) counters.push(0);
    counters[depth - 1] += 1;

    clauses.push({
      text: match ? line.slice(match[0].length).trim() : line,
      number: counters.slice(0, depth).join('.'),
      depth,
      bullets: [],
    });
  }

  if (clauses.length === 1 && clauses[0].bullets.length === 0 && !sawNumberToken) {
    return { paragraph: clauses[0].text, clauses: [] };
  }
  return { clauses };
}

/**
 * Interpolate Handlebars placeholders ({{client.companyName}}, {{contractDate}},
 * …) inside admin-authored template text against the contract view model.
 * Malformed placeholders must never break document generation — fall back to
 * the raw text.
 */
export function interpolateTemplateText(text: string, context: unknown): string {
  if (!text || !text.includes('{{')) return text ?? '';
  try {
    return Handlebars.compile(text)(context);
  } catch {
    return text;
  }
}
