package skillbill.scaffold.model

import java.nio.file.Path

data class RoutingSignals(
  val strong: List<String>,
  val tieBreakers: List<String>,
)

data class DeclaredFiles(
  /**
   * SKILL-47/SKILL-48 contract: `null` means the pack ships no code-review baseline — that is
   * a meaningful, intentional absence at the manifest layer (e.g. quality-check-only packs).
   * Consumers MUST NOT re-narrow this with `!!` or default to a synthesized path; the schema
   * (`orchestration/contracts/platform-pack-schema.yaml`, `declared_files.baseline`) is the
   * single source of truth for the optional/required boundary, and `areas-require-baseline`
   * ensures areas without a baseline already loud-fail upstream.
   */
  val baseline: Path?,
  val areas: Map<String, Path>,
)

data class PointerSpec(
  val skillRelativeDir: String,
  val name: String,
  val target: String,
)

data class PlatformManifest(
  val slug: String,
  val packRoot: Path,
  val contractVersion: String,
  val routingSignals: RoutingSignals,
  val declaredCodeReviewAreas: List<String>,
  val declaredFiles: DeclaredFiles,
  val areaMetadata: Map<String, String>,
  val displayName: String? = null,
  val notes: String? = null,
  val declaredQualityCheckFile: Path? = null,
  val pointers: List<PointerSpec> = emptyList(),
  /**
   * SKILL-48 Subtask 3: carries every non-anchored top-level field from `platform.yaml`
   * verbatim. Intentionally untyped (`Map<String, Any?>`) so repo authors can add
   * fork-specific keys to the canonical schema without runtime support being added first;
   * generating Kotlin types for these fields is out of scope.
   */
  val customFields: Map<String, Any?> = emptyMap(),
) {
  /**
   * SKILL-47/SKILL-48 contract: stable identifier of the pack's code-review baseline shell.
   * `null` means the pack has no code-review feature declared — derived from
   * [DeclaredFiles.baseline] being absent in the manifest. Consumers MUST NOT re-narrow with
   * `!!` or default to a synthesized name to hide that absence; the manifest schema is the
   * single source of truth for whether code-review applies, and downstream features that
   * require it (e.g. agent rendering) must null-check this property.
   */
  val routedSkillName: String? = declaredFiles.baseline?.let { "bill-$slug-code-review" }
}

data class GovernedAddonFile(
  val packSlug: String,
  val addonPath: Path,
) {
  val addonSlug: String = addonPath.fileName.toString().removeSuffix(".md")
}

data class ScaffoldResult(
  val kind: String,
  val skillName: String,
  val skillPath: Path,
  val createdFiles: List<Path> = emptyList(),
  val manifestEdits: List<Path> = emptyList(),
  val symlinks: List<Path> = emptyList(),
  val installTargets: List<Path> = emptyList(),
  val notes: List<String> = emptyList(),
)

/**
 * Match rule for a skill class. The renderer picks the first class file whose matcher list
 * resolves the candidate skill name; within a file, an `exact` match wins over a `pattern` match.
 * `excludeExact` removes skill names that would otherwise match a `pattern`, used when a more
 * specific class wants to opt out of a broader regex declared in another file.
 */
data class SkillClassMatcher(
  val exact: String? = null,
  val pattern: Regex? = null,
  val excludeExact: List<String> = emptyList(),
)

/**
 * Framework-owned section inserted between the generated `## Descriptor` and the authored
 * `## Execution` body. Body is written verbatim, no template substitution.
 */
data class SkillClassSection(
  val heading: String,
  val body: String,
)

/**
 * A single class manifest read from `orchestration/skill-classes/<class>.yaml`. Captures
 * framework behavior for a category of skills (e.g. code-review-shell, quality-check-leaf) so
 * authored `content.md` files can stay free of ceremony and renderer-owned prose.
 */
data class SkillClassManifest(
  val classId: String,
  val classFile: Path,
  val contractVersion: String,
  val matchers: List<SkillClassMatcher>,
  val pointers: List<String>,
  val sections: List<SkillClassSection>,
  val ceremonyLines: List<String>,
)
