import { describe, expect, it } from "vitest";
import {
  DeploymentPreflightError,
  validateDeploymentPreflight,
  type DeploymentPreflightInput,
} from "../../src/deployment/preflight.js";

const VALID_ENV = {
  NODE_ENV: "production",
  DATABASE_URL: "postgresql://hub:secret@127.0.0.1:5432/hub",
  HUB_SILO_ORGANIZATION_ID: "org_test",
  HUB_SYSTEMD_UNIT: "cph-hub-test.service",
  CPH_BIN: "/srv/curriculum-project-hub/current/bin/cph",
  HOST: "127.0.0.1",
  PORT: "8788",
  HUB_PROJECT_WORKSPACE_ROOT: "/var/lib/cph-hub/workspaces",
  HUB_PUBLIC_BASE_URL: "https://hub.example.com",
  HUB_SESSION_SECRET: "a-production-session-secret-with-32-bytes",
  HUB_AGENT_MAX_CONCURRENT_RUNS: "1",
  HUB_AGENT_MAX_RUN_SECONDS: "900",
  HUB_HTTP_BODY_LIMIT_BYTES: "1048576",
  HUB_MAX_FILES_PER_MESSAGE: "8",
  HUB_MAX_FILE_BYTES: "26214400",
  HUB_HTTP_REQUESTS_PER_MINUTE: "120",
  HUB_FEISHU_EVENTS_PER_MINUTE: "120",
} as const;

function input(overrides: Partial<DeploymentPreflightInput> = {}): DeploymentPreflightInput {
  return {
    baseDir: "/srv/curriculum-project-hub",
    hubDir: "/srv/curriculum-project-hub/current/hub",
    serviceHome: "/var/lib/cph-hub/home",
    stateDir: "/var/lib/cph-hub/state",
    cacheDir: "/var/cache/cph-hub",
    workspaceRoot: "/var/lib/cph-hub/workspaces",
    expectedServiceUnit: "cph-hub-test.service",
    env: VALID_ENV,
    ...overrides,
  };
}

describe("validateDeploymentPreflight", () => {
  it("accepts a complete production configuration with persistent storage outside the deployment tree", () => {
    expect(validateDeploymentPreflight(input())).toEqual({
      bind: { host: "127.0.0.1", port: 8788 },
      databaseUrl: "postgresql://hub:secret@127.0.0.1:5432/hub",
      cphBin: "/srv/curriculum-project-hub/current/bin/cph",
    });
  });

  it.each([
    ["inside the live Hub", "/srv/curriculum-project-hub/current/hub/data/workspaces"],
    ["inside future releases", "/srv/curriculum-project-hub/releases/workspaces"],
    ["an ancestor of the deployment root", "/srv"],
  ])("rejects a workspace %s", (_description, workspaceRoot) => {
    expect(() =>
      validateDeploymentPreflight(
        input({
          workspaceRoot,
          env: { ...VALID_ENV, HUB_PROJECT_WORKSPACE_ROOT: workspaceRoot },
        }),
      ),
    ).toThrow("must not overlap deployment root");
  });

  it("rejects a runtime workspace that differs from the provisioned path", () => {
    expect(() =>
      validateDeploymentPreflight(
        input({ env: { ...VALID_ENV, HUB_PROJECT_WORKSPACE_ROOT: "/var/lib/cph-hub/other" } }),
      ),
    ).toThrow("HUB_PROJECT_WORKSPACE_ROOT must equal provisioned workspace root");
  });

  it("rejects a relative runtime workspace even if it resolves to the provisioned path", () => {
    expect(() =>
      validateDeploymentPreflight(
        input({ env: { ...VALID_ENV, HUB_PROJECT_WORKSPACE_ROOT: "./workspaces" } }),
      ),
    ).toThrow("HUB_PROJECT_WORKSPACE_ROOT must be an absolute normalized path");
  });

  it("reports all invalid required production values in one failed preflight", () => {
    expect.assertions(2);
    try {
      validateDeploymentPreflight(
        input({
          env: {
            ...VALID_ENV,
            HUB_SILO_ORGANIZATION_ID: "",
            HUB_PUBLIC_BASE_URL: "http://hub.example.com",
            HUB_SESSION_SECRET: "short",
          },
        }),
      );
    } catch (error) {
      expect(error).toBeInstanceOf(DeploymentPreflightError);
      expect((error as Error).message).toMatchInlineSnapshot(`
        "deployment preflight failed:
        - HUB_SILO_ORGANIZATION_ID is required
        - HUB_PUBLIC_BASE_URL must use https
        - HUB_SESSION_SECRET must contain at least 32 characters"
      `);
    }
  });

  it("rejects relative operational paths", () => {
    expect(() => validateDeploymentPreflight(input({ cacheDir: "./cache" }))).toThrow(
      "cache directory must be an absolute normalized path",
    );
  });

  it("rejects legacy process-global provider settings instead of silently ignoring them", () => {
    expect(() =>
      validateDeploymentPreflight(
        input({ env: { ...VALID_ENV, ANTHROPIC_AUTH_TOKEN: "global-secret" } }),
      ),
    ).toThrow("ANTHROPIC_AUTH_TOKEN must not be set");
  });
});
