import Spec.Courseware.Model.Primitives
import Spec.Courseware.Export.Artifact

/-!
# Render —— export target = artifact + 有序 typed steps

一个 export target 是一次 build,产出一个有类型的 `Artifact`(ADR-0009)。build 的形状
是:一个 target = `artifact` + 一串有序的 typed step(ADR-0011)。

- `typstCompile template` —— 把模板文件(如 `exports/student.typ`)编译成产物。它是
  typed 而非裸 shell,因为框架要把 manifest 注入模板(经 `--input manifest=…`),裸
  字符串表达不了这个 wiring。presentation(编号、样式)住模板里,不在 manifest。
- `shell run` —— 逃生口,给难以声明的步骤。
- `assembleMarkdown field` —— 按 parts 顺序把每个 element 的 `field`(markdown content
  叶子,ADR-0015)拼成单文件 markdown 产物。typed 而非裸 `cat`:按 manifest `[[parts]]`
  顺序读取 + 跳过缺失项 + 写到单文件产物路径,框架自己来比裸 shell 稳。

渲染覆盖:契约只保留覆盖声明 `covers`(该 target 渲染哪些 kind),供种子诊断用;渲染的
"how"住模板里(ADR-0011)。

shell step 的执行语义(ADR-0013):`shell` 会被执行,语义是把 `run` 交给平台 shell、以
工程根为工作目录运行,产物由被调外部工具自己写出(框架不装配内容)。三条边界:

1. opt-in:命令执行只在用户显式 build 一个 shell target 时发生,绝不在 `check` 里跑。
   `check` 只校验结构,不执行外部工具、不验其产物。
2. 失败归属:shell step 退出非零是一次 build 过程失败,不是 lesson 的合法性缺陷;因此
   它不进 `Diagnostic` 的 7 类(那 7 类是 lesson 自身的诊断,见 `Check/Diagnostic.lean`),
   而由 build 执行层报告。诊断分类保持 7 类不变。
3. 非-typst target 不过 typst 编译:一个只含 `shell` step 的 target(教具包即此)由
   执行器跑命令,而非走 typst 引擎;`check` 的 compile 阶段跳过它。

assembleMarkdown step 的执行语义(ADR-0015):与 `shell` 同属"非-typst target"。框架
自己读每个 element 的 `<field>.md`、按 `[[parts]]` 顺序拼接、写到单文件产物路径(产物
是 markdown,不经 typst 引擎)。同 shell 的三条边界:① 只在显式 `build --target` 跑,
`check` 不跑;② 装配失败(如写盘失败、引用图缺失)是 build 过程错误,不入 7 类诊断;
③ 非-typst target,`check` 的 compile 阶段跳过它。

单文件产物(现):装配器注入课程级标题为文档 h1(课程元数据,非 element 内容),每份
`slides.md`/`transcript.md` 贡献其 `##` part 与 `###` 小节(presentation 在内容里,
ADR-0011),按 `[[parts]]` 顺序拼接(分隔符空行)。自包含:装配器扫描产物里的 `![](rel)`
图引用,把引用到的本地图从工程根按原相对路径复制进产物所在 build 根,使该 build 目录
可独立交付;引用图在工程根缺失属 build 过程错误(不入 7 类)。外部 URL(`http(s)://`、
`data:`)不复制。结构化 FileTree 产物延后(待 parts 树形重组,ADR-0015 OPEN)。
-/

namespace Spec.Courseware

variable (P : Primitives)

/-- 一个 build step(ADR-0011;可扩展)。MVP 仅一个 `typstCompile`;`steps` 是 list 因为
    FileTree、第三方 build 会需多步。不把模板内部、shell 命令的解析结构写进来(实现细节,
    ADR-0011 OPEN)。 -/
inductive Step where
  /-- 编译模板文件 `template`(相对工程根)成产物;框架注入 manifest。typed 的理由:
      注入这件事裸 shell 写不出。 -/
  | typstCompile (template : String)
  /-- shell 逃生口:执行命令 `run`(ADR-0013)。以工程根为 cwd 执行,opt-in(只在显式
      build 该 target 时跑,`check` 不跑),失败属 build 过程错误而非 lesson 诊断。
      教具包(如 KenKen 交互 HTML 由外部 `kendoku` 生成)即走此 step。 -/
  | shell (run : String)
  /-- 装配 markdown:按 `[[parts]]` 顺序读取每个 element 的 `field`(markdown content 叶子,
      ADR-0015),注入课程 h1 后拼接成单文件 markdown 产物,并收集 `![](rel)` 引用的本地图
      进 build 根使产物自包含。typed 而非 `cat`:按序读取+跳过缺失+注入标题+写盘+收集图由
      框架自己来。非-typst target(`check` 不跑,失败属 build 过程错误不入 7 类诊断)。
      slides 大纲、逐字稿口播即走此 step(直接 markdown+KaTeX 撰写,绕开 typst→md 公式转换,
      ADR-0014)。 -/
  | assembleMarkdown (field : String)

/-- 一个 export target 的 build 规格(ADR-0011:artifact + 有序 steps)。 -/
structure TargetSpec where
  /-- 产物(带字段,ADR-0011)。决定 build 折叠成单文件还是文件树。 -/
  artifact : Artifact
  /-- 有序 build steps。按序执行;MVP 仅一个 `typstCompile`。 -/
  steps : List Step
  /-- 覆盖声明:`covers k` 表示此 target 渲染 kind `k`。契约只声明"渲染哪些 kind"
      (种子诊断 `renderIgnored` 用),"how"由 `steps` 的模板实现(ADR-0011)。 -/
  covers : P.KindId → Prop

/-- 渲染配置(ADR-0009/0011)。`spec t = none` 表示 target `t` 未声明(不导出);
    `some s` 给出其 build 规格。 -/
structure RenderConfig where
  /-- target ↦ 该 target 的 build 规格(未声明则 `none`)。 -/
  spec : P.TargetId → Option (TargetSpec P)

/-- kind `k` 在 target `t` 下被渲染(ADR-0009/0011)。成立 ⟺ `t` 已声明
    (`spec t = some s`)且 `s.covers k`。为假即"此 kind 在此 target 下不被渲染"——
    checker 据此报 warning(见 `Diagnostic.renderIgnored`)。 -/
def RenderConfig.covers {P : Primitives} (c : RenderConfig P)
    (k : P.KindId) (t : P.TargetId) : Prop :=
  match c.spec t with
  | none => False
  | some s => s.covers k

end Spec.Courseware
