import {
  argoApplicationName,
  buildkitRunnerImage,
  ciToolsRunnerImage,
  defaultDevBaseImage,
  defaultRegistryPrefix,
  defaultServiceIds,
  defaultSourceRepo,
  gitopsPathFor,
  gitopsPathForProfile,
  gitopsTargetForProfile,
  isRuntimeLane,
  namespaceNameForProfile,
  primitiveValidationTasks,
  proxyEnv,
  serviceIdsForLane,
  servicesParamForLane
} from "./core.mjs";
import {
  ciTimingShellFunction,
  collectArtifactsScript,
  controlPlaneReconcileScript,
  gitopsPromoteScript,
  perServicePublishScript,
  planArtifactsScript,
  pollerScript,
  prepareSourceScript,
  primitiveValidationTask,
  primitiveValidationTaskNames
} from "./tekton-scripts.mjs";

function ciLaneSettings(argsOrLane = "node", deploy = null) {
  const lane = typeof argsOrLane === "string" ? argsOrLane : argsOrLane.lane;
  if (isRuntimeLane(lane)) {
    const namespace = namespaceNameForProfile(lane, deploy);
    return {
      lane,
      profile: lane,
      gitopsTarget: lane,
      pipelineName: `${namespace}-ci-image-publish`,
      pipelineRunPrefix: `${namespace}-ci-poll`,
      serviceAccountName: `${namespace}-tekton-runner`,
      pollerName: `${namespace}-branch-poller`,
      controlPlaneReconcilerName: `${namespace}-control-plane-reconciler`,
      tektonDir: `tekton-${lane}`,
      catalogPath: typeof argsOrLane === "object" ? argsOrLane.catalogPath : `deploy/artifact-catalog.${lane}.json`,
      imageTagMode: typeof argsOrLane === "object" ? argsOrLane.imageTagMode : "full",
      runtimeNamespace: namespace
    };
  }
  return {
    lane: "node",
    profile: "dev",
    gitopsTarget: "node",
    pipelineName: "hwlab-node-ci-image-publish",
    pipelineRunPrefix: "hwlab-node-ci-poll",
    serviceAccountName: "hwlab-tekton-runner",
    pollerName: "hwlab-node-branch-poller",
    controlPlaneReconcilerName: "hwlab-node-control-plane-reconciler",
    tektonDir: "tekton",
    catalogPath: "deploy/artifact-catalog.dev.json",
    imageTagMode: "short",
    runtimeNamespace: "hwlab-dev"
  };
}

