import assert from "node:assert/strict";

import {
  ENGINE_PRIVATE_EXTENSION_APPLY_RECEIPT_SCHEMA_VERSION,
  ENGINE_PRIVATE_EXTENSION_APPLY_REQUEST_SCHEMA_VERSION,
  ENGINE_PRIVATE_EXTENSION_CREDENTIAL_TYPES,
  ENGINE_PRIVATE_EXTENSION_INACTIVE_BASELINE,
  ENGINE_PRIVATE_EXTENSION_MANAGE_CAPABILITY,
  ENGINE_PRIVATE_EXTENSION_NODE_TYPES,
  ENGINE_PRIVATE_EXTENSION_OPERATION_SCHEMA_VERSION,
  ENGINE_PRIVATE_EXTENSION_PACKAGE_NAME,
  ENGINE_PRIVATE_EXTENSION_PLAN_REQUEST_SCHEMA_VERSION,
  ENGINE_PRIVATE_EXTENSION_PLAN_SCHEMA_VERSION,
  ENGINE_PRIVATE_EXTENSION_READ_CAPABILITY,
  ENGINE_PRIVATE_EXTENSION_STATUS_SCHEMA_VERSION,
  authorizeEnginePrivateExtensionOperation,
  computeEnginePrivateExtensionPlanHash,
  validateEnginePrivateExtensionApplyReceipt,
  validateEnginePrivateExtensionApplyRequest,
  validateEnginePrivateExtensionOperation,
  validateEnginePrivateExtensionPlan,
  validateEnginePrivateExtensionPlanRequest,
  validateEnginePrivateExtensionStatus,
} from "../src/index.mjs";

const digest = "03413c6f3c706a1f6a9597a1cf1730504e9719228d0c77fdfb93bbdcc57a4cf3";
const release = Object.freeze({
  kind: "release",
  packageName: ENGINE_PRIVATE_EXTENSION_PACKAGE_NAME,
  releaseId: "0.1.0-03413c6f3c706a1f",
  packageSha256: digest,
});
const inactive = Object.freeze({
  kind: "inactive-baseline",
  packageName: ENGINE_PRIVATE_EXTENSION_PACKAGE_NAME,
  baselineId: ENGINE_PRIVATE_EXTENSION_INACTIVE_BASELINE,
});
const manage = [ENGINE_PRIVATE_EXTENSION_MANAGE_CAPABILITY];

const activateRequest = {
  schemaVersion: ENGINE_PRIVATE_EXTENSION_PLAN_REQUEST_SCHEMA_VERSION,
  requestId: "extension-request-20260715-001",
  idempotencyKey: "extension-request-20260715-001",
  action: "activate",
  requestedAt: "2026-07-15T18:00:00.000Z",
  requestExpiresAt: "2026-07-15T18:10:00.000Z",
  expectedCurrentGeneration: 0,
  target: release,
};

assert.deepEqual(
  validateEnginePrivateExtensionPlanRequest(activateRequest, {
    now: "2026-07-15T18:00:01.000Z",
    grantedCapabilities: manage,
  }),
  { ok: true, errors: [] },
);
assert.equal(
  validateEnginePrivateExtensionPlanRequest(activateRequest, {
    now: "2026-07-15T18:00:01.000Z",
    grantedCapabilities: ["engine.l2.deploy"],
  }).errors.includes("engine_private_extension_capability_required"),
  true,
);
assert.equal(authorizeEnginePrivateExtensionOperation("status", [ENGINE_PRIVATE_EXTENSION_READ_CAPABILITY]).ok, true);
assert.equal(authorizeEnginePrivateExtensionOperation("apply", [ENGINE_PRIVATE_EXTENSION_READ_CAPABILITY]).ok, false);

