/**
 * spec-projections.js — docs/specs exports from SF working state.
 *
 * Purpose: export human-readable docs/specs files from the same SF working
 * model agents use in `.sf`, so git history captures the contract without
 * making docs a second source of truth.
 *
 * Consumer: commands-plan.js for `/plan specs generate|diff|check`.
 */

import { existsSync } from "node:fs";
import { join } from "node:path";
import { DatabaseSync } from "node:sqlite";
import { getErrorMessage } from "./error-utils.js";

const SPEC_GENERATOR_VERSION = "1";

function present(path) {
	return existsSync(path) ? "present" : "missing";
}

function countTable(db, table) {
	try {
		return (
			db.prepare(`SELECT COUNT(*) AS count FROM ${table}`).get()?.count ?? 0
		);
	} catch {
		return "missing";
	}
}

function readDbSummary(basePath) {
	const dbPath = join(basePath, ".sf", "sf.db");
	if (!existsSync(dbPath)) {
		return ["- Database state: missing"].join("\n");
	}
	let db;
	try {
		db = new DatabaseSync(dbPath);
		const version =
			db.prepare("SELECT MAX(version) AS version FROM schema_version").get()
				?.version ?? "unknown";
		const milestones = countTable(db, "milestones");
		const slices = countTable(db, "slices");
		const tasks = countTable(db, "tasks");
		const milestoneSpecs = countTable(db, "milestone_specs");
		const sliceSpecs = countTable(db, "slice_specs");
		const taskSpecs = countTable(db, "task_specs");
		return [
			`- Database schema version: ${version}`,
			`- DB planning rows: milestones=${milestones}, slices=${slices}, tasks=${tasks}`,
			`- DB spec rows: milestone_specs=${milestoneSpecs}, slice_specs=${sliceSpecs}, task_specs=${taskSpecs}`,
		].join("\n");
	} catch (err) {
		const message = getErrorMessage(err);
		return `- Database state: unreadable (${message})`;
	} finally {
		db?.close();
	}
}

/**
 * Build the source input block shared by generated spec exports.
 *
 * Purpose: make every generated spec disclose which `.sf` working model,
 * database, and source inputs it was projected from so reviewers can audit
 * drift.
 *
 * Consumer: generateOperatingModelSpec.
 */
export function buildWorkingModelInputs(basePath) {
	const sfDir = join(basePath, ".sf");
	return [
		"## Working Model Inputs",
		"",
		`- Generator: \`sf-spec-projections@${SPEC_GENERATOR_VERSION}\``,
		`- Database: \`.sf/sf.db\` (${present(join(sfDir, "sf.db"))})`,
		`- Guidance: \`.sf/PRINCIPLES.md\` (${present(join(sfDir, "PRINCIPLES.md"))})`,
		`- Guidance: \`.sf/STYLE.md\` (${present(join(sfDir, "STYLE.md"))})`,
		`- Guidance: \`.sf/NON-GOALS.md\` (${present(join(sfDir, "NON-GOALS.md"))})`,
		`- Optional knowledge: \`.sf/KNOWLEDGE.md\` (${present(join(sfDir, "KNOWLEDGE.md"))})`,
		`- Optional preferences: \`.sf/preferences.yaml\` (${present(join(sfDir, "preferences.yaml"))})`,
		readDbSummary(basePath),
		"- Source roots analyzed as implementation evidence: `src/resources/extensions/sf/`, `src/headless*.ts`, `src/cli.ts`, `src/help-text.ts`, `web/`, `vscode-extension/`, `packages/`",
		"",
		"This file is a human export for review, navigation, and git history. Generated docs are allowed to change because Git keeps the human-facing history. If SF needs operational history or future-use knowledge, store it in `.sf`/DB-backed state instead of relying on this export.",
		"",
	].join("\n");
}

/**
 * Generate the SF operating model human export.
 *
 * Purpose: provide one deterministic human-readable export of SF's terminology,
 * state, and source-placement contract from the repo-local working model.
 *
 * Consumer: `/plan specs generate` and `/plan specs check`.
 */
