import { stableJsonSha256 } from "./stable-json";
import {
  readSub2RankConfig,
  resolveSub2RankTarget,
  sub2RankConfigLabel,
  type Sub2RankConfig,
  type Sub2RankTarget,
} from "./platform-infra-sub2rank-config";
import {
  renderSub2RankManifest,
  sub2RankConfigBase64Placeholder,
  sub2RankConfigSha256Placeholder,
  sub2RankImagePlaceholder,
  sub2RankSourceCommitPlaceholder,
} from "./platform-infra-sub2rank";
import type { PacSourceArtifactBinding, PacSourceArtifactMode, PacSourceArtifactRenderer } from "./platform-infra-pipelines-as-code-source-artifact";

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

interface EffectiveDelivery {
  profileId: string | null;
  sourceBranch: string;
  pipeline: Sub2RankConfig["delivery"]["pipeline"];
  build: Sub2RankConfig["delivery"]["build"];
  gitops: Sub2RankConfig["delivery"]["gitops"];
}

export function sub2RankOwningSourceRemote(): string {
  return readSub2RankConfig().application.remote;
}

export function renderSub2RankDesiredPipeline(binding: PacSourceArtifactBinding): {
  pipeline: Record<string, unknown>;
  provenance: Sub2RankPipelineProvenance;
} {
  const config = readSub2RankConfig();
  const expectedConfigRef = binding.consumer.sourceArtifact.configRef;
  const { target, delivery } = effectiveDelivery(config, expectedConfigRef, binding.consumer.node);
  const manifestTemplate = renderSub2RankManifest(config, target, {
    imageRef: sub2RankImagePlaceholder,
    appConfigBase64: sub2RankConfigBase64Placeholder,
    appConfigSha256: sub2RankConfigSha256Placeholder,
    sourceCommit: sub2RankSourceCommitPlaceholder,
  });
  return {
    pipeline: renderPipeline(config, target, delivery, binding, manifestTemplate),
    provenance: {
      configRef: expectedConfigRef,
      effectiveConfigSha256: stableJsonSha256(pipelineOwner(config, target, delivery)),
      renderer: "sub2rank-platform-service",
      mode: "embedded-pipeline-spec",
    },
  };
}

function effectiveDelivery(config: Sub2RankConfig, configRef: string, consumerNode: string): { target: Sub2RankTarget; delivery: EffectiveDelivery } {
  const baseRef = `${sub2RankConfigLabel}#delivery`;
  if (configRef === baseRef) {
    return {
      target: resolveSub2RankTarget(config, consumerNode),
      delivery: { profileId: null, sourceBranch: config.application.branch, pipeline: config.delivery.pipeline, build: config.delivery.build, gitops: config.delivery.gitops },
    };
  }
  const prefix = `${baseRef}.profiles.`;
  if (!configRef.startsWith(prefix)) throw new Error(`Sub2Rank sourceArtifact.configRef must equal ${baseRef} or ${prefix}<profile>`);
  const profileId = configRef.slice(prefix.length);
  const profile = config.delivery.profiles[profileId];
  if (!profile) throw new Error(`Sub2Rank delivery profile ${profileId} does not exist`);
  const target = resolveSub2RankTarget(config, profile.targetId);
  const routeNode = target.route.split(":", 1)[0];
  if (routeNode !== consumerNode) throw new Error(`Sub2Rank delivery profile ${profileId} target route must match consumer node ${consumerNode}`);
  return { target, delivery: { profileId, sourceBranch: profile.sourceBranch, pipeline: profile.pipeline, build: config.delivery.build, gitops: profile.gitops } };
}

function renderPipeline(
  config: Sub2RankConfig,
  target: Sub2RankTarget,
  delivery: EffectiveDelivery,
  binding: PacSourceArtifactBinding,
  manifestTemplate: string,
): Record<string, unknown> {
  const params = [
    { name: "git-read-url", type: "string", default: binding.repository.cloneUrl },
    { name: "source-branch", type: "string", default: delivery.sourceBranch },
    { name: "revision", type: "string" },
    { name: "source-stage-ref", type: "string" },
    { name: "config-path", type: "string", default: config.application.sourceConfigPath },
    { 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 }) => ({ name })), 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": "sub2rank",
        "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-cli-validate",
          image: "$(params.tools-image)",
          imagePullPolicy: "IfNotPresent",
          env: proxyEnv(),
          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: buildScript(),
        }], ["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\"",
    "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/sub2rank-source-snapshot\"",
    "git checkout --detach \"$(params.revision)\"",
    "test \"$(git rev-parse HEAD)\" = \"$(params.revision)\"",
    "bun install --frozen-lockfile",
    "bun scripts/apistate-cli.ts --config \"$(params.config-path)\" config validate",
    "chmod -R a+rX,g+rwX \"$root/repo\"",
    "printf '{\"ok\":true,\"phase\":\"source-validate\",\"sourceCommit\":\"%s\",\"configPath\":\"%s\",\"valuesPrinted\":false}\\n' \"$(params.revision)\" \"$(params.config-path)\"",
  ].join("\n");
}

function buildScript(): string {
  return [
    "#!/bin/sh",
    "set -eu",
    "exec \"$(workspaces.source.path)/repo/scripts/ci/build-image.sh\"",
  ].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)\" \\",
    "  --config-path \"$(params.config-path)\" \\",
    "  --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)" },
  ];
}

function pipelineOwner(config: Sub2RankConfig, target: Sub2RankTarget, delivery: EffectiveDelivery): Record<string, unknown> {
  const { scope: _scope, ...legacySecret } = config.runtime.secret;
  const targetOwner = delivery.profileId === null ? {
    id: target.id,
    route: target.route,
    namespace: target.namespace,
    enabled: target.enabled,
    replicas: target.replicas,
    publicExposure: target.publicExposure,
  } : target;
  const deliveryOwner = delivery.profileId === null ? {
    enabled: config.delivery.enabled,
    pipeline: delivery.pipeline,
    build: delivery.build,
    gitops: delivery.gitops,
  } : delivery;
  return {
    application: {
      repository: config.application.repository,
      remote: config.application.remote,
      branch: delivery.sourceBranch,
      sourceConfigPath: config.application.sourceConfigPath,
      dockerfile: config.application.dockerfile,
      runtimeTarget: config.application.runtimeTarget,
    },
    image: config.image,
    delivery: deliveryOwner,
    target: targetOwner,
    runtime: delivery.profileId === null ? { ...config.runtime, secret: legacySecret } : config.runtime,
  };
}