function tektonRbac(args = { lane: "node" }, deploy = null) {
  const settings = ciLaneSettings(args, deploy);
  if (isRuntimeLane(settings.lane)) {
    const runtimeObserverName = `${settings.runtimeNamespace}-runtime-observer`;
    const pipelineRunWriterName = `${settings.runtimeNamespace}-pipelinerun-writer`;
    const argoReconcilerName = `${settings.runtimeNamespace}-argocd-reconciler`;
    return {
      apiVersion: "v1",
      kind: "List",
      items: [
        { apiVersion: "v1", kind: "Namespace", metadata: { name: "hwlab-ci" } },
        { apiVersion: "v1", kind: "ServiceAccount", metadata: { name: settings.serviceAccountName, namespace: "hwlab-ci" } },
        {
          apiVersion: "rbac.authorization.k8s.io/v1",
          kind: "Role",
          metadata: { name: runtimeObserverName, namespace: settings.runtimeNamespace },
          rules: [
            { apiGroups: [""], resources: ["pods", "services", "configmaps"], verbs: ["get", "list", "watch"] },
            { apiGroups: ["apps"], resources: ["deployments", "statefulsets"], verbs: ["get", "list", "watch"] },
            { apiGroups: ["batch"], resources: ["jobs"], verbs: ["get", "list", "watch"] }
          ]
        },
        {
          apiVersion: "rbac.authorization.k8s.io/v1",
          kind: "RoleBinding",
          metadata: { name: runtimeObserverName, namespace: settings.runtimeNamespace },
          subjects: [{ kind: "ServiceAccount", name: settings.serviceAccountName, namespace: "hwlab-ci" }],
          roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: runtimeObserverName }
        },
        {
          apiVersion: "rbac.authorization.k8s.io/v1",
          kind: "Role",
          metadata: { name: pipelineRunWriterName, namespace: "hwlab-ci" },
          rules: [
            { apiGroups: ["tekton.dev"], resources: ["pipelineruns"], verbs: ["get", "list", "create"] },
            { apiGroups: ["tekton.dev"], resources: ["pipelines", "tasks"], verbs: ["get", "patch", "create"] },
            { apiGroups: ["batch"], resources: ["cronjobs"], verbs: ["get", "patch", "create"] },
            { apiGroups: ["rbac.authorization.k8s.io"], resources: ["roles", "rolebindings"], verbs: ["get", "patch", "create"] },
            { apiGroups: [""], resources: ["serviceaccounts"], verbs: ["get", "patch", "create"] }
          ]
        },
        {
          apiVersion: "rbac.authorization.k8s.io/v1",
          kind: "RoleBinding",
          metadata: { name: pipelineRunWriterName, namespace: "hwlab-ci" },
          subjects: [{ kind: "ServiceAccount", name: settings.serviceAccountName, namespace: "hwlab-ci" }],
          roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: pipelineRunWriterName }
        },
        {
          apiVersion: "rbac.authorization.k8s.io/v1",
          kind: "Role",
          metadata: { name: argoReconcilerName, namespace: "argocd" },
          rules: [
            { apiGroups: ["argoproj.io"], resources: ["applications", "appprojects"], verbs: ["get", "patch", "create"] },
            { apiGroups: ["rbac.authorization.k8s.io"], resources: ["roles", "rolebindings"], verbs: ["get", "patch", "create"] }
          ]
        },
        {
          apiVersion: "rbac.authorization.k8s.io/v1",
          kind: "RoleBinding",
          metadata: { name: argoReconcilerName, namespace: "argocd" },
          subjects: [{ kind: "ServiceAccount", name: settings.serviceAccountName, namespace: "hwlab-ci" }],
          roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: argoReconcilerName }
        }
      ]
    };
  }
  return {
    apiVersion: "v1",
    kind: "List",
    items: [
      { apiVersion: "v1", kind: "Namespace", metadata: { name: "hwlab-ci" } },
      { apiVersion: "v1", kind: "ServiceAccount", metadata: { name: "hwlab-tekton-runner", namespace: "hwlab-ci" } },
      {
        apiVersion: "rbac.authorization.k8s.io/v1",
        kind: "Role",
        metadata: { name: "hwlab-node-control-plane-reconciler", namespace: "hwlab-dev" },
        rules: [
          { apiGroups: ["rbac.authorization.k8s.io"], resources: ["roles", "rolebindings"], verbs: ["get", "patch", "create"] }
        ]
      },
      {
        apiVersion: "rbac.authorization.k8s.io/v1",
        kind: "RoleBinding",
        metadata: { name: "hwlab-node-control-plane-reconciler", namespace: "hwlab-dev" },
        subjects: [{ kind: "ServiceAccount", name: "hwlab-tekton-runner", namespace: "hwlab-ci" }],
        roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: "hwlab-node-control-plane-reconciler" }
      },
      {
        apiVersion: "rbac.authorization.k8s.io/v1",
        kind: "Role",
        metadata: { name: "hwlab-tekton-runtime-observer", namespace: "hwlab-dev" },
        rules: [
          { apiGroups: [""], resources: ["pods", "services", "configmaps"], verbs: ["get", "list", "watch"] },
          { apiGroups: ["apps"], resources: ["deployments", "statefulsets"], verbs: ["get", "list", "watch"] },
          { apiGroups: ["batch"], resources: ["jobs"], verbs: ["get", "list", "watch"] }
        ]
      },
      {
        apiVersion: "rbac.authorization.k8s.io/v1",
        kind: "RoleBinding",
        metadata: { name: "hwlab-tekton-runtime-observer", namespace: "hwlab-dev" },
        subjects: [{ kind: "ServiceAccount", name: "hwlab-tekton-runner", namespace: "hwlab-ci" }],
        roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: "hwlab-tekton-runtime-observer" }
      },
      {
        apiVersion: "rbac.authorization.k8s.io/v1",
        kind: "Role",
        metadata: { name: "hwlab-tekton-pipelinerun-writer", namespace: "hwlab-ci" },
        rules: [
          { apiGroups: ["tekton.dev"], resources: ["pipelineruns"], verbs: ["get", "list", "create"] },
          { apiGroups: ["tekton.dev"], resources: ["pipelines", "tasks"], verbs: ["get", "patch", "create"] },
          { apiGroups: ["batch"], resources: ["cronjobs"], verbs: ["get", "patch", "create"] },
          { apiGroups: ["rbac.authorization.k8s.io"], resources: ["roles", "rolebindings"], verbs: ["get", "patch", "create"] },
          { apiGroups: [""], resources: ["serviceaccounts"], verbs: ["get", "patch", "create"] }
        ]
      },
      {
        apiVersion: "rbac.authorization.k8s.io/v1",
        kind: "RoleBinding",
        metadata: { name: "hwlab-tekton-pipelinerun-writer", namespace: "hwlab-ci" },
        subjects: [{ kind: "ServiceAccount", name: "hwlab-tekton-runner", namespace: "hwlab-ci" }],
        roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: "hwlab-tekton-pipelinerun-writer" }
      }
    ]
  };
}

