/**
 * Generate committed Cordis artifacts from the Typert catalog projector and
 * the independent vendored-core projector.
 */

import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
import { dirname, resolve } from 'node:path'
import {
  projectCordisCatalog,
  renderEvents,
  renderServices,
} from '@deepseek-ai/dsh-typert-generator'
import type { CordisCatalogPolicy } from '@deepseek-ai/dsh-typert-generator'
import { renderCordisCoreApiPages } from './cordis-core-api.ts'

const root = resolve(import.meta.dirname, '..')
const OUT_EVENTS = 'docs/cordis-catalog/events.md'
const OUT_SERVICES = 'docs/cordis-catalog/services.md'
const OUT_RUNTIME_API = 'packages/cordis/tool-cordis/src/api-catalog.ts'

/** One primary core-data-structures page per project type used by a generated signature. */
export const LINK_MAP: Readonly<Record<string, string>> = {
  Agent: 'core.md',
  AgentCancelCause: 'core.md',
  AgentOptions: 'core.md',
  AgentStatus: 'core.md',
  ContentBlock: 'core.md',
  ContinuationDecision: 'core.md',
  ContinuationStop: 'core.md',
  GenerateOptions: 'core.md',
  MessageId: 'core.md',
  HookContext: 'core.md',
  SettleReason: 'core.md',
  AdapterRegistrationHandle: 'core.md',
  DirectoryRegistrationHandle: 'core.md',
  LlmCallConfig: 'core.md',
  LlmModelContext: 'core.md',
  LlmModelReasoningInfo: 'core.md',
  LlmResolvedModelInfo: 'core.md',
  LlmFailure: 'llm-streaming.md',
  LlmModelInfo: 'core.md',
  LlmProviderInfo: 'core.md',
  LlmConfigurableProvider: 'core.md',
  LlmModelDiscoveryRequest: 'core.md',
  LlmDiscoveredModel: 'core.md',
  ResolvedRetryPolicy: 'llm-streaming.md',
  Message: 'core.md',
  MessageSource: 'core.md',
  UserMessage: 'session.md',
  PreStepDecision: 'core.md',
  PreStepContext: 'core.md',
  RequestErrorAction: 'core.md',
  RequestFailureContext: 'core.md',
  PreparedReferencedMessage: 'session-reference.md',
  SessionReferenceCandidate: 'session-reference.md',
  SessionReferenceInput: 'session-reference.md',
  SessionEvent: 'core.md',
  SessionId: 'core.md',
  SessionStartSource: 'core.md',
  SessionLogSnapshot: 'session-query.md',
  SessionSurfaceSnapshot: 'session-query.md',
  ApprovalOutcome: 'approval.md',
  ApprovalPolicy: 'approval.md',
  ApprovalRequest: 'approval.md',
  ApprovalService: 'approval.md',
  BashExecRequest: 'bash.md',
  BashExecSpec: 'bash.md',
  BashProcess: 'bash.md',
  BashRunResult: 'bash.md',
  DshEnvironment: 'subprocess.md',
  SubprocessHandle: 'subprocess.md',
  SubprocessOutcome: 'subprocess.md',
  SubprocessOutputRead: 'subprocess.md',
  SubprocessOutputReader: 'subprocess.md',
  SubprocessSpawnSpec: 'subprocess.md',
  CodeRunRequest: 'code-runtime.md',
  CodeRunResult: 'code-runtime.md',
  CompactionResult: 'compaction.md',
  CompactionTrigger: 'compaction.md',
  PruneResult: 'compaction.md',
  FileReadOutcome: 'filesystem.md',
  FsDirEntry: 'filesystem.md',
  FsEditOutcome: 'filesystem.md',
  FsEditRequest: 'filesystem.md',
  FsInfo: 'filesystem.md',
  FsPathInfo: 'filesystem.md',
  FsPolicyExec: 'filesystem.md',
  FsTarget: 'filesystem.md',
  FsVersion: 'filesystem.md',
  FsWriteIntent: 'filesystem.md',
  FsWriteOutcome: 'filesystem.md',
  CreateGoalRequest: 'goal.md',
  CreateGoalResult: 'goal.md',
  EditGoalRequest: 'goal.md',
  GoalBlockReason: 'goal.md',
  GoalChanged: 'goal.md',
  GoalRef: 'goal.md',
  GoalView: 'goal.md',
  CommandDefinition: 'commands.md',
  CommandDescriptor: 'commands.md',
  CommandResult: 'commands.md',
  CommandSurface: 'commands.md',
  LlmAdapter: 'llm-streaming.md',
  PreparedLlmCall: 'llm-streaming.md',
  LlmService: 'llm-streaming.md',
  StreamChunk: 'llm-streaming.md',
  SkillProviderControl: 'skills.md',
  CreateSessionOptions: 'persistence.md',
  PrepareSessionOptions: 'persistence.md',
  SessionHeader: 'persistence.md',
  SessionInspection: 'persistence.md',
  SessionLocation: 'persistence.md',
  SessionPreparation: 'persistence.md',
  SessionPersistenceSnapshot: 'persistence.md',
  ConfinedArgv: 'sandbox.md',
  SandboxExecutionPolicy: 'sandbox.md',
  SandboxMode: 'sandbox.md',
  SandboxPolicy: 'sandbox.md',
  PtyBackend: 'pty.md',
  PtyReadRequest: 'pty.md',
  PtyReadResult: 'pty.md',
  PtySendOperation: 'pty.md',
  PtySendRequest: 'pty.md',
  PtySessionId: 'pty.md',
  PtySessionSnapshot: 'pty.md',
  PtySignal: 'pty.md',
  PtySignalResult: 'pty.md',
  PtySpawnRequest: 'pty.md',
  PtySpawnResult: 'pty.md',
  SandboxPolicyRequest: 'sandbox.md',
  ScopeKey: 'scope.md',
  Scoped: 'scope.md',
  EpochHeader: 'session.md',
  Session: 'session.md',
  SessionEventMap: 'session.md',
  TurnEndReason: 'session.md',
  TurnTrigger: 'session.md',
  SessionEventReadRequest: 'session-query.md',
  SessionEventRecord: 'session-query.md',
  SessionEventResultFilter: 'session-query.md',
  SessionEventSearchDocument: 'session-query.md',
  SessionEventSearchHit: 'session-query.md',
  SessionEventSearchPage: 'session-query.md',
  SessionEventSearchRequest: 'session-query.md',
  SessionEventTrace: 'session-query.md',
  SessionEventTraceObservation: 'session-query.md',
  SessionEventTraceRequest: 'session-query.md',
  SessionEventWindow: 'session-query.md',
  SessionLineageTrace: 'session-query.md',
  SessionRecord: 'session-query.md',
  SessionResultFilter: 'session-query.md',
  SessionSearchExecContext: 'session-query.md',
  SessionSearchHit: 'session-query.md',
  SessionSearchPage: 'session-query.md',
  SessionSearchRequest: 'session-query.md',
  SessionTitleObservation: 'session-query.md',
  SessionTitleObservationResult: 'session-query.md',
  SessionTitleProvider: 'session-title.md',
  SessionTitleSnapshot: 'session-title.md',
  SkillCatalogSnapshot: 'skills.md',
  SkillDefinition: 'skills.md',
  SkillLookupOptions: 'skills.md',
  SkillProvider: 'skills.md',
  SkillProviderObservation: 'skills.md',
  SkillRegistration: 'skills.md',
  SkillSummary: 'skills.md',
  SaveTextSpill: 'spill.md',
  SpillRef: 'spill.md',
  ContinuableCreateRequest: 'subagent.md',
  ContinuableCreateSpec: 'subagent.md',
  ContinuableSetupContribution: 'subagent.md',
  ContinuableStart: 'subagent.md',
  ContinuableStartSpec: 'subagent.md',
  CoordinatorMessageSource: 'subagent.md',
  SubagentFollowupOptions: 'subagent.md',
  SubagentListEntry: 'subagent.md',
  SubagentProvider: 'subagent.md',
  SubagentReportDelivery: 'subagent.md',
  SubagentReportMessageSource: 'subagent.md',
  SubagentReportOptions: 'subagent.md',
  SubagentRun: 'subagent.md',
  SubagentService: 'subagent.md',
  SubagentStartRequest: 'subagent.md',
  AssembleContext: 'system-prompt.md',
  PromptContext: 'system-prompt.md',
  PromptSection: 'system-prompt.md',
  SystemPrompt: 'system-prompt.md',
  ToolProviderResult: 'system-prompt.md',
  TaskDoneListener: 'tasks.md',
  TaskId: 'tasks.md',
  TaskRead: 'tasks.md',
  TaskSnapshot: 'tasks.md',
  TaskStart: 'tasks.md',
  TokenMeasurement: 'token-meter.md',
  CodeDispatchLog: 'tools.md',
  PostToolDecision: 'tools.md',
  PreToolDecision: 'tools.md',
  ToolDefinition: 'tools.md',
  ToolExecution: 'tools.md',
  ToolDispatchExecution: 'tools.md',
  ToolExecutionInput: 'tools.md',
  ToolExecutionMode: 'tools.md',
  ToolExecutionResult: 'tools.md',
  ToolExecutionToken: 'tools.md',
  ToolGuard: 'tools.md',
  ToolRegistry: 'tools.md',
  ToolRestriction: 'tools.md',
  ToolSchema: 'tools.md',
  SettingsNamespace: 'settings.md',
  SettingsRegisterOptions: 'settings.md',
  SettingsScope: 'settings.md',
  SettingsDescriptor: 'settings.md',
  SettingsPathOp: 'settings.md',
  SettingsDescribeOptions: 'settings.md',
  SettingsUpdateSource: 'settings.md',
  CredentialRef: 'credentials.md',
  CredentialInfo: 'credentials.md',
  ResolvedCredential: 'credentials.md',
  AskUserQuestionAnswer: 'user-interaction.md',
  AskUserQuestionRequest: 'user-interaction.md',
  UserInteractionProvider: 'user-interaction.md',
  WebFetchProvider: 'web.md',
  WebFetchRequest: 'web.md',
  WebFetchResult: 'web.md',
  WebSearchProvider: 'web.md',
  WebSearchRequest: 'web.md',
  WebSearchResult: 'web.md',
  WorkflowRun: 'workflow.md',
  WorkflowRunInfo: 'workflow.md',
  WorkflowStartRequest: 'workflow.md',
}