const activatePlan = withPlanHash({
  schemaVersion: ENGINE_PRIVATE_EXTENSION_PLAN_SCHEMA_VERSION,
  planId: "extension-plan-20260715-001",
  planHash: hash("0"),
  requestId: activateRequest.requestId,
  idempotencyKey: activateRequest.idempotencyKey,
  action: "activate",
  createdAt: "2026-07-15T18:00:01.000Z",
  expiresAt: "2026-07-15T18:09:00.000Z",
  singleUse: true,
  requiredCapability: ENGINE_PRIVATE_EXTENSION_MANAGE_CAPABILITY,
  expectedCurrentGeneration: 0,
  nextGeneration: 1,
  currentState: inactive,
  targetState: release,
  recoveryState: inactive,
  actions: [
    "verify_staged_immutable_release",
    "prepare_sealed_package_tree",
    "verify_community_package_loader_policy",
    "quiesce_deploy_run_and_drain_queue",
    "record_recovery_state",
    "atomic_switch_current",
    "force_recreate_main_workers_webhooks_as_version_barrier",
    "verify_exact_runtime_acceptance",
    "commit_active_state",
    "resume_deploy_run",
  ],
  transition: transition(),
  acceptance: acceptanceSpec(release),
  failurePolicy: failurePolicy(),
});

assert.deepEqual(validateEnginePrivateExtensionPlan(activatePlan, { request: activateRequest }), { ok: true, errors: [] });
assert.equal(
  validateEnginePrivateExtensionPlan({ ...activatePlan, nextGeneration: 2 }).errors.includes("nextGeneration_must_increment_current_generation"),
  true,
);
assert.equal(
  validateEnginePrivateExtensionPlan({ ...activatePlan, transition: { ...transition(), runtimeAction: "restart" } })
    .errors.includes("transition_policy_mismatch"),
  true,
);
assert.equal(
  validateEnginePrivateExtensionPlan({
    ...activatePlan,
    transition: { ...transition(), loaderMode: "custom-extension", loaderPath: "N8N_CUSTOM_EXTENSIONS" },
  }).errors.includes("transition_policy_mismatch"),
  true,
);
assert.equal(
  validateEnginePrivateExtensionPlan({
    ...activatePlan,
    transition: { ...transition(), hotReload: true },
  }).errors.includes("transition_policy_mismatch"),
  true,
);
assert.equal(
  validateEnginePrivateExtensionPlan({
    ...activatePlan,
    transition: {
      ...transition(),
      loaderEnvironment: { ...transition().loaderEnvironment, N8N_REINSTALL_MISSING_PACKAGES: "true" },
    },
  }).errors.includes("transition_policy_mismatch"),
  true,
);
assert.equal(
  validateEnginePrivateExtensionPlan({
    ...activatePlan,
    transition: {
      ...transition(),
      loaderEnvironment: { ...transition().loaderEnvironment, NODE_PATH: "/tmp/untrusted-node-modules" },
    },
  }).errors.includes("transition.loaderEnvironment.NODE_PATH_not_allowed"),
  true,
);

const applyRequest = {
  schemaVersion: ENGINE_PRIVATE_EXTENSION_APPLY_REQUEST_SCHEMA_VERSION,
  planId: activatePlan.planId,
  planHash: activatePlan.planHash,
  idempotencyKey: activatePlan.idempotencyKey,
  confirmedAt: "2026-07-15T18:00:02.000Z",
};
assert.deepEqual(validateEnginePrivateExtensionApplyRequest(applyRequest, {
  plan: activatePlan,
  now: "2026-07-15T18:00:02.000Z",
  grantedCapabilities: manage,
}), { ok: true, errors: [] });
assert.equal(validateEnginePrivateExtensionApplyRequest(applyRequest, {
  plan: activatePlan,
  now: "2026-07-15T18:10:00.000Z",
  grantedCapabilities: manage,
}).errors.includes("plan_expired"), true);

const receipt = {
  schemaVersion: ENGINE_PRIVATE_EXTENSION_APPLY_RECEIPT_SCHEMA_VERSION,
  operationId: "extension-operation-20260715-001",
  planId: activatePlan.planId,
  planHash: activatePlan.planHash,
  action: "activate",
  acceptedAt: "2026-07-15T18:00:02.100Z",
  state: "queued",
};
assert.deepEqual(validateEnginePrivateExtensionApplyReceipt(receipt, { plan: activatePlan }), { ok: true, errors: [] });
assert.equal(validateEnginePrivateExtensionApplyReceipt({ ...receipt, state: "active" })
  .errors.includes("apply_receipt_state_must_be_queued"), true);

