import { stableJsonSha256 } from "./stable-json";
import {
  readStaticSiteConfig,
  resolveStaticSiteTarget,
  staticSiteConfigLabel,
  type StaticSiteConfig,
  type StaticSiteTarget,
} from "./platform-infra-static-site-config";
import type { PacSourceArtifactBinding, PacSourceArtifactMode, PacSourceArtifactRenderer } from "./platform-infra-pipelines-as-code-source-artifact";

export const staticSiteImagePlaceholder = "__HWPOD_HOME_IMAGE_REF__";
export const staticSiteSourceCommitPlaceholder = "__HWPOD_HOME_SOURCE_COMMIT__";

export interface StaticSitePipelineProvenance {
  readonly configRef: string;
  readonly effectiveConfigSha256: string;
  readonly renderer: PacSourceArtifactRenderer;
  readonly mode: PacSourceArtifactMode;
}

export function staticSiteOwningSourceRemote(): string {
  return readStaticSiteConfig().application.remote;
}

export function renderStaticSiteDesiredPipeline(binding: PacSourceArtifactBinding): {
  pipeline: Record<string, unknown>;
  provenance: StaticSitePipelineProvenance;
} {
  const config = readStaticSiteConfig();
  const target = resolveStaticSiteTarget(config, binding.consumer.node);
  const expectedConfigRef = `${staticSiteConfigLabel}#delivery`;
  if (binding.consumer.sourceArtifact.configRef !== expectedConfigRef) throw new Error(`static-site sourceArtifact.configRef must equal ${expectedConfigRef}`);
  const manifest = renderStaticSiteManifest(config, target, staticSiteImagePlaceholder, staticSiteSourceCommitPlaceholder);
  return {
    pipeline: renderPipeline(config, target, binding, manifest),
    provenance: {
      configRef: expectedConfigRef,
      effectiveConfigSha256: stableJsonSha256({ application: config.application, image: config.image, delivery: config.delivery, target, runtime: config.runtime }),
      renderer: "static-site-runtime",
      mode: "embedded-pipeline-spec",
    },
  };
}

export function renderStaticSiteManifest(config: StaticSiteConfig, target: StaticSiteTarget, imageRef: string, sourceCommit: string): string {
  const labels = {
    "app.kubernetes.io/name": config.runtime.deploymentName,
    "app.kubernetes.io/part-of": "hwpod-home",
    "app.kubernetes.io/managed-by": "unidesk",
  };
  const podLabels = { ...labels, "app.kubernetes.io/component": "website" };
  const probe = {
    httpGet: { path: config.runtime.probes.healthPath, port: "http" },
    initialDelaySeconds: config.runtime.probes.initialDelaySeconds,
    periodSeconds: config.runtime.probes.periodSeconds,
    timeoutSeconds: config.runtime.probes.timeoutSeconds,
    failureThreshold: config.runtime.probes.failureThreshold,
  };
  const objects: Record<string, unknown>[] = [
    { apiVersion: "v1", kind: "Namespace", metadata: { name: target.namespace, labels } },
    {
      apiVersion: "v1",
      kind: "Service",
      metadata: { name: config.runtime.serviceName, namespace: target.namespace, labels },
      spec: {
        type: "NodePort",
        selector: { "app.kubernetes.io/name": config.runtime.deploymentName },
        ports: [{ name: "http", port: config.runtime.servicePort, targetPort: "http", nodePort: config.runtime.nodePort }],
      },
    },
    {
      apiVersion: "apps/v1",
      kind: "Deployment",
      metadata: { name: config.runtime.deploymentName, namespace: target.namespace, labels },
      spec: {
        replicas: target.replicas,
        strategy: { type: "RollingUpdate", rollingUpdate: { maxUnavailable: 0, maxSurge: 1 } },
        selector: { matchLabels: { "app.kubernetes.io/name": config.runtime.deploymentName } },
        template: {
          metadata: {
            labels: podLabels,
            annotations: {
              "unidesk.ai/source-commit": sourceCommit,
              "unidesk.ai/canonical-origin": `https://${target.publicExposures.apex.hostname}`,
            },
          },
          spec: {
            automountServiceAccountToken: false,
            securityContext: { runAsNonRoot: true, runAsUser: 101, runAsGroup: 101, fsGroup: 101, seccompProfile: { type: "RuntimeDefault" } },
            terminationGracePeriodSeconds: 10,
            containers: [{
              name: "site",
              image: imageRef,
              imagePullPolicy: config.image.pullPolicy,
              ports: [{ name: "http", containerPort: config.runtime.containerPort, protocol: "TCP" }],
              readinessProbe: probe,
              livenessProbe: probe,
              resources: config.runtime.resources,
              securityContext: { allowPrivilegeEscalation: false, readOnlyRootFilesystem: true, capabilities: { drop: ["ALL"] } },
              volumeMounts: [{ name: "tmp", mountPath: "/tmp" }],
            }],
            volumes: [{ name: "tmp", emptyDir: { sizeLimit: "32Mi" } }],
          },
        },
      },
    },
  ];
  return `${objects.map((item) => Bun.YAML.stringify(item).trim()).join("\n---\n")}\n`;
}