function buildTaskName(serviceId) {
  return `build-${serviceId}`;
}

function affectedResultName(serviceId) {
  return `affected-${serviceId}`;
}

function buildResultName(serviceId) {
  return `build-${serviceId}`;
}

const serviceResultFields = Object.freeze([
  "status",
  "service-id",
  "image",
  "image-tag",
  "digest",
  "repository-digest",
  "source-commit-id",
  "component-input-hash",
  "environment-input-hash",
  "code-input-hash",
  "runtime-mode",
  "boot-repo",
  "boot-commit",
  "boot-sh",
  "build-created-at",
  "build-backend",
  "reused-from",
  "go-base-image",
  "go-base-image-status",
  "go-base-digest",
  "go-base-build-duration-ms",
  "go-base-buildkit-cache-ref"
]);

function serviceResultParamName(serviceId, field) {
  return `${serviceId}-${field}`;
}

function serviceResultEnvName(serviceId, field) {
  return `HWLAB_SERVICE_RESULT_${serviceId.toUpperCase().replace(/[^A-Z0-9]+/gu, "_")}_${field.toUpperCase().replace(/[^A-Z0-9]+/gu, "_")}`;
}

function serviceResultParams(serviceIds = defaultServiceIds) {
  return serviceIds.flatMap((serviceId) => serviceResultFields.map((field) => ({ name: serviceResultParamName(serviceId, field), default: "" })));
}

function serviceResultParamValues(serviceIds = defaultServiceIds) {
  return serviceIds.flatMap((serviceId) => serviceResultFields.map((field) => ({
    name: serviceResultParamName(serviceId, field),
    value: `$(tasks.${buildTaskName(serviceId)}.results.${field})`
  })));
}

function serviceResultEnv(serviceIds = defaultServiceIds) {
  return serviceIds.flatMap((serviceId) => serviceResultFields.map((field) => ({
    name: serviceResultEnvName(serviceId, field),
    value: `$(params.${serviceResultParamName(serviceId, field)})`
  })));
}

function serviceWorkVolume() {
  return { name: "service-work", emptyDir: {} };
}

function buildkitBinVolume() {
  return { name: "buildkit-bin", emptyDir: {} };
}

function buildkitRunVolume() {
  return { name: "buildkit-run", emptyDir: {} };
}

function prepareBuildkitClientStep() {
  return {
    name: "prepare-buildkit-client",
    image: buildkitRunnerImage,
    securityContext: { runAsUser: 0, runAsGroup: 0 },
    volumeMounts: [{ name: "buildkit-bin", mountPath: "/workspace/buildkit-bin" }],
    script: `#!/bin/sh
set -eu
mkdir -p /workspace/buildkit-bin
cp /usr/bin/buildctl /workspace/buildkit-bin/
chmod +x /workspace/buildkit-bin/*
`
  };
}

function buildkitSidecar() {
  return {
    name: "buildkitd",
    image: buildkitRunnerImage,
    args: ["--addr", "unix:///workspace/buildkit-run/buildkitd.sock", "--oci-worker-no-process-sandbox"],
    env: proxyEnv(),
    volumeMounts: [{ name: "buildkit-run", mountPath: "/workspace/buildkit-run" }],
    securityContext: {
      runAsUser: 1000,
      runAsGroup: 1000,
      allowPrivilegeEscalation: true,
      appArmorProfile: { type: "Unconfined" },
      seccompProfile: { type: "Unconfined" }
    }
  };
}

function planArtifactsTask({ serviceIds = defaultServiceIds } = {}) {
  return {
    name: "plan-artifacts",
    runAfter: primitiveValidationTaskNames(),
    workspaces: [{ name: "source", workspace: "source" }],
    taskSpec: {
      params: [
        { name: "git-url" },
        { name: "git-read-url" },
        { name: "gitops-branch" },
        { name: "lane" },
        { name: "revision" },
        { name: "catalog-path" },
        { name: "registry-prefix" },
        { name: "services" }
      ],
      results: serviceIds.flatMap((serviceId) => [
        { name: affectedResultName(serviceId), description: `${serviceId} rollout affected according to ci-plan` },
        { name: buildResultName(serviceId), description: `${serviceId} image build required according to ci-plan` }
      ]),
      workspaces: [{ name: "source" }],
      steps: [{
        name: "plan",
        image: ciToolsRunnerImage,
        env: [
          { name: "HWLAB_SELECTED_SERVICES", value: "$(params.services)" },
          { name: "HWLAB_DEV_REGISTRY_K8S_NATIVE", value: "1" },
          { name: "HWLAB_TEKTON_PIPELINERUN", value: "$(context.pipelineRun.name)" },
          ...proxyEnv()
        ],
        script: planArtifactsScript()
      }]
    },
    params: [
      { name: "git-url", value: "$(params.git-url)" },
      { name: "git-read-url", value: "$(params.git-read-url)" },
      { name: "gitops-branch", value: "$(params.gitops-branch)" },
      { name: "lane", value: "$(params.lane)" },
      { name: "revision", value: "$(params.revision)" },
      { name: "catalog-path", value: "$(params.catalog-path)" },
      { name: "registry-prefix", value: "$(params.registry-prefix)" },
      { name: "services", value: "$(params.services)" }
    ]
  };
}

