import { mkdtemp, mkdir, symlink, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
import { unsafeOpaqueId, type CredentialReferenceId } from "@awp/contracts";
import { FileSecretStore } from "../../packages/providers/machine-ssh/src/file-secret-store.js";
import {
  machineSshArgs,
  SshK3sMachineHostAdapter,
} from "../../packages/providers/machine-ssh/src/machine-ssh.js";

const schemaSource = readFileSync("packages/persistence/src/schema.ts", "utf8");
const adapterSource = readFileSync("packages/providers/machine-ssh/src/machine-ssh.ts", "utf8");
const deploySource = readFileSync("infra/dogfood/deploy-debian3.sh", "utf8");

function tableBlock(name: string): string {
  const start = schemaSource.indexOf(`export const ${name} =`);
  expect(start).toBeGreaterThanOrEqual(0);
  const next = schemaSource.indexOf("\nexport const ", start + 1);
  return schemaSource.slice(start, next < 0 ? undefined : next);
}

describe("I7a machine enrollment trust boundary", () => {
  it("stores credential references but no SSH private key or K3s join token fields in product tables", () => {
    const machines = tableBlock("machines");
    const enrollments = tableBlock("machineEnrollments");
    const clusters = tableBlock("clusters");
    expect(machines).toContain("sshCredentialReferenceId");
    expect(clusters).toContain("joinCredentialReferenceId");
    for (const block of [machines, enrollments, clusters]) {
      expect(block).not.toMatch(/private[_A-Za-z]*key/i);
      expect(block).not.toMatch(/join[_A-Za-z]*token/i);
      expect(block).not.toMatch(/k3s[_A-Za-z]*token/i);
      expect(block).not.toMatch(/password\s*:/i);
    }
  });

  it("prevents CredentialReference path escape from the configured secret root", async () => {
    const root = await mkdtemp(join(tmpdir(), "awp-secret-root-"));
    const outside = await mkdtemp(join(tmpdir(), "awp-secret-outside-"));
    await writeFile(join(outside, "secret"), "outside-secret\n", { mode: 0o600 });
    await mkdir(join(root, "links"));
    await symlink(join(outside, "secret"), join(root, "links", "escape"));
    const store = new FileSecretStore(root);
    await expect(
      store.resolve(
        {
          id: unsafeOpaqueId<CredentialReferenceId>("credential:escape"),
          secretStoreKey: "links/escape",
        },
        "test",
      ),
    ).rejects.toThrow(/escapes configured secret root/);
  });

  it("does not put SSH keys or K3s join tokens in ssh process arguments", () => {
    expect(adapterSource).toMatch(/spawn\(\s*"ssh"/);
    expect(adapterSource).toMatch(/"sh"\s*,\s*"-s"/);
    expect(adapterSource).not.toContain("apiServerUrl, input.joinToken");
    expect(adapterSource).not.toContain("input.credential.value, input.joinToken");
    expect(adapterSource).toContain('Buffer.from(input.joinToken, "utf8").toString("base64")');
  });

  it("uses the configured OpenSSH port instead of Tailscale SSH port 22", () => {
    const args = machineSshArgs("user", "debian2", "/tmp/key", "/tmp/known-hosts", 2222);
    expect(args).toContain("-p");
    expect(args[args.indexOf("-p") + 1]).toBe("2222");
    expect(args.at(-1)).toBe("user@debian2");
    expect(() => new SshK3sMachineHostAdapter("/tmp/known-hosts", "v1.36.3+k3s1", 0)).toThrow(
      /Invalid SSH port/,
    );
    expect(() => new SshK3sMachineHostAdapter("/tmp/known-hosts", "v1.36.3+k3s1", 65536)).toThrow(
      /Invalid SSH port/,
    );
  });

  it("dogfood provisioning keeps enrollment secrets in protected files and pins K3s to the running server", () => {
    expect(deploySource).toContain("ssh-keygen -q -t ed25519");
    expect(deploySource).toContain(
      'sudo cat /var/lib/rancher/k3s/server/node-token > "$CONFIG_DIR/k3s-node-token"',
    );
    expect(deploySource).toContain("AWP_MACHINE_ENROLLMENT_SECRET_ROOT=$CONFIG_DIR");
    expect(deploySource).toContain("AWP_CLUSTER_K3S_VERSION=$K3S_VERSION");
    expect(deploySource).toContain('chmod 600 "$CONFIG_DIR"/*');
  });
});
