// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. bundle module for scripts/src/artifact-registry.ts.

// Moved mechanically from scripts/src/artifact-registry.ts:979-1080 for #903.

import { createHash } from "node:crypto";
import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { runCommand, type CommandResult } from "../command";
import { readConfig, type UniDeskConfig, repoRoot, rootPath } from "../config";
import { startJob } from "../jobs";
import {
  compareDeployJsonExecutorMirrors,
  deployJsonCommitImage,
  deployJsonDriftResult,
  deployJsonSourceOfTruth,
  hasDeployJsonExecutorContract,
  k3sManifestExecutorMirror,
  parseDeployJsonServiceContractBase64,
  readDeployJsonServiceContractFromFile,
  type DeployJsonExecutorMirror,
  type DeployJsonServiceContract,
} from "../deploy-json-contract";
import { d601K3sGuardShellLines } from "../d601-k3s-guard";
import { composeRuntimeEnvValue } from "../runtime-env";

import type { ArtifactRegistryOptions, RenderedBundle, RenderedFile } from "./types";

export function sha256(text: string): string {
  return createHash("sha256").update(text).digest("hex");
}

export function shellQuote(value: string): string {
  return `'${value.replace(/'/g, `'\\''`)}'`;
}

export function d601K3sGuardScript(): string {
  return d601K3sGuardShellLines().join("\n");
}

export function base64(value: string): string {
  return Buffer.from(value, "utf8").toString("base64");
}

export function safeName(value: string): string {
  return value.replace(/[^A-Za-z0-9_.-]/gu, "-");
}

export function rootExecPrelude(): string {
  return [
    "root_exec() {",
    "  if [ \"$(id -u)\" = \"0\" ]; then \"$@\"; return; fi",
    "  if sudo -n true >/dev/null 2>&1; then sudo -n \"$@\"; return; fi",
    "  if [ -x /mnt/c/Windows/System32/wsl.exe ]; then /mnt/c/Windows/System32/wsl.exe -u root -- \"$@\"; return; fi",
    "  echo 'artifact_registry_root_access=missing' >&2",
    "  return 1",
    "}",
  ].join("\n");
}

export function file(path: string, content: string, mode = "0644"): RenderedFile {
  return { path, mode, sha256: sha256(content), content };
}

export function registryConfig(): string {
  return `version: 0.1
log:
  fields:
    service: unidesk-artifact-registry
storage:
  filesystem:
    rootdirectory: /var/lib/registry
  delete:
    enabled: false
http:
  addr: :5000
  headers:
    X-Content-Type-Options: [nosniff]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3
`;
}

export function composeFile(options: ArtifactRegistryOptions): string {
  return `name: ${options.composeProject}
services:
  ${options.serviceName}:
    image: ${options.image}
    container_name: ${options.containerName}
    restart: unless-stopped
    ports:
      - "${options.host}:${options.port}:5000"
    volumes:
      - "${options.baseDir}/config.yml:/etc/docker/registry/config.yml:ro"
      - "${options.storageDir}:/var/lib/registry"
    labels:
      unidesk.ai/service-id: artifact-registry
      unidesk.ai/managed-by: host-systemd-docker-compose
      unidesk.ai/provider-id: ${options.providerId}
`;
}

export function systemdUnit(options: ArtifactRegistryOptions): string {
  return `[Unit]
Description=UniDesk D601 Artifact Registry (CNCF Distribution)
Documentation=https://github.com/pikasTech/unidesk/blob/master/docs/reference/artifact-registry.md
After=network-online.target
Wants=network-online.target
ConditionPathExists=/var/run/docker.sock

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=${options.baseDir}
ExecStartPre=/bin/mkdir -p ${options.baseDir} ${options.storageDir}
ExecStartPre=/bin/sh -lc 'docker info >/dev/null'
ExecStart=/bin/sh -lc 'docker compose -p ${options.composeProject} -f ${options.baseDir}/compose.yml up -d --remove-orphans'
ExecStop=/bin/sh -lc 'docker compose -p ${options.composeProject} -f ${options.baseDir}/compose.yml down'
TimeoutStartSec=120
TimeoutStopSec=120

[Install]
WantedBy=multi-user.target
`;
}

export function renderBundle(options: ArtifactRegistryOptions): RenderedBundle {
  const paths = {
    unit: `/etc/systemd/system/${options.unitName}`,
    compose: `${options.baseDir}/compose.yml`,
    config: `${options.baseDir}/config.yml`,
    storage: options.storageDir,
    baseDir: options.baseDir,
  };
  return {
    paths,
    files: [
      file(paths.config, registryConfig()),
      file(paths.compose, composeFile(options)),
      file(paths.unit, systemdUnit(options)),
    ],
  };
}