function perServiceBuildTask(serviceId, { runAfter = ["plan-artifacts"] } = {}) {
  return {
    name: buildTaskName(serviceId),
    runAfter,
    workspaces: [{ name: "source", workspace: "source" }],
    when: [{ input: `$(tasks.plan-artifacts.results.${buildResultName(serviceId)})`, operator: "in", values: ["true"] }],
    taskSpec: {
      params: [
        { name: "revision" },
        { name: "lane" },
        { name: "catalog-path" },
        { name: "image-tag-mode" },
        { name: "registry-prefix" },
        { name: "service-id" },
        { name: "base-image" },
        { name: "build-cache-mode" }
      ],
      results: serviceResultFields.map((field) => ({ name: field, description: `${serviceId} ${field}` })),
      workspaces: [{ name: "source" }],
      volumes: [serviceWorkVolume(), buildkitBinVolume(), buildkitRunVolume()],
      sidecars: [buildkitSidecar()],
      steps: [
        prepareBuildkitClientStep(),
        {
          name: "publish",
          image: ciToolsRunnerImage,
          securityContext: { runAsUser: 1000, runAsGroup: 1000 },
          env: proxyEnv(),
          volumeMounts: [{ name: "service-work", mountPath: "/workspace/service-work" }, { name: "buildkit-bin", mountPath: "/workspace/buildkit-bin" }, { name: "buildkit-run", mountPath: "/workspace/buildkit-run" }],
          script: perServicePublishScript()
        }
      ]
    },
    params: [
      { name: "revision", value: "$(params.revision)" },
      { name: "lane", value: "$(params.lane)" },
      { name: "catalog-path", value: "$(params.catalog-path)" },
      { name: "image-tag-mode", value: "$(params.image-tag-mode)" },
      { name: "registry-prefix", value: "$(params.registry-prefix)" },
      { name: "service-id", value: serviceId },
      { name: "base-image", value: "$(params.base-image)" },
      { name: "build-cache-mode", value: "$(params.build-cache-mode)" }
    ]
  };
}

function perServiceBuildMatrixTask(serviceIds, { runAfter = ["plan-artifacts"] } = {}) {
  const task = perServiceBuildTask(serviceIds[0], { runAfter });
  task.name = "build-services";
  delete task.when;
  task.params = task.params.filter((param) => param.name !== "service-id");
  task.matrix = { params: [{ name: "service-id", value: serviceIds }] };
  return task;
}

function collectArtifactsTask({ serviceIds = defaultServiceIds } = {}) {
  return {
    name: "collect-artifacts",
    runAfter: ["build-services"],
    workspaces: [{ name: "source", workspace: "source" }],
    taskSpec: {
      params: [
        { name: "revision" },
        { name: "lane" },
        { name: "catalog-path" },
        { name: "image-tag-mode" },
        { name: "registry-prefix" },
        { name: "services" },
        { name: "base-image" }
      ],
      workspaces: [{ name: "source" }],
      steps: [{ name: "collect", image: ciToolsRunnerImage, env: proxyEnv(), script: collectArtifactsScript() }]
    },
    params: [
      { name: "revision", value: "$(params.revision)" },
      { name: "lane", value: "$(params.lane)" },
      { name: "catalog-path", value: "$(params.catalog-path)" },
      { name: "image-tag-mode", value: "$(params.image-tag-mode)" },
      { name: "registry-prefix", value: "$(params.registry-prefix)" },
      { name: "services", value: "$(params.services)" },
      { name: "base-image", value: "$(params.base-image)" }
    ]
  };
}

function runtimeReadyScript() {
  return `#!/bin/sh
set -eu
${ciTimingShellFunction()}
export HWLAB_TEKTON_PIPELINERUN="$(context.pipelineRun.name)"
export HWLAB_TEKTON_TASKRUN="$(context.taskRun.name)"
export HWLAB_TEKTON_TASK="runtime-ready"
export HWLAB_SOURCE_REVISION="$(params.revision)"
export HWLAB_RUNTIME_READY_TIMEOUT_MS="$(params.timeout-ms)"
cd /workspace/source/repo
node scripts/ci/runtime-ready.mjs
`;
}