const activeOperation = {
  schemaVersion: ENGINE_PRIVATE_EXTENSION_OPERATION_SCHEMA_VERSION,
  operationId: receipt.operationId,
  planId: activatePlan.planId,
  planHash: activatePlan.planHash,
  action: "activate",
  state: "active",
  outcome: "committed",
  phase: "complete",
  expectedCurrentGeneration: 0,
  nextGeneration: 1,
  targetState: release,
  recoveryState: inactive,
  effectiveState: release,
  runtime: runtime(1, 2, 2),
  acceptance: acceptanceReport(release, "accepted"),
  updatedAt: "2026-07-15T18:00:32.000Z",
};
assert.deepEqual(validateEnginePrivateExtensionOperation(activeOperation), { ok: true, errors: [] });
const unexpectedSchema = structuredClone(activeOperation);
unexpectedSchema.acceptance.nodeTypes.observed.push("n8n-nodes-ndc.unreviewedNode");
assert.equal(validateEnginePrivateExtensionOperation(unexpectedSchema).errors
  .includes("acceptance.nodeTypes.observed_exact_set_required"), true);

const automaticRollback = {
  ...activeOperation,
  state: "rolled-back",
  outcome: "automatically-rolled-back",
  phase: "complete",
  effectiveState: inactive,
  acceptance: acceptanceReport(inactive, "accepted"),
  errorCode: "runtime_acceptance_failed",
};
assert.deepEqual(validateEnginePrivateExtensionOperation(automaticRollback), { ok: true, errors: [] });
assert.equal(
  validateEnginePrivateExtensionOperation({ ...automaticRollback, errorCode: undefined })
    .errors.includes("errorCode_invalid"),
  true,
);

const activeStatus = {
  schemaVersion: ENGINE_PRIVATE_EXTENSION_STATUS_SCHEMA_VERSION,
  packageName: ENGINE_PRIVATE_EXTENSION_PACKAGE_NAME,
  generation: 1,
  health: "ready",
  currentState: release,
  previousState: inactive,
  runtime: runtime(1, 2, 2),
  acceptance: acceptanceReport(release, "accepted"),
  updatedAt: "2026-07-15T18:00:33.000Z",
};
assert.deepEqual(validateEnginePrivateExtensionStatus(activeStatus), { ok: true, errors: [] });

const rollbackRequest = {
  schemaVersion: ENGINE_PRIVATE_EXTENSION_PLAN_REQUEST_SCHEMA_VERSION,
  requestId: "extension-rollback-request-20260715-001",
  idempotencyKey: "extension-rollback-request-20260715-001",
  action: "rollback",
  requestedAt: "2026-07-15T18:05:00.000Z",
  requestExpiresAt: "2026-07-15T18:15:00.000Z",
  expectedCurrentGeneration: 1,
  target: { kind: "previous-state", packageName: ENGINE_PRIVATE_EXTENSION_PACKAGE_NAME },
};
assert.deepEqual(validateEnginePrivateExtensionPlanRequest(rollbackRequest, {
  now: "2026-07-15T18:05:01.000Z",
  grantedCapabilities: manage,
}), { ok: true, errors: [] });

const rollbackPlan = withPlanHash({
  schemaVersion: ENGINE_PRIVATE_EXTENSION_PLAN_SCHEMA_VERSION,
  planId: "extension-rollback-plan-20260715-001",
  planHash: hash("0"),
  requestId: rollbackRequest.requestId,
  idempotencyKey: rollbackRequest.idempotencyKey,
  action: "rollback",
  createdAt: "2026-07-15T18:05:01.000Z",
  expiresAt: "2026-07-15T18:14:00.000Z",
  singleUse: true,
  requiredCapability: ENGINE_PRIVATE_EXTENSION_MANAGE_CAPABILITY,
  expectedCurrentGeneration: 1,
  nextGeneration: 2,
  currentState: release,
  targetState: inactive,
  recoveryState: release,
  actions: [
    "verify_previous_activation_state",
    "verify_community_package_loader_policy",
    "quiesce_deploy_run_and_drain_queue",
    "record_recovery_state",
    "atomic_switch_current_to_previous",
    "force_recreate_main_workers_webhooks_as_version_barrier",
    "verify_exact_runtime_acceptance",
    "commit_rolled_back_state",
    "resume_deploy_run",
  ],
  transition: transition(),
  acceptance: acceptanceSpec(inactive),
  failurePolicy: failurePolicy(),
});
assert.deepEqual(validateEnginePrivateExtensionPlan(rollbackPlan, { request: rollbackRequest }), { ok: true, errors: [] });