function renderPipeline(config: StaticSiteConfig, target: StaticSiteTarget, binding: PacSourceArtifactBinding, manifestTemplate: string): Record<string, unknown> {
  const delivery = config.delivery;
  const params = [
    { name: "git-read-url", type: "string", default: binding.repository.cloneUrl },
    { name: "source-branch", type: "string", default: config.application.branch },
    { name: "revision", type: "string" },
    { name: "source-stage-ref", type: "string" },
    { name: "dockerfile", type: "string", default: config.application.dockerfile },
    { name: "image-repository", type: "string", default: config.image.repository },
    { name: "tools-image", type: "string", default: delivery.pipeline.toolsImage },
    { name: "buildkit-image", type: "string", default: delivery.pipeline.buildkitImage },
    { name: "build-network", type: "string", default: delivery.build.networkMode },
    { name: "build-http-proxy", type: "string", default: delivery.build.proxy.http },
    { name: "build-https-proxy", type: "string", default: delivery.build.proxy.https },
    { name: "build-all-proxy", type: "string", default: delivery.build.proxy.all },
    { name: "build-no-proxy", type: "string", default: delivery.build.proxy.noProxy.join(",") },
    { name: "gitops-read-url", type: "string", default: delivery.gitops.readUrl },
    { name: "gitops-write-url", type: "string", default: delivery.gitops.writeUrl },
    { name: "gitops-branch", type: "string", default: delivery.gitops.branch },
    { name: "gitops-manifest-path", type: "string", default: delivery.gitops.manifestPath },
    { name: "gitops-release-state-path", type: "string", default: delivery.gitops.releaseStatePath },
    { name: "gitops-max-push-attempts", type: "string", default: String(delivery.gitops.maxPushAttempts) },
    { name: "gitops-author-name", type: "string", default: delivery.gitops.author.name },
    { name: "gitops-author-email", type: "string", default: delivery.gitops.author.email },
    { name: "manifest-template-b64", type: "string", default: Buffer.from(manifestTemplate, "utf8").toString("base64") },
  ];
  const task = (name: string, steps: readonly Record<string, unknown>[], runAfter: readonly string[] = []): Record<string, unknown> => ({
    name,
    ...(runAfter.length === 0 ? {} : { runAfter }),
    workspaces: [{ name: "source", workspace: "source" }],
    taskSpec: { params: params.map(({ name: paramName }) => ({ name: paramName })), workspaces: [{ name: "source" }], steps },
    params: params.map(({ name: paramName }) => ({ name: paramName, value: `$(params.${paramName})` })),
  });
  return {
    apiVersion: "tekton.dev/v1",
    kind: "Pipeline",
    metadata: {
      name: binding.consumer.pipeline,
      namespace: binding.consumer.namespace,
      labels: { "app.kubernetes.io/name": "hwpod-home", "app.kubernetes.io/part-of": "platform-infra", "app.kubernetes.io/managed-by": "unidesk", "unidesk.ai/node": target.id },
    },
    spec: {
      params,
      workspaces: [{ name: "source" }],
      tasks: [
        task("source-validate", [{
          name: "checkout-and-site-check",
          image: "$(params.tools-image)",
          imagePullPolicy: "IfNotPresent",
          env: [
            ...proxyEnv(),
            { name: "GITEA_TOKEN", valueFrom: { secretKeyRef: { name: binding.repository.secretName, key: binding.repository.tokenKey } } },
          ],
          script: sourceValidateScript(),
        }]),
        task("image-build", [{
          name: "build-and-push",
          image: "$(params.buildkit-image)",
          imagePullPolicy: "IfNotPresent",
          env: [
            ...proxyEnv(),
            { name: "BUILDKITD_FLAGS", value: "--oci-worker-no-process-sandbox --oci-worker-net=host --allow-insecure-entitlement network.host" },
            { name: "SOURCE_ROOT", value: "$(workspaces.source.path)/repo" },
            { name: "SOURCE_COMMIT", value: "$(params.revision)" },
            { name: "IMAGE_REPOSITORY", value: "$(params.image-repository)" },
            { name: "DOCKERFILE", value: "$(params.dockerfile)" },
            { name: "BUILD_NETWORK", value: "$(params.build-network)" },
            { name: "BUILD_METADATA_FILE", value: "$(workspaces.source.path)/build-metadata.json" },
            { name: "BUILD_RESULT_FILE", value: "$(workspaces.source.path)/build-result.json" },
          ],
          securityContext: { privileged: true, runAsUser: 1000, runAsGroup: 1000 },
          script: "#!/bin/sh\nset -eu\nexec \"$(workspaces.source.path)/repo/scripts/ci/build-image.sh\"",
        }], ["source-validate"]),
        task("gitops-publish", [{ name: "publish-digest-manifest", image: "$(params.tools-image)", imagePullPolicy: "IfNotPresent", env: proxyEnv(), script: gitopsPublishScript() }], ["image-build"]),
      ],
    },
  };
}