function runtimeReadyTask({ profile = "dev", deploy = null } = {}) {
  return {
    name: "runtime-ready",
    runAfter: ["gitops-promote"],
    workspaces: [{ name: "source", workspace: "source" }],
    taskSpec: {
      params: [{ name: "revision" }, { name: "runtime-namespace" }, { name: "gitops-target" }, { name: "timeout-ms" }],
      workspaces: [{ name: "source" }],
      steps: [{
        name: "runtime-ready",
        image: ciToolsRunnerImage,
        env: [
          ...proxyEnv(),
          { name: "HWLAB_RUNTIME_NAMESPACE", value: "$(params.runtime-namespace)" },
          { name: "HWLAB_GITOPS_TARGET", value: "$(params.gitops-target)" }
        ],
        script: runtimeReadyScript()
      }]
    },
    params: [
      { name: "revision", value: "$(params.revision)" },
      { name: "runtime-namespace", value: namespaceNameForProfile(profile, deploy) },
      { name: "gitops-target", value: gitopsTargetForProfile(profile) },
      { name: "timeout-ms", value: "$(params.runtime-ready-timeout-ms)" }
    ]
  };
}

function imagePublishTaskSet(args = { lane: "node" }, deploy = null) {
  const serviceIds = serviceIdsForLane(args.lane, deploy);
  const buildTask = perServiceBuildMatrixTask(serviceIds, { runAfter: ["plan-artifacts"] });
  return [
    planArtifactsTask({ serviceIds }),
    buildTask,
    collectArtifactsTask({ serviceIds })
  ];
}

function taskResourceName(pipelineName, taskName) {
  return `${pipelineName}-${taskName}`.replace(/[^a-z0-9-]/gu, "-").slice(0, 63).replace(/-+$/u, "");
}

function reusableTaskResource(pipelineName, task) {
  return {
    apiVersion: "tekton.dev/v1",
    kind: "Task",
    metadata: {
      name: taskResourceName(pipelineName, task.name),
      namespace: "hwlab-ci",
      labels: {
        "app.kubernetes.io/part-of": "hwlab",
        "hwlab.pikastech.local/tekton-pipeline": pipelineName
      }
    },
    spec: task.taskSpec
  };
}

function tektonTasks(args = { lane: "node" }, deploy = null) {
  const settings = ciLaneSettings(args, deploy);
  const prepareSource = {
    name: "prepare-source",
    taskSpec: {
      params: [
        { name: "git-url" }, { name: "git-read-url" }, { name: "source-branch" },
        { name: "gitops-branch" }, { name: "lane" }, { name: "catalog-path" },
        { name: "image-tag-mode" }, { name: "registry-prefix" }, { name: "services" }, { name: "revision" }
      ],
      workspaces: [{ name: "source" }, { name: "git-ssh" }],
      steps: [{ name: "prepare-source", image: ciToolsRunnerImage, env: proxyEnv(), script: prepareSourceScript() }]
    }
  };
  const pipelineTasks = [
    prepareSource,
    ...primitiveValidationTasks.map(primitiveValidationTask),
    ...imagePublishTaskSet(args, deploy),
    {
      name: "gitops-promote",
      taskSpec: {
        params: [
          { name: "git-url" }, { name: "git-read-url" }, { name: "git-write-url" },
          { name: "source-branch" }, { name: "gitops-branch" }, { name: "lane" },
          { name: "catalog-path" }, { name: "image-tag-mode" }, { name: "runtime-path" },
          { name: "revision" }, { name: "registry-prefix" }
        ],
        results: [{ name: "runtime-ready-required", description: "true when GitOps promotion changed runtime desired state and runtime readiness must be observed" }],
        workspaces: [{ name: "source" }, { name: "git-ssh" }],
        steps: [{ name: "promote", image: ciToolsRunnerImage, env: proxyEnv(), script: gitopsPromoteScript() }]
      }
    },
    ...(isRuntimeLane(settings.lane) ? [] : [{ name: "runtime-ready", taskSpec: runtimeReadyTask({ profile: settings.profile }).taskSpec }])
  ];
  return pipelineTasks.map((task) => reusableTaskResource(settings.pipelineName, task));
}

