"""Create deterministic Unity implementation instructions for every concept game folder."""

from __future__ import annotations

import json
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1] / "content" / "speech"
PLAN = json.loads((ROOT / "plan" / "content-plan.json").read_text(encoding="utf-8"))


def concept_path(concept: dict) -> Path:
    return ROOT / "concepts" / Path(*concept["category"].split(".")) / concept["key"]


def render_instruction(concept: dict, brief: dict) -> str:
    key = concept["key"]
    name = concept["name"]
    nodes = brief["applicable_node_keys"]
    games = brief["games"]
    node_lines = "\n".join(f"- {node}" for node in nodes)
    game_lines = "\n".join(
        f"- `{g['game_key']}` — template `{g['template']}`, purpose `{g['purpose']}`, "
        f"difficulty {g['difficulty']}; distractors: {g['distractor_policy']}; "
        f"responses: {', '.join(g['response_modes'])}."
        for g in games
    )
    return f"""# Unity playable-game implementation: {name}

Concept key: `{key}`  
Concept type: `{concept['type']}`  
Category: `{concept['category']}`  
Status: implementation brief; SLP, accessibility, language/cultural and safeguarding review required.

## Outcome

Build one reusable Unity scene that can run five data-driven activities for `{name}`. The game must teach or assess the concept without requiring speech, audio, reading, colour discrimination or rapid motor responses. It must work with touch, mouse, keyboard/switch scanning and clinician-assisted pointing; enable AAC, eye-gaze and observed-response adapters when configured.

## Recommended Unity baseline

- Unity 2022.3 LTS or later LTS; Universal Render Pipeline is optional, not required.
- uGUI or UI Toolkit, with all interaction behind an `IResponseInput` interface.
- Addressables for images/audio; Unity Localization package for strings and locale assets.
- New Input System for touch, mouse, keyboard, gamepad and switch scanning.
- JSON content loaded from StreamingAssets or a remote immutable content bundle.
- No third-party analytics, advertising, accounts, chat, purchases or network dependency in the learner scene.
- Target 60 fps, but all success logic must remain frame-rate independent and untimed by default.

## Scene and prefab structure

```text
Scenes/
  SpeechGame.unity
Prefabs/
  StimulusCard.prefab
  ChoiceGrid.prefab
  PromptPanel.prefab
  FeedbackOverlay.prefab
  ProgressDots.prefab
  PauseAndStop.prefab
Scripts/
  SpeechGameController.cs
  TrialDirector.cs
  ContentLoader.cs
  DifficultyAdapter.cs
  PromptController.cs
  ResponseInputRouter.cs
  AccessibilitySettings.cs
  TrialEventWriter.cs
ScriptableObjects/
  ConceptGameDefinition.asset
  ReinforcementProfile.asset
Tests/
  EditMode/
  PlayMode/
```

Use a single `SpeechGame` scene. The controller receives `conceptKey={key}`, `gameKey`, `language`, `accessProfile`, `sessionSeed` and optional clinician overrides. Do not create concept-specific game logic; `{key}` is content data.

## Required content binding

- Target images: `../images/{key}_isolated_01.png`, `{key}_natural_context_01.png`, `{key}_variation_01.png`, `{key}_viewpoint_01.png`, `{key}_context_complex_01.png` and `{key}_unfamiliar_01.png`.
- Text: load semantic keys from `../text/` and approved child-facing strings from `../locales/<language>/`.
- Spoken prompts: optional approved files from `../locales/<language>/audio/`; never make audio the only cue.
- Concept game data: `game-briefs.json`; production tracking: `games-list.csv`.
- Sample screen reference: `{key}_unity_game_sample_01.png` in this folder. It is a visual direction reference, not a shippable UI asset.
- Verify asset checksums and reject missing or unknown keys before entering a trial.

## Applicable learning nodes

{node_lines}

## Five required game modes

{game_lines}

Implementation behavior:

1. **Find:** show one target and one clear unrelated distractor. Prompt using the approved semantic instruction. Randomize left/right while preventing more than two identical target positions in succession.
2. **Match:** show a reference card above two choices. A valid different exemplar of `{name}` is the match; do not match an image to an identical duplicate unless the learner is at the earliest access stage.
3. **Discriminate:** use two to four choices. Start with clear-category distractors; unlock same-category and context-rich distractors only after success. Never use colour or screen position as the answer cue.
4. **Use:** place `{name}` in a meaningful scene or routine. Accept configured touch, pointing, gesture, sign, AAC, vocalisation, speech or clinician-observed response; speech recognition must never be authoritative.
5. **Novel probe:** use `{key}_unfamiliar_01.png` or another approved unseen exemplar, give no corrective teaching during the scored probe, balance position and record it separately from teaching trials.

## Trial flow

`Load -> Ready -> Present stimulus -> Wait for response -> Record raw response -> Apply prompt/feedback -> Inter-trial pause -> Next or Stop`.

- Default field size: 2; configurable 1–4.
- No countdown. Default response window is unlimited; an optional clinician timeout records `no_response`, never `incorrect`.
- Lock choices only after a valid activation. Debounce duplicate touch/switch input for 300 ms.
- Correct response: gentle visual highlight and optional short neutral sound; no confetti storm or flashing.
- Incorrect response: no buzzer, red X, loss of points or negative language. Pause, reduce choices or provide the configured least-intrusive prompt.
- Offer an always-visible Stop/Pause control. Stop immediately on distress, fatigue or clinician action and preserve completed trial data.
- Use deterministic `sessionSeed` randomization so a session can be replayed exactly.

## Prompt hierarchy and adaptive difficulty

Prompt levels, from least to most support: independent, natural cue, visual cue, gesture, verbal cue, partial model, full model, physical assistance. Store raw correctness separately from independence.

- Increase difficulty after three consecutive independent correct trials across at least two target exemplars.
- Reduce field size or distractor similarity after two consecutive errors, two no-responses, or clinician-observed distress.
- Hold difficulty when correctness rises but prompt dependence increases.
- Never automatically advance on prompted correctness alone.
- Clinician override must be immediate and included in the event record.

## Accessibility and safeguarding

- Minimum interactive target: 96 x 96 logical pixels; minimum spacing: 24 pixels.
- Support large-card mode, high contrast, colour-independent focus outline, reduced motion, mute, captions and replay.
- Provide single-switch auto-scan with configurable 1–10 second interval and manual step-scan.
- Keep the selected item highlighted for at least 800 ms; never rely on a transient animation.
- Eye-gaze dwell is configurable and disabled by default; expose a cancel/dwell-reset action.
- Avoid drag-only interactions; every drag action needs tap-select/tap-destination and switch alternatives.
- Never display identifiable learner data, ads, external links or competitive leaderboards.
- Reinforcement is calm and age-respectful. Do not withhold communication, food, breaks or preferred items.

## Localization

- UI stores semantic keys, never embedded English. Use `{key}.label`, `{key}.instruction.identify`, `game.feedback.correct`, `game.feedback.try_again`, `game.pause` and `game.stop`.
- Allow text expansion to 200%, right-to-left layout, locale fonts and line wrapping without clipping.
- Voice and text may be independently enabled. The selected response mode must remain valid when audio is unavailable.
- Require native-language and SLP approval before marking a locale production-ready.

## Trial event contract

Write one append-only JSON object per presented trial:

```json
{{
  "schema_version": 1,
  "session_id": "pseudonymous-local-id",
  "session_seed": 0,
  "concept_key": "{key}",
  "node_key": "one-applicable-node-key",
  "game_key": "{key}_find",
  "language": "en",
  "stimulus_asset_key": "{key}_isolated_01",
  "choice_asset_keys": [],
  "target_index": 0,
  "response_mode": "touch",
  "raw_response": null,
  "correct": null,
  "prompt_level": "independent",
  "attempt": 1,
  "latency_ms": null,
  "difficulty": 1,
  "clinician_override": false,
  "stop_reason": null,
  "timestamp_utc": "ISO-8601"
}}
```

Store locally using the host application's approved privacy layer. Do not put names, recordings, free text or medical identifiers in the event.

## Sample-screen composition

Use a calm 16:9 landscape layout: neutral warm background, small progress dots at top, short localized prompt area, two or three large rounded stimulus cards in the center, persistent replay/accessibility/pause controls, and a reserved feedback region. The sample must depict `{name}` as the selected target with one clinically sensible clear distractor. Avoid embedded words in the generated mockup so it can guide all languages.

## Definition of done

- [ ] All five game keys load from `game-briefs.json` without code changes.
- [ ] Six target exemplars and approved distractors resolve by stable asset key.
- [ ] Touch, keyboard, switch scan and clinician-observed paths pass PlayMode tests.
- [ ] Audio-off, reduced-motion, high-contrast, large-card and RTL layouts pass.
- [ ] Position balancing and deterministic replay tests pass for 100 seeded sessions.
- [ ] Correctness, prompt, independence, latency and stop reason are recorded separately.
- [ ] Missing assets/localization fail safely before a learner trial.
- [ ] SLP, accessibility, cultural/language, privacy and technical reviews are recorded.
- [ ] `games-list.csv` is populated with owner and review status before release.

## Required automated tests

1. Load every game key and locale without exceptions.
2. Assert exactly one target per scored choice trial.
3. Assert target position balance stays within 10% over seeded runs.
4. Assert no more than two identical target positions consecutively.
5. Assert timeout produces `no_response`, not `incorrect`.
6. Assert prompted correctness does not count as independent mastery.
7. Assert Stop works during every state and writes a final stop event.
8. Assert missing audio still leaves a complete visual/non-speech path.
9. Assert all controls remain reachable at 200% text and large-card settings.
10. Assert no personally identifying fields are serialized.
"""


updated = 0
for concept in PLAN["concepts"]:
    folder = concept_path(concept) / "games"
    brief_path = folder / "game-briefs.json"
    brief = json.loads(brief_path.read_text(encoding="utf-8"))
    content = render_instruction(concept, brief).rstrip() + "\n"
    path = folder / "instruction.txt"
    if not path.exists() or path.read_text(encoding="utf-8") != content:
        path.write_text(content, encoding="utf-8")
        updated += 1

print(f"Unity game instructions ready: {len(PLAN['concepts'])} concepts; {updated} files updated")