const explicitRollbackOperation = {
  ...activeOperation,
  operationId: "extension-operation-rollback-20260715-001",
  planId: rollbackPlan.planId,
  planHash: rollbackPlan.planHash,
  action: "rollback",
  state: "rolled-back",
  outcome: "explicitly-rolled-back",
  expectedCurrentGeneration: 1,
  nextGeneration: 2,
  targetState: inactive,
  recoveryState: release,
  effectiveState: inactive,
  runtime: runtime(2, 2, 2),
  acceptance: acceptanceReport(inactive, "accepted"),
};
assert.deepEqual(validateEnginePrivateExtensionOperation(explicitRollbackOperation), { ok: true, errors: [] });

const quarantinedStatus = {
  ...activeStatus,
  health: "quarantined",
  activeOperationId: "extension-operation-failed-20260715-001",
  acceptance: acceptanceReport(release, "rejected"),
  errorCode: "automatic_rollback_failed",
};
assert.deepEqual(validateEnginePrivateExtensionStatus(quarantinedStatus), { ok: true, errors: [] });

const pathInjection = structuredClone(activateRequest);
pathInjection.target.releaseId = "../../runtime";
assert.equal(validateEnginePrivateExtensionPlanRequest(pathInjection, {
  now: "2026-07-15T18:00:01.000Z",
  grantedCapabilities: manage,
}).errors.includes("target.releaseId_invalid"), true);

const commandInjection = { ...activateRequest, command: "docker exec runtime npm install" };
assert.equal(validateEnginePrivateExtensionPlanRequest(commandInjection, {
  now: "2026-07-15T18:00:01.000Z",
  grantedCapabilities: manage,
}).errors.includes("enginePrivateExtensionPlanRequest.command_not_allowed"), true);

console.log("external-provider Engine private-extension contract: ok");

function transition() {
  return {
    mountMode: "read-only",
    loaderMode: "community-package",
    loaderPath: "/home/node/.n8n/nodes/node_modules/n8n-nodes-ndc",
    loaderEnvironment: {
      N8N_COMMUNITY_PACKAGES_ENABLED: "true",
      N8N_COMMUNITY_PACKAGES_PREVENT_LOADING: "false",
      N8N_REINSTALL_MISSING_PACKAGES: "false",
    },
    quiesceMode: "block-deploy-run-and-drain-queue",
    stateSwitch: "atomic-current-previous",
    runtimeAction: "force-recreate",
    scope: "main-workers-webhooks",
    requireUniformGeneration: true,
    hotReload: false,
    preserveCredentials: true,
  };
}

function failurePolicy() {
  return {
    mode: "automatic-rollback",
    rollbackFailureOutcome: "quarantined",
    preserveImmutableRelease: true,
    preserveCredentials: true,
  };
}

function acceptanceSpec(state) {
  return {
    mode: "exact",
    nodeTypes: state.kind === "release" ? [...ENGINE_PRIVATE_EXTENSION_NODE_TYPES] : [],
    credentialSchemas: state.kind === "release" ? [...ENGINE_PRIVATE_EXTENSION_CREDENTIAL_TYPES] : [],
    requireUniformGeneration: true,
  };
}

function acceptanceReport(state, status) {
  const nodes = state.kind === "release" ? [...ENGINE_PRIVATE_EXTENSION_NODE_TYPES] : [];
  const credentials = state.kind === "release" ? [...ENGINE_PRIVATE_EXTENSION_CREDENTIAL_TYPES] : [];
  return {
    state: status,
    nodeTypes: { expected: nodes, observed: status === "pending" ? [] : nodes },
    credentialSchemas: { expected: credentials, observed: status === "pending" ? [] : credentials },
    uniformGeneration: status === "accepted",
  };
}

function runtime(generation, expectedInstances, readyInstances) {
  return { mode: "force-recreate", generation, expectedInstances, readyInstances };
}

function withPlanHash(plan) {
  plan.planHash = computeEnginePrivateExtensionPlanHash(plan);
  return plan;
}

function hash(character) {
  return "sha256:" + character.repeat(64);
}