function tektonPipeline(args = { lane: "node" }, deploy = null) {
  const settings = ciLaneSettings(args, deploy);
  const runtimePath = gitopsPathForProfile(args, settings.profile, deploy);
  const preFlushRuntimeReadyTasks = isRuntimeLane(settings.lane)
    ? []
    : [{
        ...runtimeReadyTask({ profile: settings.profile, deploy }),
        when: [{ input: "$(tasks.gitops-promote.results.runtime-ready-required)", operator: "in", values: ["true"] }]
      }];
  return {
    apiVersion: "tekton.dev/v1",
    kind: "Pipeline",
    metadata: {
      name: settings.pipelineName,
      namespace: "hwlab-ci",
      labels: {
        "app.kubernetes.io/part-of": "hwlab",
        "hwlab.pikastech.local/gitops-target": settings.gitopsTarget
      },
      annotations: {
        "hwlab.pikastech.local/source-config": "scripts/gitops-render.mjs#tekton-native-primitive-ci",
        "hwlab.pikastech.local/ci-contract": "tekton-native-primitive-tasks",
        "hwlab.pikastech.local/policy": "native-per-service-taskrun-image-publish"
      }
    },
    spec: {
      params: [
        { name: "git-url", type: "string", default: defaultSourceRepo },
        { name: "git-read-url", type: "string", default: args.gitReadUrl },
        { name: "git-write-url", type: "string", default: args.gitWriteUrl },
        { name: "source-branch", type: "string", default: args.sourceBranch },
        { name: "gitops-branch", type: "string", default: args.gitopsBranch },
        { name: "lane", type: "string", default: settings.lane },
        { name: "catalog-path", type: "string", default: args.catalogPath || settings.catalogPath },
        { name: "image-tag-mode", type: "string", default: args.imageTagMode || settings.imageTagMode },
        { name: "runtime-path", type: "string", default: runtimePath },
        { name: "revision", type: "string", description: "Full git commit SHA; the only source truth for image tags." },
        { name: "registry-prefix", type: "string", default: defaultRegistryPrefix },
        { name: "services", type: "string", default: servicesParamForLane(args.lane, deploy) },
        { name: "base-image", type: "string", default: defaultDevBaseImage },
        { name: "build-cache-mode", type: "string", default: "registry" },
        { name: "runtime-ready-timeout-ms", type: "string", default: "60000" }
      ],
      workspaces: [
        { name: "source" },
        { name: "git-ssh" }
      ],
      tasks: [
        {
          name: "prepare-source",
          workspaces: [
            { name: "source", workspace: "source" },
            { name: "git-ssh", workspace: "git-ssh" }
          ],
          taskSpec: {
            params: [
              { name: "git-url" },
              { name: "git-read-url" },
              { name: "source-branch" },
              { name: "gitops-branch" },
              { name: "lane" },
              { name: "catalog-path" },
              { name: "image-tag-mode" },
              { name: "registry-prefix" },
              { name: "services" },
              { name: "revision" }
            ],
            workspaces: [{ name: "source" }, { name: "git-ssh" }],
            steps: [{ name: "prepare-source", image: ciToolsRunnerImage, env: proxyEnv(), script: prepareSourceScript() }]
          },
          params: [
            { name: "git-url", value: "$(params.git-url)" },
            { name: "git-read-url", value: "$(params.git-read-url)" },
            { name: "source-branch", value: "$(params.source-branch)" },
            { name: "gitops-branch", value: "$(params.gitops-branch)" },
            { name: "lane", value: "$(params.lane)" },
            { name: "catalog-path", value: "$(params.catalog-path)" },
            { name: "image-tag-mode", value: "$(params.image-tag-mode)" },
            { name: "registry-prefix", value: "$(params.registry-prefix)" },
            { name: "services", value: "$(params.services)" },
            { name: "revision", value: "$(params.revision)" }
          ]
        },
        ...primitiveValidationTasks.map(primitiveValidationTask),
        ...imagePublishTaskSet(args, deploy),
        {
          name: "gitops-promote",
          runAfter: ["collect-artifacts"],
          workspaces: [
            { name: "source", workspace: "source" },
            { name: "git-ssh", workspace: "git-ssh" }
          ],
          taskSpec: {
            params: [
              { name: "git-url" },
              { name: "git-read-url" },
              { name: "git-write-url" },
              { name: "source-branch" },
              { name: "gitops-branch" },
              { name: "lane" },
              { name: "catalog-path" },
              { name: "image-tag-mode" },
              { name: "runtime-path" },
              { name: "revision" },
              { name: "registry-prefix" }
            ],
            results: [{ name: "runtime-ready-required", description: "true when GitOps promotion changed runtime desired state and runtime readiness must be observed" }],
            workspaces: [{ name: "source" }, { name: "git-ssh" }],
            steps: [{ name: "promote", image: ciToolsRunnerImage, env: proxyEnv(), script: gitopsPromoteScript() }]
          },
          params: [
            { name: "git-url", value: "$(params.git-url)" },
            { name: "git-read-url", value: "$(params.git-read-url)" },
            { name: "git-write-url", value: "$(params.git-write-url)" },
            { name: "source-branch", value: "$(params.source-branch)" },
            { name: "gitops-branch", value: "$(params.gitops-branch)" },
            { name: "lane", value: "$(params.lane)" },
            { name: "catalog-path", value: "$(params.catalog-path)" },
            { name: "image-tag-mode", value: "$(params.image-tag-mode)" },
            { name: "runtime-path", value: "$(params.runtime-path)" },
            { name: "revision", value: "$(params.revision)" },
            { name: "registry-prefix", value: "$(params.registry-prefix)" }
          ]
        },
        ...preFlushRuntimeReadyTasks
      ]
    }
  };
}

