import type {
  ConfigurationDefinition,
  ConfigurationOverride,
  ConfigurationScope,
  EffectiveConfiguration,
} from "@awp/contracts";

const scopeOrder: readonly ConfigurationScope[] = ["system", "project", "plan", "task", "run"];

export class ConfigurationRegistry {
  readonly #definitions = new Map<string, ConfigurationDefinition<unknown>>();
  register<T>(definition: ConfigurationDefinition<T>): void {
    if (this.#definitions.has(definition.key))
      throw new Error(`Configuration already registered: ${definition.key}`);
    this.#definitions.set(definition.key, definition as ConfigurationDefinition<unknown>);
  }
  get(key: string): ConfigurationDefinition<unknown> | undefined {
    return this.#definitions.get(key);
  }
}

export function resolveConfiguration<T>(
  definition: ConfigurationDefinition<T>,
  overrides: readonly ConfigurationOverride<T>[],
): EffectiveConfiguration<T> {
  const legal = overrides.filter((item) => definition.allowedScopes.includes(item.scopeType));
  legal.sort((a, b) => scopeOrder.indexOf(a.scopeType) - scopeOrder.indexOf(b.scopeType));
  const provenanceChain = legal.map(({ scopeType, scopeId }) => ({ scopeType, scopeId }));
  const selected = definition.overrideStrategy === "deny-lower-override" ? legal[0] : legal.at(-1);
  if (selected)
    return {
      value: selected.value,
      sourceScope: selected.scopeType,
      sourceId: selected.scopeId,
      provenanceChain,
    };
  if (definition.defaultValue === undefined)
    throw new Error(`No value configured for ${definition.key}`);
  return {
    value: definition.defaultValue,
    sourceScope: "system",
    sourceId: "default",
    provenanceChain: [],
  };
}

export const I1_CONFIGURATION_KEYS = {
  executionEnabled: "execution.enabled",
  executionDefaultModel: "execution.defaultModel",
  executionNativeAcpEnabled: "execution.nativeAcpEnabled",
  workspaceNamespace: "workspace.namespace",
  workspaceImage: "workspace.image",
} as const;

export const I1_CONFIGURATION_DEFINITIONS = [
  {
    key: I1_CONFIGURATION_KEYS.executionEnabled,
    schemaVersion: 1,
    title: "Execution enabled",
    description: "Project execution off-switch for future FactoryRuns",
    defaultValue: true,
    allowedScopes: ["system", "project"],
    overrideStrategy: "replace",
    mutable: true,
    effectCategory: "future-runs-only",
    sensitiveReferenceOnly: false,
  },
  {
    key: I1_CONFIGURATION_KEYS.executionDefaultModel,
    schemaVersion: 1,
    title: "Default execution model",
    description: "Default model persisted to a new FactoryRun when the owner does not select one",
    allowedScopes: ["system", "project"],
    overrideStrategy: "replace",
    mutable: true,
    effectCategory: "future-runs-only",
    sensitiveReferenceOnly: false,
  },
  {
    key: I1_CONFIGURATION_KEYS.executionNativeAcpEnabled,
    schemaVersion: 1,
    title: "Native ACP execution",
    description: "Deployment-managed switch selecting the native ACP execution path",
    defaultValue: false,
    allowedScopes: ["system"],
    overrideStrategy: "replace",
    mutable: false,
    effectCategory: "restart/reload-required",
    sensitiveReferenceOnly: false,
  },
  {
    key: I1_CONFIGURATION_KEYS.workspaceNamespace,
    schemaVersion: 1,
    title: "Workspace namespace",
    description: "Deployment-managed Kubernetes namespace used for I1 execution workspaces",
    defaultValue: "awp-workspaces",
    allowedScopes: ["system"],
    overrideStrategy: "replace",
    mutable: false,
    effectCategory: "restart/reload-required",
    sensitiveReferenceOnly: false,
  },
  {
    key: I1_CONFIGURATION_KEYS.workspaceImage,
    schemaVersion: 1,
    title: "Workspace image",
    description: "Deployment-managed OCI image used for I1 execution workspaces",
    defaultValue: "busybox:1.36.1",
    allowedScopes: ["system"],
    overrideStrategy: "replace",
    mutable: false,
    effectCategory: "restart/reload-required",
    sensitiveReferenceOnly: false,
  },
] as const satisfies readonly ConfigurationDefinition<unknown>[];

export type I1ConfigurationKey = (typeof I1_CONFIGURATION_DEFINITIONS)[number]["key"];

export function i1ConfigurationDefinition(
  key: string,
): ConfigurationDefinition<unknown> | undefined {
  return I1_CONFIGURATION_DEFINITIONS.find((definition) => definition.key === key);
}

export function validateI1ConfigurationValue(key: string, value: unknown): unknown {
  if (key === I1_CONFIGURATION_KEYS.executionEnabled) {
    if (typeof value !== "boolean") throw new Error(`${key} must be boolean`);
    return value;
  }
  if (key === I1_CONFIGURATION_KEYS.executionDefaultModel) {
    if (typeof value !== "string" || value.trim().length === 0 || value.length > 200) {
      throw new Error(`${key} must be a non-empty model identifier up to 200 characters`);
    }
    return value.trim();
  }
  if (key === I1_CONFIGURATION_KEYS.executionNativeAcpEnabled) {
    if (typeof value !== "boolean") throw new Error(`${key} must be boolean`);
    return value;
  }
  if (key === I1_CONFIGURATION_KEYS.workspaceNamespace) {
    if (typeof value !== "string" || !/^[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?$/.test(value)) {
      throw new Error(`${key} must be a valid Kubernetes namespace name`);
    }
    return value;
  }
  if (key === I1_CONFIGURATION_KEYS.workspaceImage) {
    if (typeof value !== "string" || value.trim().length === 0 || value.length > 500) {
      throw new Error(`${key} must be a non-empty OCI image reference up to 500 characters`);
    }
    return value.trim();
  }
  throw new Error(`Unknown I1 configuration definition: ${key}`);
}

/**
 * Environment/files in this list are process bootstrap, secret-store plumbing,
 * provider deployment identity, or test/dogfood harness inputs. They are not
 * registered ConfigurationDefinitions and cannot override a database setting.
 */
export const CONTROL_PLANE_BOOTSTRAP_ENVIRONMENT_KEYS = [
  "DATABASE_URL",
  "PORT",
  "AWP_CONTROL_HOST",
  "AWP_OPERATOR_PASSWORD_HASH",
  "AWP_SUBROUTER_URL",
  "AWP_SUBROUTER_ADMIN_TOKEN_FILE",
  "AWP_SUBROUTER_ACCOUNT_IMPORT_TOKEN_FILE",
  "AWP_CODEX_COMMAND",
  "AWP_FABRO_URL",
  "AWP_FABRO_TOKEN_FILE",
  "AWP_KUBERNETES_API_BASE",
  "AWP_KUBERNETES_TOKEN",
  "AWP_EXECUTION_CALLBACK_SECRET",
  "AWP_EXECUTION_CALLBACK_BASE_URL",
  "AWP_MODEL_GATEWAY_URL",
  "AWP_MODEL_GATEWAY_SIGNING_SECRET_FILE",
  "AWP_SOURCE_REPOSITORY_PATH",
  "AWP_GITHUB_PUBLICATION_TOKEN_FILE",
  "AWP_SELF_REPOSITORY_URL",
  "AWP_SELF_PROJECT_BOOTSTRAP",
  "AWP_KUBECTL_COMMAND",
  "AWP_DBOS_EXECUTOR_ID",
  "AWP_VISION_FILE",
  "AWP_SELF_REQUIRED_CHECKS",
  "AWP_DEPLOYMENT_MODE",
  "AWP_DOGFOOD_NATIVE_ACP_FAIL_AFTER_PROMPT_ON_INITIAL",
  "AWP_AGENT_START_DELAY_SECONDS",
  "AWP_AGENT_PATCH_FIXTURE_MODE",
] as const;
