#!/usr/bin/env python3
"""Fill Matt's required communication template from the LL2 contract data."""
import html
import json
import re
from pathlib import Path

from bs4 import BeautifulSoup, Comment

ROOT = Path('/Users/agency/Documents/Agty/KomplexLogopédia')
PLAN = ROOT / 'plans/rita-journey-20261001'
OUT = ROOT / 'outputs/rita-journey-20261001'
TEMPLATE = Path('/Users/agency/Documents/Codex/2026-09-22/create-a-new-variant-of-ll/outputs/lll-communication-template.html')
if not TEMPLATE.is_file():
    TEMPLATE = PLAN / 'source/matt-communication-template.html'
MAP_URL = 'https://review.clientsflow.hu/komplex-journey-map-2026-10-01-v1/'
CONTRACT_URL = 'https://review.clientsflow.hu/komplex-journey-contract-2026-10-01-v1/'


def esc(value):
    return html.escape(str(value), quote=True)


def section(soup, heading):
    return next(h.find_parent('section') for h in soup.find_all('h2') if h.get_text(strip=True) == heading)


def fragment(markup):
    return BeautifulSoup(markup, 'html.parser')


def render():
    data = json.loads((PLAN / 'board.json').read_text())
    count = sum(len(d['criteria']) for d in data['deliverables'])
    execution = data.get('execution_state', 'not_launched')
    authorized = execution != 'not_launched'
    def passed(c):
        return c.get('evidence', {}).get('state') in ('passed', 'verified', 'complete')
    done = sum(passed(c) for d in data['deliverables'] for c in d['criteria'])
    complete = done == count
    values = {
        'TITLE': data['title'], 'RUN_STATE': 'LAUNCH AUTHORIZED' if execution == 'launch_authorized' else execution.upper().replace('_', ' ') if authorized else 'READY FOR REVIEW',
        'ONE_SENTENCE_PURPOSE': data['purpose'],
        'CURRENT_OWNER': data.get('current_owner', 'Matt review, production not launched'),
        'RUN_ID': data['run_id'], 'SESSION_ID': data['session_id'],
        'UPDATED_AT': data['updated_at'], 'END_GOAL': data['desired_state'],
        'END_STATE': 'Published frontend review package. All44 criteria have their required source, behavior and independent postdeployment visual evidence. Commercial proposals and the later real-service implementation remain separate.' if complete else 'Target: the fully linked, mobile-tested frontend canvas and complete evidence. Local production and interaction receipts are now attached. Counts and screenshots remain distinct from independent acceptance.',
        'DONE_COUNT': str(done), 'TOTAL_COUNT': str(count),
        'DELIVERABLE_COUNT': str(len(data['deliverables'])), 'OPEN_COUNT': '3' if authorized else str(len(data.get('review_questions', []))),
        'CONSTRAINT_1': 'Matt authorized a pinned GPT-6.1 Sol Ultra /goal executor. Build the frontend review package only.' if authorized else 'Stop here for Matt review. A later explicit Launch is required before production.',
        'CONSTRAINT_2': 'Keep daily Rita feedback and original exercise wording. Customer interfaces and QA are mobile only.',
        'CONSTRAINT_3': 'Bare review URLs, preserved comment identity, no automatic opening. Shared external API limit: USD15.',
        'PROOF_STANDARD': 'Acceptance needs actual assets, coherent reader-to-next-step copy, source comparisons, working demo states and fresh deployed mobile screenshots. Each criterion records its current evidence and remaining verification.',
    }
    raw = TEMPLATE.read_text()
    for key, value in values.items():
        raw = raw.replace('{{' + key + '}}', esc(value))
    soup = BeautifulSoup(raw, 'html.parser')
    soup.title.string = 'Komplex · journey rebuild goal contract'
    soup.body['data-run-state'] = 'COMPLETE' if complete else 'READY'
    soup.body['data-execution-state'] = execution
    hero = soup.select_one('header.hero')
    hero.select_one('.eyebrow').string = 'LL2 · Matt’s communication template'
    gate = 'Matt’s 2 October instruction authorizes execution after reconciling all four browser comments. The frontend-only boundary is fixed. These are production requirements; a green check needs actual evidence. The parent goes idle after dispatch.' if authorized else 'The visual-map and source-check workers only prepared this plan. No asset executor, creative generation or change to the September client review has started.'
    hero.append(fragment(f'<p class="proof" id="review-gate"><b>Authority:</b> {gate} <a href="{MAP_URL}">Two-diagram decision map</a>.</p>'))
    hero.append(fragment('<p class="proof" id="comment-service-status"><b>Feedback for this review:</b> The shared comment service now returns real provider JSON. The September master has 267 exactly verified saved critiques. Disposable browser tests verified comment and inline-edit persistence without writing test records to live documents. Earlier plan-limit failures remain historical evidence, not the current service state.</p>'))
    if authorized:
        hero.append(fragment('<p class="proof" id="published-journey"><b>Actual deliverable:</b> <a href="https://review.clientsflow.hu/komplex-journey-2026-10-02-v1/">Published customer journey canvas</a> · <a href="https://review.clientsflow.hu/komplex-journey-2026-10-02-v1/review/review-index.html">Complete deliverable and evidence index</a>.</p>'))

    goal = soup.select_one('[data-id="goal"]')
    goal.append(fragment('<p class="proof" id="requested-quantities"><b>Required package:</b> 20 square ads + four landing variants + 14 participant days + 14 Rita review screens. The images are 20 ads + seven landing visuals + 56 daily alternatives = <b>83 actual generated visual assets</b>.</p>'))

    flow = soup.select_one('.flow')
    flow.clear()
    nodes = [
        ('source', 'input', 'Source inputs', 'Rita, Figma and live identity', 'Use six original modules, the full persona workbook, commented Figma variants and current Komplex design.'),
        ('audience', 'input', 'Thinking before design', 'One meta-persona', 'Describe common situations and objections, then explain the next decision at each step.'),
        ('ads', 'deliverable', 'Attention', '20 new square ads', 'Luna criticizes each caption before GPT imagery. The matching copy explains the offer and click.'),
        ('landings', 'deliverable', 'Offer', 'Four complete landing variants', 'Two variants per offer, all supplied layouts, seven new visuals and clear CTA alternatives.'),
        ('payment', 'deliverable', 'Purchase and access', 'Stripe and email states', 'Show the demo handoff, confirmation, access, nurture and daily delivery without real transactions.'),
        ('daily', 'deliverable', 'Practice and feedback', '14 participant + 14 Rita screens', 'Exact exercise text, daily forms/video, 56 visual alternatives, previous-day review and daily feedback.'),
        ('canvas', 'deliverable', 'Assembled outcome', 'Canvas, proof and client email', 'Every asset is linked in journey order, with expert screens in the row below and comments on the outside.'),
        ('review', 'next', 'Next action now', 'Matt reviews this contract', 'Send changes to the scope and assumptions in the Codex chat. Production awaits a later explicit Launch.'),
    ]
    for ident, cls, label, name, why in nodes:
        flow.append(fragment(f'<article class="flow-node {cls}" id="flow-{ident}" data-id="flow-{ident}" data-state="READY"><div class="flow-label">{esc(label)}</div><h3 id="flow-{ident}-name">{esc(name)}</h3><p class="why" id="flow-{ident}-why"><b>Why:</b> {esc(why)}</p></article>'))
    if authorized:
        node = flow.select_one('#flow-review')
        node.select_one('.flow-label').string = 'Review the completed package' if complete else 'Current authority'
        node.select_one('h3').string = 'Published frontend package with complete evidence' if complete else 'GPT-6.1 Sol Ultra execution authorized'
        node.select_one('.why').string = 'Start with the published canvas, compare the two entry routes, test a participant and Rita daily loop, then inspect day14 and consultation. The complete index retains every alternative and proof.' if complete else 'Matt authorized autonomous frontend delivery. The executor maintains the proof and prepares one morning handoff.'
    flow_section = section(soup, 'Deliverable flow')
    flow_section.select_one('.empty').string = 'The intended customer journey and the authorized frontend-only scope.'

    messages = section(soup, 'Parent ↔ child messages')
    messages.find('h2').string = 'What changed in this contract'
    messages.find('small').string = 'Latest instruction governs'
    stack = messages.select_one('.stack')
    stack.clear()
    stack.append(fragment('<article class="surface message parent" id="latest-authority" data-owner="parent" data-state="READY"><div class="message-head"><strong>Matt’s correction · 1 October</strong><span>Review first</span></div><p>“Sorry I was wrong before. Definitely stop for a review.” The earlier autonomous instruction is superseded. The map and goal contract may be prepared and published. Production remains unlaunched.</p></article>'))
    if authorized:
        stack.select_one('#latest-authority').decompose()
        stack.append(fragment('<article class="surface message parent" id="latest-authority" data-owner="parent" data-state="READY"><div class="message-head"><strong>Matt’s launch · 2 October</strong><span>Frontend execution authorized</span></div><p>All four browser comments are reconciled: precise participant acceptance, frontend-only Rita and checkout prototypes, and the actual CC Klíma sales-call canvas. Launch one pinned GPT-6.1 Sol Ultra thread with /goal. Rational criterion replacements require a before/after log and equivalent or stronger proof. Parent goes idle.</p></article>'))
    stack.append(fragment('<article class="surface message parent" id="source-clarifications" data-owner="parent" data-state="READY"><div class="message-head"><strong>Source and scope clarifications</strong><span>Source snapshot</span></div><p>Figma recheck: 178 comments with unchanged IDs, messages and resolution states. The new kit has 28 labeled layouts, S1–S28. Rita’s source has six modules covering the full 14-day range. Daily reflection forms need saved readback, and payment success/access is a simulation rather than proof of real fulfillment.</p></article>'))

    readset = soup.select_one('[data-id="parent-read-set"]')
    readset.find('summary').find('span').string = 'Sources and plan decisions'
    body = readset.select_one('.accordion-body')
    body.clear()
    body.append(fragment(f'<p id="source-boundary">These are inputs and proposed boundaries. They are not production evidence.</p><div class="read-set"><a href="{MAP_URL}">Decision map</a><a href="https://review.clientsflow.hu/komplex-rita-funnel-2026-09-26-v1/">Existing September review</a><a href="https://review.clientsflow.hu/ccklima-canvas-2026-10-01-v1/">Canvas reference</a><a href="https://www.figma.com/design/g8LRZzfXbjRUgDA8dROzzb/">Rita’s Figma file</a><a href="https://www.komplexlogopedia.hu/">Current website identity</a></div>'))
    for item in data['inputs']:
        body.append(fragment(f'<p id="input-{item["id"]}"><b>{esc(item["label"])}:</b> {esc(item["detail"])}</p>'))
    for item in data['constraints']:
        body.append(fragment(f'<div class="proof" id="constraint-{item["id"]}"><b>{esc(item["text"])}</b><p>Source: {esc(item["source"])}</p></div>'))
    for item in data['tools']:
        body.append(fragment(f'<p id="tool-{item["id"]}">{esc(item["text"])}</p>'))
    soup.select_one('[data-id="ideas"]').decompose()

    work = soup.select_one('.work-panel .stack')
    work.clear()
    for d in data['deliverables']:
        checks = []
        for c in d['criteria']:
            state = c.get('evidence', {}).get('state', 'pending')
            evidence = c.get('evidence', {})
            proof = esc(evidence.get('summary', evidence.get('detail', 'Evidence pending. Do not mark passed until the actual required output and verification exist.')))
            for link in evidence.get('links', []):
                href = link.get('url', '') if isinstance(link, dict) else str(link)
                label = link.get('label', href) if isinstance(link, dict) else href
                proof += f'<p><a href="{esc(href)}">{esc(label)}</a></p>'
            checks.append(f'''<div class="detail" id="criterion-{c['id']}" data-id="criterion-{c['id']}" data-state="{esc(state)}">
<h4 id="requirement-{c['id']}">{'☑' if passed(c) else '☐'} {esc(c['requirement'])}</h4>
<p id="verification-{c['id']}"><b>Verification:</b> {esc(c['verification'])}</p>
<details><summary>Evidence and screenshots</summary><div class="proof"><b>{esc(state)}.</b> {proof}</div></details></div>''')
        d_done = sum(passed(c) for c in d['criteria'])
        work.append(fragment(f'''<article class="work-card" id="deliverable-{d['id']}" data-id="deliverable-{d['id']}" data-owner="{'GPT-6.1 Sol Ultra executor' if authorized else 'unassigned-until-launch'}" data-state="READY">
<div class="card-head"><div><h3 id="name-{d['id']}">{esc(d['name'])}</h3><p class="card-copy" id="bullet-{d['id']}">{esc(d['bullet'])}</p></div><div><span class="state">{'VERIFIED' if d_done == len(d['criteria']) else 'AUTHORIZED' if authorized else 'PLANNED'}</span><span class="counter" style="display:inline-flex;margin-left:5px">{d_done} / {len(d['criteria'])}</span></div></div>
<p class="card-copy" id="problem-{d['id']}"><b>Current problem:</b> {esc(d['current_problem'])}</p>
<p class="card-copy" id="change-{d['id']}"><b>Proposed change:</b> {esc(d['proposed_change'])}</p>
<p class="card-copy" id="visible-{d['id']}"><b>Visible at:</b> {esc(d['visible_at'])}</p>
<details><summary>Checks and constraints</summary><div class="stack">{''.join(checks)}</div><div class="proof">Shared rules and source boundaries above apply. The check state stays pending until evidence exists.</div></details></article>'''))

    open_section = section(soup, 'Open decisions and blockers')
    open_section.find('h2').string = 'Settled defaults and remaining source gaps' if authorized else 'Questions to answer once'
    open_section.find('small').string = 'Do not block frontend execution on demo data' if authorized else 'Proposed answers are filled in'
    stack = open_section.select_one('.stack')
    stack.clear()
    stack.append(fragment('<p class="proof" id="one-pass-answer"><b>One-pass review:</b> Read the scope above, then answer Q1–Q6 below. You can say “keep the proposed answers” and list only your changes. Missing dates or media can remain clearly marked in a review prototype. This is a review, not an automatic Launch.</p>'))
    if authorized:
        stack.select_one('#one-pass-answer').string = 'Matt authorized execution. Use the proposed defaults unless the clarified frontend scope or a logged rational amendment changes them. Unconfirmed dates, recordings and booking availability stay explicitly demo/proposed. Do not ask the sleeping user to approve settled scope again.'
    for q in data.get('review_questions', []):
        settled = authorized and q.get('status') in ('default_settled_for_execution', 'amended_for_execution')
        stack.append(fragment(f'<article class="surface message parent" id="question-{q["id"]}" data-state="{"SETTLED" if settled else "OPEN"}"><div class="message-head"><strong>{esc(q["id"])} · {esc(q["question"])}</strong><span>{"Applied frontend default" if settled else "Open input"}</span></div><p><b>{"Applied answer" if settled else "Proposed answer"}:</b> {esc(q["proposed_answer"])}</p><p><b>{"Original review prompt, retained for context" if settled else "Your input"}:</b> {esc(q["input"])}</p></article>'))
    stack.append(fragment('<h3 id="assumptions-title">Working assumptions and source gaps</h3>'))
    for ident, title, text in [
        ('prototype', 'Working assumption · interactive review prototype', 'The web app saves demonstration state locally, including a local video preview. Stripe and email states are simulated. A later infrastructure loop implements real accounts, video storage, payment fulfillment and sending.'),
        ('variants', 'Working assumption · variant coverage', 'Keep two landing variants for each offer, three critical CTA alternatives, substantive email A/B coverage, and a proposed 10 free-video + 10 direct-challenge ad split. All28 kit layouts appear meaningfully across the four-page family under the logged rational amendment. All four pages remain complete and materially different, with50 purposeful sections instead of112 repetitive blocks.'),
        ('commercial', 'Open input · pilot terms', '24,900 Ft once and 12 participants carry forward as review proposals, not a binding new client commitment. Daily feedback remains the core pilot service. The response timetable and workload assumptions must be plainly visible to Rita.'),
        ('dates', 'Open input · real dates', 'Starting date and signup closing date are not fixed. A countdown cannot imply a real deadline until it is agreed. Demo dates are visibly marked.'),
        ('recording-booking', 'Open input · Rita video and real booking URL', 'The source exercise text is available. Rita’s recorded instruction/welcome video and the actual final-consultation booking destination still need a verified source. The review labels teaching videos as unavailable and booking as a local demo. The technical playback fixture is neither a Rita lesson nor a participant recording.'),
    ]:
        stack.append(fragment(f'<article class="surface message parent" id="decision-{ident}" data-state="OPEN"><div class="message-head"><strong>{esc(title)}</strong></div><p>{esc(text)}</p></article>'))
    soup.select_one('[data-id="recovery"]').decompose()
    footer = soup.select_one('footer')
    footer.clear()
    footer.append(fragment(f'<p id="next-review-action"><b>Review now:</b> start with Goal and finish line, follow the Deliverable flow, then open the relevant card’s Checks and constraints. Review the assumptions and open inputs. Send changes in the Codex chat while comment saving is unavailable. A later explicit Launch is the production gate.</p><p><a href="{MAP_URL}">Two-diagram decision map</a> · <a href="{CONTRACT_URL}">Stable goal contract</a></p>'))
    if authorized:
        footer.select_one('#next-review-action').string = 'All44 frontend criteria are verified with their linked actual evidence. Review the published canvas and unsent client email. Daily service capacity, proposed pilot terms, actual recordings and dates still require a later operational agreement. No live payment, upload, email or booking service was implemented.' if complete else 'Execution authorized. The executor owns the frontend assets, logged rational criterion changes, actual verification and live evidence updates. Review the completed canvas and client email in the morning. This board is a progress/evidence surface, not proof that the requested assets are already done.'
    for node in soup.find_all(True):
        if node.get('id'):
            node['data-cid'] = node['id']
    for comment in soup.find_all(string=lambda text: isinstance(text, Comment)):
        comment.extract()
    output = str(soup)
    if re.search(r'\{\{[^{}]+\}\}', output):
        raise ValueError('Unresolved template placeholders')
    (OUT / 'contract.html').write_text(output)
    (PLAN / 'template-receipt.json').write_text(json.dumps({'source': str(TEMPLATE), 'output': str(OUT / 'contract.html'), 'deliverables': len(data['deliverables']), 'criteria': count, 'execution_state': data['execution_state']}, indent=2))
    print(f'Filled Matt template: {len(data["deliverables"])} cards, {done}/{count} verified checks')


if __name__ == '__main__':
    render()