function tektonPipelineRunTemplate({ source, args, deploy = null }) {
  const settings = ciLaneSettings(args, deploy);
  const runtimePath = gitopsPathForProfile(args, settings.profile, deploy);
  return {
    apiVersion: "tekton.dev/v1",
    kind: "PipelineRun",
    metadata: {
      generateName: `${settings.pipelineRunPrefix}-manual-`,
      namespace: "hwlab-ci",
      labels: {
        "app.kubernetes.io/part-of": "hwlab",
        "hwlab.pikastech.local/gitops-target": settings.gitopsTarget,
        "hwlab.pikastech.local/source-commit": source.full
      }
    },
    spec: {
      pipelineRef: { name: settings.pipelineName },
      taskRunTemplate: {
        serviceAccountName: settings.serviceAccountName,
        podTemplate: {
          hostNetwork: true,
          dnsPolicy: "ClusterFirstWithHostNet",
          securityContext: { fsGroup: 1000 }
        }
      },
      params: [
        { name: "git-url", value: args.sourceRepo },
        { name: "git-read-url", value: args.gitReadUrl },
        { name: "git-write-url", value: args.gitWriteUrl },
        { name: "source-branch", value: args.sourceBranch },
        { name: "gitops-branch", value: args.gitopsBranch },
        { name: "lane", value: settings.lane },
        { name: "catalog-path", value: args.catalogPath || settings.catalogPath },
        { name: "image-tag-mode", value: args.imageTagMode || settings.imageTagMode },
        { name: "runtime-path", value: runtimePath },
        { name: "revision", value: source.full },
        { name: "registry-prefix", value: args.registryPrefix },
        { name: "base-image", value: defaultDevBaseImage },
        { name: "build-cache-mode", value: "registry" }
      ],
      workspaces: [
        { name: "source", volumeClaimTemplate: { spec: { accessModes: ["ReadWriteOnce"], resources: { requests: { storage: "8Gi" } } } } },
        { name: "git-ssh", secret: { secretName: "hwlab-git-ssh" } }
      ]
    }
  };
}

function tektonPollerCronJob(args, deploy) {
  const settings = ciLaneSettings(args, deploy);
  if (settings.lane === "v02") throw new Error("v02 CI/CD is manually triggered by UniDesk CLI and must not render a branch poller CronJob");
  return {
    apiVersion: "batch/v1",
    kind: "CronJob",
    metadata: {
      name: settings.pollerName,
      namespace: "hwlab-ci",
      labels: {
        "app.kubernetes.io/part-of": "hwlab",
        "hwlab.pikastech.local/gitops-target": settings.gitopsTarget
      },
      annotations: {
        "hwlab.pikastech.local/source-branch": args.sourceBranch,
        "hwlab.pikastech.local/gitops-branch": args.gitopsBranch
      }
    },
    spec: {
      schedule: settings.lane === "v02" ? "*/10 * * * *" : "* * * * *",
      concurrencyPolicy: "Forbid",
      successfulJobsHistoryLimit: 3,
      failedJobsHistoryLimit: 3,
      jobTemplate: {
        spec: {
          backoffLimit: 1,
          template: {
            metadata: {
              labels: {
                "app.kubernetes.io/part-of": "hwlab",
                "hwlab.pikastech.local/gitops-target": settings.gitopsTarget
              }
            },
            spec: {
              serviceAccountName: settings.serviceAccountName,
              restartPolicy: "Never",
              hostNetwork: true,
              dnsPolicy: "ClusterFirstWithHostNet",
              containers: [{
                name: "poll",
                image: ciToolsRunnerImage,
                imagePullPolicy: "IfNotPresent",
                env: [
                  ...proxyEnv(),
                  { name: "POD_NAMESPACE", valueFrom: { fieldRef: { fieldPath: "metadata.namespace" } } },
                  { name: "PIPELINE_NAME", value: settings.pipelineName },
                  { name: "PIPELINERUN_PREFIX", value: settings.pipelineRunPrefix },
                  { name: "SERVICE_ACCOUNT_NAME", value: settings.serviceAccountName },
                  { name: "GITOPS_TARGET", value: settings.gitopsTarget },
                  { name: "LANE", value: settings.lane },
                  { name: "CATALOG_PATH", value: args.catalogPath || settings.catalogPath },
                  { name: "IMAGE_TAG_MODE", value: args.imageTagMode || settings.imageTagMode },
                  { name: "RUNTIME_PATH", value: gitopsPathForProfile(args, settings.profile, deploy) },
                  { name: "GIT_URL", value: args.sourceRepo },
                  { name: "GIT_READ_URL", value: args.gitReadUrl },
                  { name: "SOURCE_BRANCH", value: args.sourceBranch },
                  { name: "GITOPS_BRANCH", value: args.gitopsBranch },
                  { name: "REGISTRY_PREFIX", value: args.registryPrefix },
                  { name: "SERVICES", value: servicesParamForLane(args.lane, deploy) },
                  { name: "BASE_IMAGE", value: defaultDevBaseImage }
                ],
                volumeMounts: [{ name: "git-ssh", mountPath: "/workspace/git-ssh", readOnly: true }],
                command: ["/bin/sh", "-c"],
                args: [pollerScript()]
              }],
              volumes: [{ name: "git-ssh", secret: { secretName: "hwlab-git-ssh" } }]
            }
          }
        }
      }
    }
  };
}