function sourceValidateScript(): string {
  return [
    "#!/bin/sh",
    "set -eu",
    "root=\"$(workspaces.source.path)\"",
    "rm -rf \"$root/repo\"",
    "askpass=/tmp/hwpod-home-source-git-askpass",
    "trap 'rm -f \"$askpass\"' 0 1 2 15",
    "cat >\"$askpass\" <<'ASKPASS'",
    "#!/bin/sh",
    "case \"$1\" in",
    "  *Username*) printf '%s' x-access-token ;;",
    "  *Password*) printf '%s' \"$GITEA_TOKEN\" ;;",
    "  *) exit 1 ;;",
    "esac",
    "ASKPASS",
    "chmod 700 \"$askpass\"",
    "export GIT_ASKPASS=\"$askpass\"",
    "export GIT_TERMINAL_PROMPT=0",
    "test -n \"$GITEA_TOKEN\"",
    "git clone --filter=blob:none --no-checkout \"$(params.git-read-url)\" \"$root/repo\"",
    "cd \"$root/repo\"",
    "git fetch --depth=1 --filter=blob:none origin \"+$(params.source-stage-ref):refs/remotes/origin/hwpod-home-source-snapshot\"",
    "git checkout --detach \"$(params.revision)\"",
    "test \"$(git rev-parse HEAD)\" = \"$(params.revision)\"",
    "node scripts/check.mjs",
    "test -f config/hwpod-home.yaml",
    "chmod -R a+rX,g+rwX \"$root/repo\"",
    "rm -f \"$askpass\"",
    "trap - 0 1 2 15",
    "printf '{\"ok\":true,\"phase\":\"source-validate\",\"sourceCommit\":\"%s\",\"valuesPrinted\":false}\\n' \"$(params.revision)\"",
  ].join("\n");
}

function gitopsPublishScript(): string {
  return [
    "#!/bin/sh",
    "set -eu",
    "root=\"$(workspaces.source.path)\"",
    "exec bun \"$root/repo/scripts/ci/publish-gitops.mjs\" \\",
    "  --source-root \"$root/repo\" \\",
    "  --source-commit \"$(params.revision)\" \\",
    "  --metadata \"$root/build-metadata.json\" \\",
    "  --manifest-template-b64 \"$(params.manifest-template-b64)\" \\",
    "  --image-repository \"$(params.image-repository)\" \\",
    "  --gitops-read-url \"$(params.gitops-read-url)\" \\",
    "  --gitops-write-url \"$(params.gitops-write-url)\" \\",
    "  --gitops-branch \"$(params.gitops-branch)\" \\",
    "  --manifest-path \"$(params.gitops-manifest-path)\" \\",
    "  --release-state-path \"$(params.gitops-release-state-path)\" \\",
    "  --max-push-attempts \"$(params.gitops-max-push-attempts)\" \\",
    "  --author-name \"$(params.gitops-author-name)\" \\",
    "  --author-email \"$(params.gitops-author-email)\" \\",
    "  --worktree \"$root/gitops\"",
  ].join("\n");
}

function proxyEnv(): Record<string, unknown>[] {
  return [
    { name: "HTTP_PROXY", value: "$(params.build-http-proxy)" },
    { name: "http_proxy", value: "$(params.build-http-proxy)" },
    { name: "HTTPS_PROXY", value: "$(params.build-https-proxy)" },
    { name: "https_proxy", value: "$(params.build-https-proxy)" },
    { name: "ALL_PROXY", value: "$(params.build-all-proxy)" },
    { name: "all_proxy", value: "$(params.build-all-proxy)" },
    { name: "NO_PROXY", value: "$(params.build-no-proxy)" },
    { name: "no_proxy", value: "$(params.build-no-proxy)" },
  ];
}
