from __future__ import annotations

import html
import json
from pathlib import Path
from typing import Any


def write_web_app(data: dict[str, Any], output_dir: Path) -> dict[str, str]:
    app_dir = output_dir / "HTML_App"
    app_dir.mkdir(parents=True, exist_ok=True)
    app_path = app_dir / "Epione_Template_App_v0.7.html"
    app_path.write_text(render_web_app(data), encoding="utf-8")
    return {"html_app": str(app_path)}


def render_web_app(data: dict[str, Any]) -> str:
    app_data = {
        "version": "v0.7",
        "clinic_context": data.get("clinic_context", {}),
        "global_defaults": data.get("global_defaults", {}),
        "forms": data.get("forms", []),
        "conditions": data.get("conditions", []),
        "product_library": data.get("product_library", {}),
    }
    data_json = json.dumps(app_data, ensure_ascii=False).replace("</", "<\\/")
    title = html.escape("Epione EMR Template System v0.7")
    return f"""<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{title}</title>
  <style>
    :root {{
      --bg: #f6f7f9;
      --panel: #ffffff;
      --ink: #172026;
      --muted: #66727c;
      --line: #d9dee3;
      --line-strong: #bbc4cc;
      --accent: #0f766e;
      --accent-2: #174ea6;
      --danger: #b42318;
      --warn: #b54708;
      --ok: #027a48;
      --field: #fbfcfd;
      --shadow: 0 10px 24px rgba(23, 32, 38, .08);
      --radius: 8px;
    }}
    * {{ box-sizing: border-box; }}
    body {{ margin: 0; background: var(--bg); color: var(--ink); font: 14px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }}
    button, input, select, textarea {{ font: inherit; }}
    button {{ cursor: pointer; }}
    .app {{ min-height: 100vh; display: grid; grid-template-rows: auto 1fr; }}
    .topbar {{ height: 56px; display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 0 18px; background: var(--panel); border-bottom: 1px solid var(--line); }}
    .brand {{ display: flex; align-items: baseline; gap: 10px; min-width: 240px; }}
    .brand h1 {{ margin: 0; font-size: 16px; font-weight: 720; letter-spacing: 0; }}
    .brand span {{ color: var(--muted); font-size: 12px; }}
    .top-actions {{ display: flex; align-items: center; gap: 8px; }}
    .status {{ color: var(--muted); font-size: 12px; }}
    .shell {{ min-height: 0; display: grid; grid-template-columns: 330px minmax(520px, 1fr) 390px; }}
    .sidebar, .preview {{ min-height: 0; overflow: auto; background: var(--panel); }}
    .sidebar {{ border-right: 1px solid var(--line); }}
    .preview {{ border-left: 1px solid var(--line); }}
    .main {{ min-height: 0; overflow: auto; padding: 18px; }}
    .section {{ padding: 14px; border-bottom: 1px solid var(--line); }}
    .section-title {{ margin: 0 0 10px; color: var(--muted); font-size: 11px; font-weight: 760; text-transform: uppercase; letter-spacing: .04em; }}
    .search {{ width: 100%; height: 36px; padding: 0 10px; border: 1px solid var(--line); border-radius: 7px; background: var(--field); }}
    .segmented {{ display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; padding: 4px; background: #edf1f4; border-radius: 8px; }}
    .segmented button {{ border: 0; border-radius: 6px; padding: 7px 8px; color: var(--muted); background: transparent; font-size: 12px; }}
    .segmented button.active {{ color: var(--ink); background: var(--panel); box-shadow: 0 1px 4px rgba(23,32,38,.10); }}
    .condition-list, .form-list {{ display: grid; gap: 6px; }}
    .guide {{ display: grid; gap: 10px; }}
    .guide-step {{ border: 1px solid var(--line); border-radius: 7px; background: #fbfcfd; overflow: hidden; }}
    .guide-question {{ width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 8px; border: 0; border-bottom: 1px solid var(--line); background: #f7f9fa; padding: 8px 10px; text-align: left; color: var(--ink); font-weight: 700; }}
    .guide-question span {{ color: var(--muted); font-weight: 600; font-size: 12px; }}
    .choice-grid {{ display: grid; gap: 5px; padding: 8px; }}
    .choice-button {{ text-align: left; border: 1px solid var(--line); border-radius: 6px; background: #fff; padding: 7px 8px; color: var(--ink); font-size: 12px; }}
    .choice-button:hover {{ border-color: #9cc7c1; background: #f2fbf9; }}
    .choice-button.active {{ border-color: var(--accent); background: #e7f4f2; color: #0b4f49; font-weight: 700; }}
    .choice-button .count {{ color: var(--muted); float: right; font-weight: 600; }}
    .row-button {{ text-align: left; border: 1px solid transparent; border-radius: 7px; background: transparent; padding: 9px; color: var(--ink); }}
    .row-button:hover {{ background: #f1f5f6; }}
    .row-button.active {{ background: #e7f4f2; border-color: #a9d8d2; }}
    .row-title {{ font-weight: 650; font-size: 13px; }}
    .row-meta {{ color: var(--muted); font-size: 12px; margin-top: 2px; }}
    .toolbar {{ display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }}
    .title-block h2 {{ margin: 0; font-size: 20px; line-height: 1.2; }}
    .title-block p {{ margin: 5px 0 0; color: var(--muted); }}
    .btn {{ height: 34px; border: 1px solid var(--line-strong); border-radius: 7px; padding: 0 11px; background: var(--panel); color: var(--ink); }}
    .btn.primary {{ border-color: var(--accent); background: var(--accent); color: white; }}
    .btn.danger {{ border-color: #f1b4ad; color: var(--danger); }}
    .btn:disabled {{ opacity: .55; cursor: not-allowed; }}
    .metrics {{ display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; margin-bottom: 14px; }}
    .metric {{ background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 10px; }}
    .metric strong {{ display: block; font-size: 18px; }}
    .metric span {{ color: var(--muted); font-size: 12px; }}
    .panel {{ background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden; }}
    .panel-head {{ display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--line); }}
    .panel-head h3 {{ margin: 0; font-size: 14px; }}
    .fields {{ display: grid; gap: 14px; padding: 14px; }}
    .field-group {{ display: grid; gap: 10px; }}
    .group-title {{ color: var(--muted); font-size: 12px; font-weight: 750; text-transform: uppercase; letter-spacing: .04em; }}
    .field {{ display: grid; grid-template-columns: 220px minmax(260px, 1fr); gap: 12px; align-items: start; padding: 10px; border: 1px solid var(--line); border-radius: 7px; background: #fff; }}
    .field label {{ font-weight: 650; }}
    .field .help {{ margin-top: 4px; color: var(--muted); font-size: 12px; }}
    .field input, .field textarea, .field select {{ width: 100%; border: 1px solid var(--line-strong); border-radius: 7px; background: var(--field); padding: 8px 9px; min-height: 36px; }}
    .field textarea {{ min-height: 82px; resize: vertical; }}
    .field-controls {{ display: grid; gap: 7px; }}
    .quick-choice-list {{ display: flex; flex-wrap: wrap; gap: 6px; max-height: 108px; overflow: auto; padding: 7px; border: 1px solid #c9e4e0; border-radius: 7px; background: #f2fbf9; }}
    .quick-choice {{ border: 1px solid #a9d8d2; border-radius: 999px; background: #fff; color: #0b4f49; padding: 5px 8px; font-size: 11px; line-height: 1.25; text-align: left; max-width: 100%; }}
    .quick-choice:hover, .quick-choice:focus {{ border-color: var(--accent); background: #e7f4f2; outline: none; }}
    .quick-choice.compact {{ max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }}
    .assist-panel {{ margin-bottom: 14px; }}
    .context-grid {{ display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; padding: 12px 14px; }}
    .context-item {{ border: 1px solid var(--line); border-radius: 7px; padding: 9px; background: #fbfcfd; }}
    .context-item strong {{ display: block; font-size: 12px; margin-bottom: 4px; }}
    .context-item span {{ color: var(--muted); font-size: 12px; }}
    .context-actions {{ display: flex; flex-wrap: wrap; gap: 8px; padding: 0 14px 14px; }}
    .favorite-panel {{ margin-bottom: 14px; }}
    .favorite-grid {{ display: grid; grid-template-columns: minmax(0, 1fr) auto auto auto; gap: 8px; padding: 12px 14px; align-items: center; }}
    .favorite-grid input, .favorite-grid select {{ min-height: 34px; border: 1px solid var(--line-strong); border-radius: 7px; background: var(--field); padding: 7px 8px; }}
    .field-meta {{ display: flex; flex-wrap: wrap; gap: 6px; margin-top: 7px; }}
    .tag {{ display: inline-flex; align-items: center; min-height: 21px; border: 1px solid var(--line); border-radius: 999px; padding: 2px 7px; color: var(--muted); background: #f7f9fa; font-size: 11px; }}
    .tag.required {{ border-color: #f3b7b2; color: var(--danger); background: #fff3f1; }}
    .tag.default {{ border-color: #abd5f5; color: var(--accent-2); background: #edf7ff; }}
    .tag.manual {{ border-color: #f4c790; color: var(--warn); background: #fff7ed; }}
    .preview-inner {{ padding: 14px; }}
    .note {{ white-space: pre-wrap; min-height: 340px; padding: 14px; border: 1px solid var(--line); border-radius: var(--radius); background: #fbfcfd; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; line-height: 1.55; }}
    .missing {{ display: grid; gap: 6px; margin: 0 0 12px; }}
    .missing-item {{ border-left: 3px solid var(--danger); background: #fff3f1; padding: 7px 8px; border-radius: 5px; color: #7a271a; font-size: 12px; }}
    .product-box {{ margin-top: 14px; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }}
    .product-row {{ display: grid; grid-template-columns: 1fr auto; gap: 8px; padding: 9px; border-top: 1px solid var(--line); }}
    .product-row:first-child {{ border-top: 0; }}
    .product-row span {{ color: var(--muted); font-size: 12px; }}
    .review-box {{ margin-top: 14px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); overflow: hidden; }}
    .review-body {{ display: grid; gap: 9px; padding: 11px; }}
    .review-item {{ display: grid; grid-template-columns: 18px 1fr; gap: 8px; align-items: start; color: var(--ink); font-size: 12px; }}
    .review-item input {{ margin-top: 2px; }}
    .review-grid {{ display: grid; grid-template-columns: 1fr 118px; gap: 8px; }}
    .review-grid input, .review-box textarea {{ width: 100%; border: 1px solid var(--line-strong); border-radius: 7px; background: var(--field); padding: 7px 8px; font-size: 12px; }}
    .review-box textarea {{ min-height: 62px; resize: vertical; }}
    .review-actions {{ display: flex; flex-wrap: wrap; gap: 8px; }}
    .review-status {{ font-size: 12px; font-weight: 700; }}
    .review-status.ready {{ color: var(--ok); }}
    .review-status.blocked {{ color: var(--danger); }}
    .pill-no {{ color: var(--danger); font-weight: 700; }}
    .empty {{ padding: 18px; color: var(--muted); text-align: center; }}
    @media (max-width: 1180px) {{ .shell {{ grid-template-columns: 300px 1fr; }} .preview {{ grid-column: 1 / -1; border-left: 0; border-top: 1px solid var(--line); }} }}
    @media (max-width: 760px) {{ .topbar {{ height: auto; padding: 12px; align-items: flex-start; flex-direction: column; }} .shell {{ grid-template-columns: 1fr; }} .sidebar, .preview {{ max-height: none; border: 0; border-bottom: 1px solid var(--line); }} .field {{ grid-template-columns: 1fr; }} .metrics {{ grid-template-columns: repeat(2, 1fr); }} .toolbar {{ align-items: flex-start; flex-direction: column; }} }}
  </style>
</head>
<body>
  <div class="app">
    <header class="topbar">
      <div class="brand"><h1>Epione EMR Templates</h1><span>v0.7 clinical documentation workspace</span></div>
      <div class="top-actions">
        <span class="status" id="saveStatus">No patient data leaves this browser.</span>
        <button class="btn" id="exportTxt">Export TXT</button>
        <button class="btn primary" id="copyNote">Copy Note</button>
      </div>
    </header>
    <div class="shell">
      <aside class="sidebar">
        <div class="section">
          <p class="section-title">Find Template</p>
          <input class="search" id="search" placeholder="Search conditions, procedures, fields">
        </div>
        <div class="section">
          <p class="section-title">Template Type</p>
          <div class="segmented" id="typeTabs">
            <button data-type="all" class="active">All</button>
            <button data-type="History">History</button>
            <button data-type="Physical Exam">Exam</button>
            <button data-type="Procedure">Procedure</button>
          </div>
        </div>
        <div class="section">
          <p class="section-title">Guided Procedure Path</p>
          <div class="guide" id="guidedPath"></div>
        </div>
        <div class="section form-picker">
          <p class="section-title">Forms</p>
          <div class="form-list" id="formList"></div>
        </div>
        <div class="section">
          <p class="section-title">Conditions</p>
          <div class="condition-list" id="conditions"></div>
        </div>
      </aside>
      <main class="main">
        <div class="toolbar">
          <div class="title-block">
            <h2 id="formTitle">Select a form</h2>
            <p id="formMeta">Choose a condition or search for a procedure.</p>
          </div>
          <div class="top-actions">
            <button class="btn danger" id="clearInputs">Clear Inputs</button>
            <button class="btn" id="fillDemo">Fill Demo</button>
          </div>
        </div>
        <div class="metrics">
          <div class="metric"><strong id="metricForms">0</strong><span>Forms</span></div>
          <div class="metric"><strong id="metricRequired">0</strong><span>Required Fields</span></div>
          <div class="metric"><strong id="metricMissing">0</strong><span>Missing Manual Fields</span></div>
          <div class="metric"><strong id="metricDefaults">0</strong><span>Resolved Defaults</span></div>
        </div>
        <section class="panel">
          <div class="panel-head"><h3>History / Physical Exam Assist</h3><span class="status" id="contextStatus">Fill history and exam first to reuse their details.</span></div>
          <div class="context-grid" id="contextSummary"></div>
          <div class="context-actions" id="contextActions"></div>
        </section>
        <section class="panel assist-panel">
          <div class="panel-head"><h3>Physician Favorites</h3><span class="status" id="favoriteStatus">Save usual choices for this procedure.</span></div>
          <div class="favorite-grid">
            <select id="favoriteSelect"><option value="">No saved favorite</option></select>
            <button class="btn" id="applyFavorite">Apply</button>
            <button class="btn primary" id="saveFavorite">Save Current</button>
            <button class="btn danger" id="deleteFavorite">Delete</button>
            <input id="favoriteName" placeholder="Favorite name, e.g. My usual right knee PRP" style="grid-column: 1 / -1;">
          </div>
        </section>
        <section class="panel assist-panel">
          <div class="panel-head"><h3>Structured Encounter Inputs</h3><span class="status" id="defaultRule">Encounter input → per-form → global → blank</span></div>
          <div class="fields" id="fields"></div>
        </section>
      </main>
      <aside class="preview">
        <div class="preview-inner">
          <p class="section-title">Narrative EMR Output</p>
          <div class="missing" id="missing"></div>
          <div class="note" id="note"></div>
          <div class="product-box" id="products"></div>
          <div class="review-box">
            <div class="panel-head"><h3>Physician Review</h3><span class="review-status blocked" id="reviewStatus">Not ready</span></div>
            <div class="review-body">
              <label class="review-item"><input type="checkbox" data-review="required"> <span>All required manual fields match the encounter.</span></label>
              <label class="review-item"><input type="checkbox" data-review="safety"> <span>Consent, time-out, allergies, infection screen, anticoagulants, side/site/level reviewed.</span></label>
              <label class="review-item"><input type="checkbox" data-review="products"> <span>Products/devices, concentration, dose/volume, lot/expiry, and verification status reviewed.</span></label>
              <label class="review-item"><input type="checkbox" data-review="narrative"> <span>Narrative output edited as needed and ready for EMR paste/signature.</span></label>
              <div class="review-grid">
                <input id="reviewerName" placeholder="Reviewer">
                <input id="reviewDate" type="date">
              </div>
              <textarea id="reviewNotes" placeholder="Review notes / exceptions"></textarea>
              <div class="review-actions">
                <button class="btn" id="copyReviewPacket">Copy Review Packet</button>
                <button class="btn" id="exportReviewPacket">Export Review TXT</button>
              </div>
            </div>
          </div>
        </div>
      </aside>
    </div>
  </div>
  <script id="epione-data" type="application/json">{data_json}</script>
  <script>
    const DATA = JSON.parse(document.getElementById('epione-data').textContent);
    const forms = DATA.forms;
    const conditions = DATA.conditions;
    const globals = DATA.global_defaults || {{}};
    const productRows = (DATA.product_library && DATA.product_library.product_rows) || [];
    const state = {{ conditionId: conditions[0]?.condition_id || null, formId: null, type: 'all', query: '', valuesByForm: {{}}, guidance: '', interventionType: '', procedureGroup: '' }};
    const FAVORITE_STORAGE_KEY = 'epione_procedure_favorites_v1';

    const $ = (id) => document.getElementById(id);
    const formById = Object.fromEntries(forms.map(f => [f.form_id, f]));
    const conditionById = Object.fromEntries(conditions.map(c => [c.condition_id, c]));

    function defaultKey(key, form) {{
      const context = [form.form_id, form.title, form.family, form.category, form.form_type].join(' ').toLowerCase();
      if (key === 'guidance_phrase') return context.includes('fluoro') || context.includes('fluoroscopy') ? 'fluoroscopy_guidance_phrase' : 'ultrasound_guidance_phrase';
      if (key === 'monitoring') {{
        if (context.includes('rfa') || context.includes('radiofrequency')) return 'default_monitoring_rfa';
        if (context.includes('infusion') || context.includes('ketamine')) return 'default_monitoring_infusion';
        return 'default_monitoring_injection';
      }}
      return key;
    }}

    function formDefaults(form) {{ return form.defaults || form.procedure_defaults || {{}}; }}
    function hasValue(v) {{ return v !== undefined && v !== null && String(v).trim() !== ''; }}
    function currentValues(form = formById[state.formId] || {{}}) {{
      const id = form.form_id || state.formId || 'unselected';
      return state.valuesByForm[id] ||= {{}};
    }}
    function conditionForms() {{
      const condition = conditionById[state.conditionId] || conditions[0];
      if (!condition) return {{}};
      return {{
        condition,
        history: formById[condition.history_form_id],
        exam: formById[condition.exam_form_id],
      }};
    }}

    function procedureFormsForCurrentCondition() {{
      const condition = conditionById[state.conditionId] || conditions[0];
      const ids = condition ? (condition.procedure_ids || []) : [];
      return ids.map(id => formById[id]).filter(Boolean);
    }}

    function resolveField(form, field) {{
      const key = field.key;
      const entered = currentValues(form)[key];
      if (hasValue(entered)) return {{ value: String(entered), source: 'Encounter' }};
      if (field.default_layer === 'No Default') return {{ value: '', source: 'Manual only' }};
      const per = formDefaults(form)[key];
      if (field.default_layer === 'Per-Form Default' && hasValue(per)) return {{ value: String(per), source: 'Per-form' }};
      const globalValue = globals[defaultKey(key, form)];
      if (field.default_layer === 'Global Default' && hasValue(globalValue)) return {{ value: String(globalValue), source: 'Global' }};
      if (hasValue(per)) return {{ value: String(per), source: 'Per-form' }};
      if (hasValue(globalValue)) return {{ value: String(globalValue), source: 'Global' }};
      return {{ value: '', source: 'Blank' }};
    }}

    function selectedForms() {{
      const condition = conditionById[state.conditionId] || conditions[0];
      const ids = condition ? [condition.history_form_id, condition.exam_form_id, ...(condition.procedure_ids || [])] : forms.map(f => f.form_id);
      let list = ids.map(id => formById[id]).filter(Boolean);
      if (state.type !== 'all') list = list.filter(f => f.form_type === state.type);
      if (state.type === 'Procedure' || list.every(f => f.form_type === 'Procedure')) {{
        list = applyGuideFilters(list);
      }}
      if (state.query) {{
        const q = state.query.toLowerCase();
        list = forms.filter(f => [f.title, f.form_id, f.family, f.category, f.condition_tags].join(' ').toLowerCase().includes(q));
      }}
      return list;
    }}

    function applyGuideFilters(list) {{
      return list.filter(form => {{
        const nav = form.navigator || {{}};
        const guidance = Array.isArray(nav.guidance_options) ? nav.guidance_options : [];
        if (state.guidance && !guidance.includes(state.guidance)) return false;
        if (state.interventionType && nav.intervention_type !== state.interventionType) return false;
        if (state.procedureGroup && nav.procedure_group !== state.procedureGroup) return false;
        return true;
      }});
    }}

    function selectForm(formId) {{
      state.formId = formId;
      resetReview();
      render();
    }}

    function allFavorites() {{
      try {{ return JSON.parse(localStorage.getItem(FAVORITE_STORAGE_KEY) || '{{}}'); }}
      catch (_) {{ return {{}}; }}
    }}
    function saveAllFavorites(favorites) {{
      localStorage.setItem(FAVORITE_STORAGE_KEY, JSON.stringify(favorites));
    }}
    function favoritesForForm(formId = state.formId) {{
      return allFavorites()[formId] || [];
    }}
    function renderFavorites(form) {{
      const favorites = favoritesForForm(form.form_id);
      $('favoriteSelect').innerHTML = '<option value="">No saved favorite</option>' + favorites.map((fav, index) => `<option value="${{index}}">${{escapeHtml(fav.name || 'Favorite ' + (index + 1))}}</option>`).join('');
      $('favoriteStatus').textContent = favorites.length ? `${{favorites.length}} saved for this procedure.` : 'Save usual choices for this procedure.';
    }}
    function saveFavorite(form) {{
      const name = $('favoriteName').value.trim() || `${{form.title}} favorite`;
      const values = {{ ...currentValues(form) }};
      const nav = form.navigator || {{}};
      const favorite = {{
        name,
        savedAt: new Date().toISOString(),
        formId: form.form_id,
        conditionId: state.conditionId,
        navigator: {{
          guidance: state.guidance || (nav.guidance_options || [])[0] || '',
          interventionType: state.interventionType || nav.intervention_type || '',
          procedureGroup: state.procedureGroup || nav.procedure_group || '',
        }},
        values,
      }};
      const all = allFavorites();
      const list = all[form.form_id] || [];
      list.push(favorite);
      all[form.form_id] = list;
      saveAllFavorites(all);
      $('favoriteName').value = '';
      renderFavorites(form);
      $('favoriteSelect').value = String(list.length - 1);
      $('favoriteStatus').textContent = 'Saved favorite for this procedure.';
    }}
    function applyFavorite(form) {{
      const index = Number($('favoriteSelect').value);
      const favorite = favoritesForForm(form.form_id)[index];
      if (!favorite) return;
      state.valuesByForm[form.form_id] = {{ ...(favorite.values || {{}}) }};
      if (favorite.navigator) {{
        state.guidance = favorite.navigator.guidance || state.guidance;
        state.interventionType = favorite.navigator.interventionType || state.interventionType;
        state.procedureGroup = favorite.navigator.procedureGroup || state.procedureGroup;
      }}
      $('favoriteStatus').textContent = 'Applied favorite.';
      render();
    }}
    function deleteFavorite(form) {{
      const index = Number($('favoriteSelect').value);
      if (Number.isNaN(index)) return;
      const all = allFavorites();
      const list = all[form.form_id] || [];
      if (!list[index]) return;
      list.splice(index, 1);
      all[form.form_id] = list;
      saveAllFavorites(all);
      renderFavorites(form);
      $('favoriteStatus').textContent = 'Deleted favorite.';
    }}

    function renderConditions() {{
      $('conditions').innerHTML = conditions.map(c => `
        <button class="row-button ${{c.condition_id === state.conditionId ? 'active' : ''}}" data-condition="${{c.condition_id}}">
          <div class="row-title">${{escapeHtml(c.condition)}}</div>
          <div class="row-meta">${{(c.procedure_ids || []).length}} procedures</div>
        </button>
      `).join('');
      $('conditions').querySelectorAll('button').forEach(btn => btn.addEventListener('click', () => {{
        state.conditionId = btn.dataset.condition;
        state.query = '';
        state.guidance = '';
        state.interventionType = '';
        state.procedureGroup = '';
        $('search').value = '';
        state.formId = null;
        state.type = 'Procedure';
        $('typeTabs').querySelectorAll('button').forEach(b => b.classList.toggle('active', b.dataset.type === 'Procedure'));
        render();
      }}));
    }}

    function renderGuidedPath() {{
      const base = procedureFormsForCurrentCondition();
      const afterGuidance = state.guidance ? base.filter(f => ((f.navigator || {{}}).guidance_options || []).includes(state.guidance)) : base;
      const afterIntervention = state.interventionType ? afterGuidance.filter(f => (f.navigator || {{}}).intervention_type === state.interventionType) : afterGuidance;
      const steps = [
        {{ key: 'guidance', label: 'Guidance technique', value: state.guidance, options: countedOptions(base, f => (f.navigator || {{}}).guidance_options || []) }},
        {{ key: 'interventionType', label: 'Intervention type', value: state.interventionType, options: countedOptions(afterGuidance, f => [(f.navigator || {{}}).intervention_type || 'Procedure']) }},
        {{ key: 'procedureGroup', label: 'Procedure family', value: state.procedureGroup, options: countedOptions(afterIntervention, f => [(f.navigator || {{}}).procedure_group || f.family || 'Procedure']) }},
      ];
      $('guidedPath').innerHTML = steps.map(step => guideStepHtml(step)).join('');
      $('guidedPath').querySelectorAll('[data-guide-key]').forEach(btn => btn.addEventListener('click', () => {{
        const key = btn.dataset.guideKey;
        const value = btn.dataset.guideValue || '';
        state[key] = state[key] === value ? '' : value;
        if (key === 'guidance') {{ state.interventionType = ''; state.procedureGroup = ''; }}
        if (key === 'interventionType') state.procedureGroup = '';
        state.formId = null;
        state.type = 'Procedure';
        $('typeTabs').querySelectorAll('button').forEach(b => b.classList.toggle('active', b.dataset.type === 'Procedure'));
        render();
      }}));
      $('guidedPath').querySelectorAll('[data-clear-guide]').forEach(btn => btn.addEventListener('click', () => {{
        const key = btn.dataset.clearGuide;
        state[key] = '';
        if (key === 'guidance') {{ state.interventionType = ''; state.procedureGroup = ''; }}
        if (key === 'interventionType') state.procedureGroup = '';
        state.formId = null;
        render();
      }}));
    }}

    function guideStepHtml(step) {{
      const selected = step.value ? `<button class="choice-button active" data-guide-key="${{step.key}}" data-guide-value="${{escapeAttr(step.value)}}">${{escapeHtml(step.value)}}<span class="count">selected</span></button>` : '';
      const options = step.value ? selected : step.options.map(([value, count]) => `
        <button class="choice-button" data-guide-key="${{step.key}}" data-guide-value="${{escapeAttr(value)}}">${{escapeHtml(value)}}<span class="count">${{count}}</span></button>
      `).join('');
      return `
        <div class="guide-step">
          <button class="guide-question" data-clear-guide="${{step.key}}" type="button">${{escapeHtml(step.label)}}<span>${{step.value ? 'Click to change' : 'Choose one'}}</span></button>
          <div class="choice-grid">${{options || '<div class="empty">No options for this condition.</div>'}}</div>
        </div>`;
    }}

    function countedOptions(list, getter) {{
      const counts = {{}};
      list.forEach(item => getter(item).filter(Boolean).forEach(value => counts[value] = (counts[value] || 0) + 1));
      return Object.entries(counts).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
    }}

    function renderFormPicker(list) {{
      if (!state.formId && list.length) state.formId = list[0].form_id;
      $('formList').innerHTML = list.length ? list.map(f => `
        <button class="row-button ${{f.form_id === state.formId ? 'active' : ''}}" data-form="${{f.form_id}}">
          <div class="row-title">${{escapeHtml(f.title)}}</div>
          <div class="row-meta">${{escapeHtml(f.form_type || '')}} · ${{escapeHtml(f.family || f.category || '')}}</div>
        </button>`).join('') : '<div class="empty">No matching forms for this condition and type.</div>';
      $('formList').querySelectorAll('button').forEach(btn => btn.addEventListener('click', () => selectForm(btn.dataset.form)));
    }}

    function renderFields(form) {{
      const groups = groupBy(form.fields || [], f => f.section || 'Other');
      $('fields').innerHTML = Object.entries(groups).map(([section, fields]) => `
        <div class="field-group">
          <div class="group-title">${{escapeHtml(section)}}</div>
          ${{fields.map(field => fieldHtml(form, field)).join('')}}
        </div>
      `).join('');
      $('fields').querySelectorAll('[data-key]').forEach(control => control.addEventListener('input', () => {{
        currentValues(form)[control.dataset.key] = control.value;
        renderPreview(form);
      }}));
      $('fields').querySelectorAll('[data-preset-for]').forEach(control => control.addEventListener('click', () => {{
        if (!control.dataset.presetText) return;
        insertPreset(form, control.dataset.presetFor, control.dataset.presetText);
      }}));
    }}

    function fieldHtml(form, field) {{
      const resolved = resolveField(form, field);
      const values = currentValues(form);
      const fieldOptions = String(field.options || '').split(';').map(s => s.trim()).filter(Boolean);
      const quickOptions = Array.isArray(field.quick_options) ? field.quick_options : String(field.quick_options || '').split(';').map(s => s.trim()).filter(Boolean);
      const presetOptions = [...fieldOptions, ...quickOptions].filter((v, i, arr) => v && arr.indexOf(v) === i);
      const preset = presetOptions.length && field.input_type !== 'select'
        ? `<div class="quick-choice-list" aria-label="Quick text choices for ${{escapeAttr(field.label || field.key)}}">${{presetOptions.map(o => `<button type="button" class="quick-choice compact" title="${{escapeAttr(o)}}" data-preset-for="${{field.key}}" data-preset-text="${{escapeAttr(o)}}">${{escapeHtml(o)}}</button>`).join('')}}</div>`
        : '';
      const control = field.input_type === 'select'
        ? `<select data-key="${{field.key}}"><option value="">${{resolved.value ? 'Use default: ' + escapeHtml(resolved.value) : 'Select'}}</option>${{fieldOptions.map(o => `<option ${{values[field.key] === o ? 'selected' : ''}}>${{escapeHtml(o)}}</option>`).join('')}}</select>`
        : field.input_type === 'textarea'
          ? `${{preset}}<textarea data-key="${{field.key}}" placeholder="${{escapeAttr(resolved.value)}}">${{escapeHtml(values[field.key] || '')}}</textarea>`
          : `${{preset}}<input data-key="${{field.key}}" value="${{escapeAttr(values[field.key] || '')}}" placeholder="${{escapeAttr(resolved.value)}}">`;
      const contextTag = Array.isArray(field.context_sources) && field.context_sources.length ? '<span class="tag default">Can pull Hx/Exam</span>' : '';
      return `
        <div class="field">
          <div>
            <label>${{escapeHtml(field.label || field.key)}}</label>
            <div class="help">${{escapeHtml(field.help || '')}}</div>
            <div class="field-meta">
              <span class="tag ${{field.required ? 'required' : ''}}">${{field.required ? 'Required' : 'Optional'}}</span>
              <span class="tag ${{field.default_layer === 'No Default' ? 'manual' : 'default'}}">${{escapeHtml(field.default_layer || '')}}</span>
              <span class="tag">${{escapeHtml(resolved.source)}}</span>
              ${{contextTag}}
            </div>
          </div>
          <div class="field-controls">${{control}}</div>
        </div>`;
    }}

    function insertPreset(form, key, text) {{
      const values = currentValues(form);
      const existing = String(values[key] || '').trim();
      values[key] = existing ? existing + '\\n' + text : text;
      render();
    }}

    function renderPreview(form) {{
      const resolved = Object.fromEntries((form.fields || []).map(field => [field.key, resolveField(form, field)]));
      const missing = (form.fields || []).filter(field => field.required && !hasValue(resolved[field.key].value));
      const defaultCount = Object.values(resolved).filter(v => v.source === 'Per-form' || v.source === 'Global').length;
      const tokenPattern = new RegExp('\\\\{{\\\\{{([^}}]+)\\\\}}\\\\}}', 'g');
      let note = String(form.narrative_template || form.template || '').replace(tokenPattern, (_, raw) => {{
        const key = raw.trim();
        const value = resolved[key]?.value;
        return hasValue(value) ? value : '[' + key + ']';
      }});
      if (missing.length) note += '\\n\\nMISSING REQUIRED FIELDS: ' + missing.map(f => f.key).join(', ');
      $('note').textContent = note;
      $('missing').innerHTML = missing.length ? missing.map(f => `<div class="missing-item">${{escapeHtml(f.label || f.key)}}</div>`).join('') : '<div class="tag">No missing required fields</div>';
      $('metricRequired').textContent = (form.fields || []).filter(f => f.required).length;
      $('metricMissing').textContent = missing.length;
      $('metricDefaults').textContent = defaultCount;
      renderProducts(form);
      updateReviewStatus();
    }}

    function renderProducts(form) {{
      const keys = new Set((form.fields || []).map(f => f.key));
      const rows = productRows.filter(r => String(r[7] || '').split(';').some(k => keys.has(k.trim()))).slice(0, 8);
      $('products').innerHTML = '<div class="panel-head"><h3>Product Verification</h3><span class="pill-no">No approved defaults</span></div>' +
        (rows.length ? rows.map(r => `<div class="product-row"><div><strong>${{escapeHtml(r[0])}}</strong><br><span>${{escapeHtml(r[3])}}</span></div><span>${{escapeHtml(r[9])}}</span></div>`).join('') : '<div class="empty">No mapped product rows for this form.</div>');
    }}

    function render() {{
      renderConditions();
      renderGuidedPath();
      const list = selectedForms();
      renderFormPicker(list);
      $('metricForms').textContent = list.length;
      const form = formById[state.formId] || list[0];
      if (!form) {{
        $('formTitle').textContent = 'No form selected';
        $('fields').innerHTML = '<div class="empty">No matching forms.</div>';
        $('note').textContent = '';
        return;
      }}
      state.formId = form.form_id;
      $('formTitle').textContent = form.title;
      $('formMeta').textContent = [form.form_type, form.family || form.category, form.form_id].filter(Boolean).join(' · ');
      renderContextAssist(form);
      renderFavorites(form);
      renderFields(form);
      renderPreview(form);
    }}

    function renderContextAssist(form) {{
      const {{ history, exam }} = conditionForms();
      const historyValues = history ? currentValues(history) : {{}};
      const examValues = exam ? currentValues(exam) : {{}};
      const summaryItems = [
        ['Condition focus', firstValue([historyValues.condition_focus, examValues.condition_focus])],
        ['History impression', historyValues.history_impression_plan],
        ['Pain generator', examValues.pain_generator_impression],
        ['Target assessment', examValues.procedural_target_assessment],
      ];
      $('contextSummary').innerHTML = summaryItems.map(([label, value]) => `
        <div class="context-item"><strong>${{escapeHtml(label)}}</strong><span>${{escapeHtml(shortValue(value))}}</span></div>
      `).join('');

      const canPull = form.form_type === 'Procedure' && (form.fields || []).some(field => Array.isArray(field.context_sources) && field.context_sources.length);
      const hxButton = history ? `<button class="btn" data-open-form="${{history.form_id}}">Open History</button>` : '';
      const examButton = exam ? `<button class="btn" data-open-form="${{exam.form_id}}">Open Physical Exam</button>` : '';
      $('contextActions').innerHTML = [
        hxButton,
        examButton,
        canPull ? '<button class="btn primary" id="pullContext">Pull Hx/Exam Into Procedure</button>' : '',
      ].join('');
      $('contextStatus').textContent = canPull ? 'Procedure fields can reuse entered history and exam details.' : 'Enter history and exam details here; procedure forms can reuse them.';
      $('contextActions').querySelectorAll('[data-open-form]').forEach(btn => btn.addEventListener('click', () => selectForm(btn.dataset.openForm)));
      const pull = $('pullContext');
      if (pull) pull.addEventListener('click', () => {{
        const count = applyContextToProcedure(form);
        $('saveStatus').textContent = count ? `Pulled ${{count}} fields from history/exam.` : 'No entered history/exam details available to pull.';
        setTimeout(() => $('saveStatus').textContent = 'No patient data leaves this browser.', 2400);
        render();
      }});
    }}

    function applyContextToProcedure(form) {{
      const {{ history, exam }} = conditionForms();
      const sourceValues = {{ ...(history ? currentValues(history) : {{}}), ...(exam ? currentValues(exam) : {{}}) }};
      const values = currentValues(form);
      let count = 0;
      (form.fields || []).forEach(field => {{
        const sources = Array.isArray(field.context_sources) ? field.context_sources : [];
        if (!sources.length || hasValue(values[field.key])) return;
        const pulled = sources
          .map(key => [key, sourceValues[key]])
          .filter(([, value]) => hasValue(value))
          .map(([key, value]) => `${{labelForContextKey(key)}}: ${{value}}`);
        if (pulled.length) {{
          values[field.key] = pulled.join('\\n');
          count += 1;
        }}
      }});
      return count;
    }}

    function firstValue(values) {{ return values.find(hasValue) || ''; }}
    function shortValue(value) {{
      if (!hasValue(value)) return 'Not entered yet';
      const text = String(value).replace(/\\s+/g, ' ').trim();
      return text.length > 150 ? text.slice(0, 147) + '...' : text;
    }}
    function labelForContextKey(key) {{
      const labels = {{
        condition_focus: 'Condition',
        pain_generator_impression: 'Pain generator impression',
        history_impression_plan: 'History impression/plan',
        presenting_complaint: 'Presenting complaint',
        pain_location_distribution: 'Pain location/distribution',
        functional_limitations: 'Functional limitation/goals',
        prior_treatments: 'Prior treatments/response',
        imaging_labs_tests: 'Imaging/tests reviewed',
        procedural_target_assessment: 'Procedure target assessment',
        red_flags_neuro_screen: 'History safety screen',
        red_flag_exam_findings: 'Exam red flags',
        current_medications: 'Current medications',
        condition_specific_exam: 'Condition-specific exam',
        exam_plan: 'Exam plan',
        patient_preferences_affordability: 'Patient preference/affordability',
      }};
      return labels[key] || key;
    }}

    function currentReviewItems() {{
      return Array.from(document.querySelectorAll('[data-review]'));
    }}

    function resetReview() {{
      currentReviewItems().forEach(item => item.checked = false);
      $('reviewNotes').value = '';
      updateReviewStatus();
    }}

    function updateReviewStatus() {{
      const boxes = currentReviewItems();
      const allChecked = boxes.length > 0 && boxes.every(item => item.checked);
      const missingCount = Number($('metricMissing').textContent || '0');
      const ready = allChecked && missingCount === 0;
      $('reviewStatus').textContent = ready ? 'Ready for EMR' : 'Needs review';
      $('reviewStatus').classList.toggle('ready', ready);
      $('reviewStatus').classList.toggle('blocked', !ready);
      return ready;
    }}

    function reviewPacket() {{
      const form = formById[state.formId] || {{}};
      const checks = currentReviewItems().map(item => `${{item.checked ? '[x]' : '[ ]'}} ${{item.nextElementSibling?.textContent || item.dataset.review}}`).join('\\n');
      return [
        'Epione physician review packet',
        'Form: ' + (form.title || ''),
        'Form ID: ' + (form.form_id || ''),
        'Status: ' + $('reviewStatus').textContent,
        'Reviewer: ' + $('reviewerName').value,
        'Review date: ' + $('reviewDate').value,
        '',
        'Checklist:',
        checks,
        '',
        'Review notes:',
        $('reviewNotes').value || '[none]',
        '',
        'Narrative note:',
        $('note').textContent,
      ].join('\\n');
    }}

    function groupBy(items, getKey) {{
      return items.reduce((acc, item) => {{ const key = getKey(item); (acc[key] ||= []).push(item); return acc; }}, {{}});
    }}
    function escapeHtml(value) {{ return String(value ?? '').replace(/[&<>"']/g, c => ({{'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}}[c])); }}
    function escapeAttr(value) {{ return escapeHtml(value).replace(/\\n/g, ' '); }}

    $('search').addEventListener('input', e => {{ state.query = e.target.value.trim(); state.formId = null; render(); }});
    $('typeTabs').querySelectorAll('button').forEach(btn => btn.addEventListener('click', () => {{
      state.type = btn.dataset.type;
      $('typeTabs').querySelectorAll('button').forEach(b => b.classList.toggle('active', b === btn));
      state.formId = null;
      render();
    }}));
    $('copyNote').addEventListener('click', async () => {{
      await navigator.clipboard.writeText($('note').textContent);
      $('saveStatus').textContent = 'Copied note to clipboard.';
      setTimeout(() => $('saveStatus').textContent = 'No patient data leaves this browser.', 2200);
    }});
    $('exportTxt').addEventListener('click', () => {{
      const blob = new Blob([$('note').textContent], {{ type: 'text/plain' }});
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url; a.download = `${{state.formId || 'epione'}}_note.txt`; a.click();
      URL.revokeObjectURL(url);
    }});
    currentReviewItems().forEach(item => item.addEventListener('change', updateReviewStatus));
    $('reviewerName').addEventListener('input', updateReviewStatus);
    $('reviewDate').addEventListener('input', updateReviewStatus);
    $('reviewNotes').addEventListener('input', updateReviewStatus);
    $('copyReviewPacket').addEventListener('click', async () => {{
      await navigator.clipboard.writeText(reviewPacket());
      $('saveStatus').textContent = 'Copied review packet to clipboard.';
      setTimeout(() => $('saveStatus').textContent = 'No patient data leaves this browser.', 2200);
    }});
    $('exportReviewPacket').addEventListener('click', () => {{
      const blob = new Blob([reviewPacket()], {{ type: 'text/plain' }});
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url; a.download = `${{state.formId || 'epione'}}_review_packet.txt`; a.click();
      URL.revokeObjectURL(url);
    }});
    $('clearInputs').addEventListener('click', () => {{
      const form = formById[state.formId];
      if (form) state.valuesByForm[form.form_id] = {{}};
      render();
    }});
    $('saveFavorite').addEventListener('click', () => {{ const form = formById[state.formId]; if (form) saveFavorite(form); }});
    $('applyFavorite').addEventListener('click', () => {{ const form = formById[state.formId]; if (form) applyFavorite(form); }});
    $('deleteFavorite').addEventListener('click', () => {{ const form = formById[state.formId]; if (form) deleteFavorite(form); }});
    $('fillDemo').addEventListener('click', () => {{
      const form = formById[state.formId];
      const values = currentValues(form);
      (form.fields || []).forEach(field => {{ if (field.default_layer === 'No Default') values[field.key] = demoValue(field); }});
      render();
    }});
    function demoValue(field) {{
      const key = field.key;
      if (key.includes('side')) return 'Right';
      if (key.includes('diagnosis')) return 'chronic pain condition under physician review';
      if (key.includes('lot')) return 'Lot/expiry entered at procedure time';
      if (key.includes('complication')) return 'None reported at time of note generation';
      if (key.includes('response')) return 'Immediate response documented after reassessment';
      if (key.includes('image')) return 'Saved to clinic imaging archive';
      return 'Entered encounter-specific detail';
    }}
    render();
  </script>
</body>
</html>
"""