/** TypeScript lib and pinned framework types with no repository-owned data page. */
export const FOUNDATION_TYPE_NAMES: ReadonlySet<string> = new Set([
  'AbortSignal',
  'AsyncIterable',
  'Context',
  'Error',
  'Map',
  'Partial',
  'Pick',
  'Promise',
  'Readonly',
])

/** Project types deliberately documented outside the core-data catalog. */
export const TYPE_LINK_EXEMPTIONS: Readonly<Record<string, string>> = {
  AgentFactory: 'agent creation seam is owned by packages/core/agent/README.md',
  z: 'schemastery schema constructor is owned by vendor/schemastery (vendored upstream)',
  BeginCommandRequest: 'event-local request contract is owned by packages/client/ui-slash/src/types.ts',
  InsertReferenceRequest: 'event-local request contract is owned by packages/client/ui-slash/src/types.ts',
  ConsumeTokenRequest: 'event-local request contract is owned by packages/client/ui-slash/src/types.ts',
  InsertTextRequest: 'event-local request contract is owned by packages/client/ui-slash/src/types.ts',
  AgentHandle: 'agent ownership handle is owned by packages/core/agent/README.md',
  BashEnvContributor: 'service-local extension type is owned by packages/bash/tool-bash/src/index.ts',
  BashEnvVariableInfo: 'service-local metadata type is owned by packages/bash/tool-bash/src/index.ts',
  CompactAgentContext: 'compaction service input is owned by packages/compact/compact/src/index.ts',
  ManualCompactAgentContext: 'manual compaction service input is owned by packages/compact/compact/src/index.ts',
  DirectoryPickerCapability: 'picker interaction contract is owned by packages/host/directory-picker/README.md',
  CreateAgentOptions: 'agent creation contract is owned by packages/core/agent/README.md',
  Domain: 'domain interface is owned by packages/storage/storage-domain/README.md',
  DomainChanged: 'event-local snapshot is owned by packages/storage/storage-domain/src/events.ts',
  DomainFacility: 'domain form facility is owned by packages/storage/storage-domain/README.md',
  DomainImpl: 'domain implementation contract is owned by packages/storage/storage-domain/README.md',
  DomainSpec: 'domain declaration contract is owned by packages/storage/storage-domain/README.md',
  StorageBackend: 'backend contract is owned by packages/storage/storage/src/backend.ts',
  StorageForms: 'merge-extensible form map is owned by packages/storage/storage/src/index.ts',
  ProjectionDefinition: 'projection unit contract is owned by packages/session-projection/session-projection/README.md',
  SessionProjectionMap: 'merge-extensible projection key map is owned by packages/session-projection/session-projection/src/types.ts',
  ProjectionChangeListener: 'change-feed listener contract is owned by packages/session-projection/session-projection/src/index.ts',
  ProjectionSnapshot: 'watermark snapshot shape is owned by packages/session-projection/session-projection/src/index.ts',
  ProjectionCheckpoint: 'persisted checkpoint row map is owned by packages/session-projection/session-projection/src/index.ts',
  CommandExecution: 'executor return contract is owned by packages/ui/commands/src/index.ts',
  TypertContribution: 'registry contribution contract is owned by packages/typert/registry/README.md',
  TypertFace: 'registry face identity is owned by packages/typert/registry/README.md',
  TypertPackageFilter: 'registry package query filter is owned by packages/typert/registry/README.md',
  TypertPackageRecord: 'registry package record is owned by packages/typert/registry/README.md',
  TypertSchemaFilter: 'registry schema query filter is owned by packages/typert/registry/README.md',
  TypertSchemaRecord: 'registry schema record is owned by packages/typert/registry/README.md',
  TypeRTDisposer: 'TypeRT lifecycle contract is owned by packages/typert/type-meta/README.md',
  'z.core.JSONSchema.BaseSchema': 'zod projection output is owned by the zod v4 API',
  'z.core.ToJSONSchemaParams': 'zod projection parameters are owned by the zod v4 API',
  InvariantInstaller: 'service-local contribution contract is owned by packages/support/invariants/README.md',
  LocaleDict: 'service-local dictionary shape is owned by packages/client/i18n/src/index.ts',
  WebBootGraph: 'web boot graph wire shape is owned by packages/client/modules/src/client/index.ts',
  WebRoute: 'route registration contract is owned by packages/host/webserver/src/index.ts',
  WebUpgradeRoute:
    'upgrade route registration contract is owned by packages/host/webserver/src/index.ts',
  ThemeTokens: 'service-local token dictionary is owned by packages/client/ui-theme/src/index.ts',
  Translate: 'service-local bound translator is owned by packages/client/i18n/src/index.ts',
  InvariantRegistration: 'service-local lifecycle handle is owned by packages/support/invariants/README.md',
  InvokeRemoteRequest: 'gateway invocation contract is owned by packages/api/gateway/README.md',
  PresetOption: 'deployment menu metadata is owned by packages/ui/permission/README.md',
  PresetSpec: 'deployment preset composition is owned by packages/ui/permission/README.md',
  KnobState: 'projection unit state shape is owned by packages/ui/permission/README.md',
  PermissionSelect: 'permissions projection payload is owned by packages/ui/permission/src/types.ts',
  PromptAssembly: 'assembly result is owned by packages/core/system-prompt/README.md',
  ResumeAgentOptions: 'agent resume contract is owned by packages/core/agent/README.md',
  SessionForkSource: 'service-local fork input is owned by packages/core/session/src/index.ts',
  SubagentRunEndInfo: 'event payload contract is owned by packages/subagent/subagent/src/types.ts',
  SubagentRunInfo: 'event payload contract is owned by packages/subagent/subagent/src/types.ts',
  TelemetryRecord: 'seam-local record contract is owned by packages/telemetry/session-telemetry/src/index.ts',
  WorkflowAgentEndInfo: 'event-local snapshot is owned by packages/workflow/workflow/src/index.ts',
  WorkflowAgentInfo: 'event-local snapshot is owned by packages/workflow/workflow/src/index.ts',
  WorkflowResultInfo: 'event-local snapshot is owned by packages/workflow/workflow/src/index.ts',
  Workspace: 'workspace entity contract is owned by packages/workspace/workspace/README.md',
  WorkspaceId: 'branded id is owned by packages/workspace/workspace/README.md',
}