function tektonControlPlaneReconcilerCronJob(args) {
  const settings = ciLaneSettings(args);
  if (settings.lane === "v02") throw new Error("v02 CI/CD is manually triggered by UniDesk CLI and must not render a control-plane reconciler CronJob");
  return {
    apiVersion: "batch/v1",
    kind: "CronJob",
    metadata: {
      name: settings.controlPlaneReconcilerName,
      namespace: "hwlab-ci",
      labels: {
        "app.kubernetes.io/part-of": "hwlab",
        "hwlab.pikastech.local/gitops-target": settings.gitopsTarget
      },
      annotations: {
        "hwlab.pikastech.local/source-branch": args.sourceBranch,
        "hwlab.pikastech.local/gitops-branch": args.gitopsBranch,
        "hwlab.pikastech.local/purpose": "render-and-apply-node-ci-control-plane"
      }
    },
    spec: {
      schedule: "*/10 * * * *",
      concurrencyPolicy: "Forbid",
      successfulJobsHistoryLimit: 3,
      failedJobsHistoryLimit: 3,
      jobTemplate: {
        spec: {
          backoffLimit: 1,
          template: {
            metadata: {
              labels: {
                "app.kubernetes.io/part-of": "hwlab",
                "hwlab.pikastech.local/gitops-target": settings.gitopsTarget
              }
            },
            spec: {
              serviceAccountName: settings.serviceAccountName,
              restartPolicy: "Never",
              hostNetwork: true,
              dnsPolicy: "ClusterFirstWithHostNet",
              containers: [{
                name: "reconcile",
                image: ciToolsRunnerImage,
                imagePullPolicy: "IfNotPresent",
                env: [
                  ...proxyEnv(),
                  { name: "POD_NAMESPACE", valueFrom: { fieldRef: { fieldPath: "metadata.namespace" } } },
                  { name: "LANE", value: settings.lane },
                  { name: "CATALOG_PATH", value: args.catalogPath || settings.catalogPath },
                  { name: "IMAGE_TAG_MODE", value: args.imageTagMode || settings.imageTagMode },
                  { name: "TEKTON_DIR", value: settings.tektonDir },
                  { name: "ARGO_FILES", value: settings.lane === "v02" ? `${gitopsPathFor(args, "argocd/project.yaml")},${gitopsPathFor(args, "argocd/application-v02.yaml")}` : "" },
                  { name: "FIELD_MANAGER", value: settings.controlPlaneReconcilerName },
                  { name: "RECONCILE_EVENT", value: `${settings.gitopsTarget}-control-plane-reconciled` },
                  { name: "GIT_URL", value: args.sourceRepo },
                  { name: "GIT_READ_URL", value: args.gitReadUrl },
                  { name: "SOURCE_BRANCH", value: args.sourceBranch },
                  { name: "GITOPS_BRANCH", value: args.gitopsBranch },
                  { name: "REGISTRY_PREFIX", value: args.registryPrefix }
                ],
                volumeMounts: [{ name: "git-ssh", mountPath: "/workspace/git-ssh", readOnly: true }],
                command: ["/bin/sh", "-c"],
                args: [controlPlaneReconcileScript()]
              }],
              volumes: [{ name: "git-ssh", secret: { secretName: "hwlab-git-ssh" } }]
            }
          }
        }
      }
    }
  };
}

export {
  affectedResultName,
  buildResultName,
  buildTaskName,
  buildkitBinVolume,
  buildkitRunVolume,
  buildkitSidecar,
  ciLaneSettings,
  collectArtifactsTask,
  imagePublishTaskSet,
  perServiceBuildTask,
  planArtifactsTask,
  prepareBuildkitClientStep,
  runtimeReadyScript,
  runtimeReadyTask,
  serviceResultEnv,
  serviceResultEnvName,
  serviceResultParamName,
  serviceResultParams,
  serviceResultParamValues,
  serviceWorkVolume,
  tektonControlPlaneReconcilerCronJob,
  tektonPipeline,
  tektonTasks,
  tektonPipelineRunTemplate,
  tektonPollerCronJob,
  tektonRbac
};