export function generateOperatingModelSpec(basePath) {
	return [
		"# SF Operating Model",
		"",
		buildWorkingModelInputs(basePath).trimEnd(),
		"",
		"SF has one workflow engine and one database-first working model. TUI, CLI, web, editor integrations, and non-interactive automation must all drive the same flow:",
		"",
		"```text",
		"intent -> structured state/evidence -> UOK/policy gates -> execution -> journal/evidence -> projected status",
		"```",
		"",
		"The names below are separate axes. Do not use one as a synonym for another.",
		"",
		"## Flow",
		"",
		"The flow is the product behavior: how SF captures intent, plans work, applies policy, executes tasks, records evidence, and reports status. Flow behavior must not fork by UI surface. If a TUI run and a non-interactive run receive the same state, run control, and permission profile, they should follow the same control model.",
		"",
		"## Surface",
		"",
		"A surface is where a person or program drives or observes the flow.",
		"",
		"- **TUI surface** — interactive terminal UI.",
		"- **CLI surface** — explicit commands and single-shot prompt entrypoints.",
		"- **Web surface** — browser UI around the same state and flow.",
		"- **Editor surface** — IDE/editor integration, usually through an adapter.",
		"- **Machine surface** — non-interactive runner for CI, scripts, schedulers, and parent processes.",
		"",
		'`sf headless` is the current command name for the machine surface. It means "without the TUI"; it does not mean "JSON", "autonomous", or a separate flow.',
		"",
		"## Protocol",
		"",
		"A protocol is how a surface or adapter talks to SF or to another agent process.",
		"",
		"- **RPC** — SF's current child-process control channel.",
		"- **stdio JSON-RPC** — process transport used by existing RPC-style adapters.",
		"- **ACP** — editor/client protocol adapter for agent/editor interoperability.",
		"- **HTTP/RPC** — possible daemon/web integration protocol.",
		"- **Wire** — a low-level internal message layer, if introduced, below surfaces and adapters.",
		"",
		"Protocols are adapters around the flow. They should translate messages, not invent policy or planning semantics.",
		"",
		"## Output Format",
		"",
		"An output format is only the encoding of a response or event stream.",
		"",
		"- `text` — human-readable progress and results.",
		"- `json` — one machine-readable result object.",
		"- `stream-json` / JSONL — event stream for parent processes and monitors.",
		"",
		"JSON is not a surface, run control, or permission profile. It is one output format that the machine surface can emit.",
		"",
		"## Run Control",
		"",
		"Run control describes how far SF continues through the flow before stopping for the operator.",
		"",
		"- **manual** — user approves each consequential step.",
		"- **assisted** — SF proposes and executes bounded steps, with human approval for important or uncertain actions.",
		"- **autonomous** — SF continues through the flow until policy, evidence, budget, or completion stops it.",
		"",
		"`auto` is not a run-control mode. Use **autonomous** for continuous run control; use **assisted** for bounded human-guided progression.",
		"",
		"> Competitor note: Copilot CLI calls continuous run control autopilot.",
		"> SF does not use that product name. The SF term is autonomous mode,",
		"> and it stays separate from permission profiles, surfaces, protocols,",
		"> and output formats.",
		"",
		"UOK kernel records carry `runControl` as a first-class lifecycle field. Workflow phases such as planning, building, verification, and finalization are separate execution stages, not run-control modes.",
		"",
		"## Permission Profile",
		"",
		"A permission profile describes what SF is allowed to touch when a run-control mode asks it to act.",
		"",
		"- **restricted** — read-mostly or planning-artifact-only permissions.",
		"- **normal** — default workspace permissions for ordinary project work.",
		"- **trusted** — wider local permissions for a trusted operator/workspace.",
		"- **unrestricted** — explicit danger profile; use for logs, policy records, or deliberate bypass flows, not as friendly default product language.",
		"",
		"Run control and permission profile are independent. For example, `autonomous + restricted` can keep going with narrow permissions, while `manual + trusted` still asks before each consequential step but can perform broader approved actions.",
		"",
		"UOK kernel records and execution-policy decisions carry `permissionProfile` as the trust posture. Permission expansion never implies autonomous continuation.",
		"",
		"## Naming Rules",
		"",
		"- Say **flow** for the shared planning/execution engine.",
		"- Say **surface** for TUI, CLI, web, editor, or machine entrypoints.",
		"- Say **protocol** for ACP, RPC, stdio JSON-RPC, HTTP, or wire messages.",
		"- Say **output format** for `text`, `json`, and `stream-json`.",
		"- Say **run control** for `manual`, `assisted`, and `autonomous`.",
		"- Say **permission profile** for `restricted`, `normal`, `trusted`, and `unrestricted`.",
		"- Use **headless** only for the current `sf headless` command and implementation path. Product docs should explain it as the machine surface.",
		"",
		"## Working State Contract",
		"",
		"SF working state is database-first. An initialized SF repo has `.sf/sf.db`, and runtime tools use it as the canonical structured store for planning hierarchy, ordering, gates, ledgers, schedules, and validation-sensitive state.",
		"",
		"Markdown under `.sf/` has two roles:",
		"",
		"- working guidance and knowledge that the runtime loads, such as `PRINCIPLES.md`, `STYLE.md`, `NON-GOALS.md`, `KNOWLEDGE.md`, and `preferences.yaml`;",
		"- human-readable projections from DB-owned records, such as rendered decisions, requirements, roadmap, plan, summary, and state files.",
		"",
		"Markdown under `docs/specs/` is a human export for review, navigation, and git history. Generated docs can change; Git records that human-facing history. If SF needs its own operational history, it should store that in `.sf`/DB-backed state. Plans should record any surface, protocol, output-format, run-control, or permission-profile impact explicitly when a milestone changes integration behavior.",
		"",
		"## Source Placement",
		"",
		"SF source placement follows the same axis model. New code should extend the owning axis instead of creating parallel trees.",
		"",
		"### Core Flow",
		"",
		"- `src/resources/extensions/sf/` owns the SF workflow extension: planning tools, UOK/runtime state, `/next` commands, prompts, templates, doctors, schedule, and DB-backed state.",
		"- `src/resources/extensions/` owns bundled extension packages loaded into the runtime.",
		"- `src/resources/agents/`, `src/resources/skills/`, and `src/resources/workflows/` own bundled runtime resources, not independent product flows.",
		"",
		"### Surfaces",
		"",
		"- `src/cli.ts` and `src/help-text.ts` own CLI/session entrypoint behavior and command help.",
		"- `src/headless*.ts` owns the existing `sf headless` machine-surface command path. Keep the command name; describe it as the machine surface in product language.",
		"- `web/` owns the browser surface.",
		"- `vscode-extension/` owns the editor surface.",
		"- `packages/tui/` owns reusable TUI primitives and terminal UI components.",
		"",
		"### Protocols And Adapters",
		"",
		"- `packages/rpc-client/` owns reusable RPC client protocol code.",
		"- RPC child-process orchestration stays in the machine-surface path unless promoted into a reusable protocol package.",
		"- ACP, stdio JSON-RPC, HTTP, and future wire layers are protocol/adapters. They should translate messages to the same SF flow, not fork planning semantics.",
		"",
		"### Workspace Packages",
		"",
		"- `packages/agent-core/` owns reusable agent-core primitives.",
		"- `packages/ai/` owns provider/model integration.",
		"- `packages/coding-agent/` owns reusable coding-agent substrate inherited from Pi.",
		"- `packages/daemon/` owns daemonized background service code.",
		"- `packages/native/` and `rust-engine/` own native/Rust performance paths.",
		"",
		"### State And Projections",
		"",
		"- `.sf/sf.db` is the canonical structured runtime state store for initialized SF repos. Treat a missing or unreadable DB as bootstrap/recovery, not a normal alternate source of truth.",
		"- `.sf/DECISIONS.md`, `.sf/REQUIREMENTS.md`, milestone roadmaps, and similar files are rendered working projections when database-backed tools own the data. They are useful to humans and agents but must not compete with DB rows.",
		"- `.sf/PRINCIPLES.md`, `.sf/STYLE.md`, `.sf/NON-GOALS.md`, `.sf/KNOWLEDGE.md`, and `.sf/preferences.yaml` are repo-local working guidance files when present.",
		"- Generated `.sf/` runtime files are evidence, projections, or import/recovery artifacts.",
		"- Durable human-facing exports belong in `docs/specs/`, `docs/adr/`, or `docs/plans/`. They are reviewable projections and git-history artifacts, not a second planning database.",
		"",
		"### Placement Rules",
		"",
		"- Do not create a second implementation because a feature appears in another surface. Add an adapter to the same flow.",
		"- Do not name output encodings as surfaces. JSON belongs to output formats.",
		"- Do not name permission expansion as run control. `autonomous` means the loop continues; `trusted` or `unrestricted` means the permission profile widened.",
		"- Do not route human questions because of `headless`. Questions come from run-control and permission-policy gates; the surface only determines delivery.",
		"",
	].join("\n");
}

export const PROMOTED_SPEC_PROJECTIONS = [
	{
		path: "docs/specs/sf-operating-model.md",
		generate: generateOperatingModelSpec,
	},
];