/** Repository data policy consumed by the Cordis catalog projector. */
export const CORDIS_CATALOG_POLICY: CordisCatalogPolicy = {
  linkedTypePages: LINK_MAP,
  foundationTypeNames: FOUNDATION_TYPE_NAMES,
  typeLinkExemptions: TYPE_LINK_EXEMPTIONS,
  inheritedEvents: [
    { name: 'internal/plugin', summary: 'A plugin fiber was created.', source: 'vendor/cordis/src/events.ts:328' },
    { name: 'internal/status', summary: 'A fiber changed lifecycle state.', source: 'vendor/cordis/src/events.ts:330' },
    { name: 'internal/service', summary: 'Interception hook for a service binding (no core producer).', source: 'vendor/cordis/src/events.ts:332' },
    { name: 'internal/update', summary: 'Waterfall: a fiber config update is being applied.', source: 'vendor/cordis/src/events.ts:334' },
    { name: 'internal/get', summary: 'Waterfall: a service is being read from the store.', source: 'vendor/cordis/src/events.ts:336' },
    { name: 'internal/set', summary: 'Waterfall: a service is being written to the store.', source: 'vendor/cordis/src/events.ts:338' },
    { name: 'internal/listener', summary: 'A listener was registered.', source: 'vendor/cordis/src/events.ts:340' },
    { name: 'internal/dispatch', summary: 'An event is being dispatched to listeners.', source: 'vendor/cordis/src/events.ts:342' },
    { name: 'hmr/change', summary: 'A watched source file changed on disk.', source: 'vendor/hmr/src/index.ts:20' },
    { name: 'hmr/reload', summary: 'Plugins are being reloaded after a change.', source: 'vendor/hmr/src/index.ts:22' },
    { name: 'hmr/config-update-failed', summary: 'A watched config-file refresh failed.', source: 'vendor/hmr/src/index.ts:29' },
    { name: 'exit', summary: 'The process is exiting on a signal.', source: 'vendor/loader/src/index.ts:23' },
    { name: 'loader/config-update', summary: 'The loader config tree changed.', source: 'vendor/loader/src/index.ts:24' },
    { name: 'loader/entry-init', summary: 'A config entry is being initialized.', source: 'vendor/loader/src/index.ts:25' },
    { name: 'loader/partial-dispose', summary: 'An entry is being partially disposed on reload.', source: 'vendor/loader/src/index.ts:26' },
    { name: 'loader/patch-context', summary: 'A context is being patched during a reload.', source: 'vendor/loader/src/index.ts:27' },
  ],
  inheritedServices: [
    { name: 'ctx.on / ctx.once', summary: 'Register an event listener (disposable).', source: 'vendor/cordis/src/events.ts:34' },
    { name: 'ctx.emit / ctx.parallel / ctx.serial / ctx.bail / ctx.waterfall', summary: 'Dispatch an event (sync / awaited / first-bail / veto-chain).', source: 'vendor/cordis/src/events.ts:34' },
    { name: 'ctx.plugin / ctx.inject', summary: 'Load a plugin / declare required services.', source: 'vendor/cordis/src/registry.ts:164' },
    { name: 'ctx.effect', summary: 'Register a disposable side effect tied to the fiber.', source: 'vendor/cordis/src/fiber.ts:9' },
    { name: 'ctx.get / ctx.set / ctx.provide / ctx.accessor / ctx.mixin', summary: 'Low-level service-store access and binding.', source: 'vendor/cordis/src/reflect.ts:7' },
    { name: 'ctx.extend / ctx.isolate / ctx.intercept', summary: 'Derive a child context (scoped services / isolation / interception).', source: 'vendor/cordis/src/context.ts:42' },
    { name: 'ctx.root / ctx.scope / ctx.fiber / ctx.registry / ctx.reflect / ctx.events / ctx.logger', summary: 'Ambient handles onto the running context graph.', source: 'vendor/cordis/src/context.ts:16' },
    { name: 'ctx.timer (+ interval / timeout / throttle / debounce / setTimeout / setInterval)', summary: 'Disposable timer helpers. The `timer` key is provided at runtime; the six helpers are mixed onto ctx directly (declared via Pick).', source: 'vendor/timer/src/index.ts:4' },
    { name: 'ctx.loader', summary: 'The config Loader that booted the app (present under the loader).', source: 'vendor/loader/src/index.ts:30' },
    { name: 'ctx.hmr', summary: 'The hot-module-reload watcher (present under the hmr plugin).', source: 'vendor/hmr/src/index.ts:15' },
  ],
}

/** CLI entry: default writes every artifact; `--check` reports stale files.
 * @returns nothing; writes files or reports freshness through the process.
 */
export function main(): void {
  const { projector, model } = projectCordisCatalog(root, CORDIS_CATALOG_POLICY)
  const outputs: [string, string][] = [
    [OUT_EVENTS, renderEvents([...model.events], CORDIS_CATALOG_POLICY)],
    [OUT_SERVICES, renderServices([...model.services], CORDIS_CATALOG_POLICY)],
    [OUT_RUNTIME_API, projector.renderRuntimeApi(model)],
    ...renderCordisCoreApiPages(),
  ]
  if (process.argv.includes('--check')) {
    const stale: string[] = []
    for (const [out, content] of outputs) {
      let committed: string | null = null
      try {
        committed = readFileSync(resolve(root, out), 'utf8')
      } catch {
        // Only ENOENT is expected; either read failure has the same remedy.
        committed = null
      }
      if (committed !== content) stale.push(out)
    }
    if (stale.length === 0) {
      console.log(`gen-cordis-catalog: ${outputs.length} generated file(s) are up to date.`)
      process.exit(0)
    }
    console.error(`gen-cordis-catalog: ${stale.join(' and ')} ${stale.length === 1 ? 'is' : 'are'} stale. Run \`pnpm run gen-cordis-catalog\` and commit the result.`)
    process.exit(1)
  }

  for (const [out, content] of outputs) {
    const destination = resolve(root, out)
    mkdirSync(dirname(destination), { recursive: true })
    writeFileSync(destination, content)
  }
  console.log(`gen-cordis-catalog: wrote ${outputs.length} generated file(s).`)
}

if (process.argv[1] && import.meta.filename === resolve(process.argv[1])) main()
