import type { ContainerSpec } from '../orchestrator.js';
import { DEFAULT_MEMORY_LIMIT, DEFAULT_NANO_CPUS } from '../orchestrator.js';

const MCPCTL_LABEL = 'mcpctl.managed';

export interface K8sPodManifest {
  apiVersion: 'v1';
  kind: 'Pod';
  metadata: {
    name: string;
    namespace: string;
    labels: Record<string, string>;
  };
  spec: {
    containers: Array<{
      name: string;
      image: string;
      command?: string[];
      args?: string[];
      env?: Array<{ name: string; value: string }>;
      ports?: Array<{ containerPort: number }>;
      stdin?: boolean;
      resources: {
        limits: { memory: string; cpu: string };
        requests: { memory: string; cpu: string };
      };
      securityContext: {
        runAsNonRoot?: boolean;
        readOnlyRootFilesystem?: boolean;
        allowPrivilegeEscalation: boolean;
        capabilities: { drop: string[] };
        seccompProfile: { type: string };
      };
    }>;
    restartPolicy: 'Always' | 'Never' | 'OnFailure';
    automountServiceAccountToken: boolean;
    nodeSelector?: Record<string, string>;
  };
}

export interface K8sDeploymentManifest {
  apiVersion: 'apps/v1';
  kind: 'Deployment';
  metadata: {
    name: string;
    namespace: string;
    labels: Record<string, string>;
  };
  spec: {
    replicas: number;
    selector: { matchLabels: Record<string, string> };
    template: {
      metadata: { labels: Record<string, string> };
      spec: K8sPodManifest['spec'];
    };
  };
}

export interface K8sNamespaceManifest {
  apiVersion: 'v1';
  kind: 'Namespace';
  metadata: { name: string };
}

function formatMemory(bytes: number): string {
  if (bytes >= 1024 * 1024 * 1024) return `${Math.floor(bytes / (1024 * 1024 * 1024))}Gi`;
  if (bytes >= 1024 * 1024) return `${Math.floor(bytes / (1024 * 1024))}Mi`;
  if (bytes >= 1024) return `${Math.floor(bytes / 1024)}Ki`;
  return `${bytes}`;
}

function formatCpu(nanoCpus: number): string {
  const millicores = Math.floor(nanoCpus / 1_000_000);
  return `${millicores}m`;
}

function sanitizeName(name: string): string {
  return name.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/^-+|-+$/g, '').slice(0, 63);
}

function buildLabels(spec: ContainerSpec): Record<string, string> {
  return {
    [MCPCTL_LABEL]: 'true',
    'app.kubernetes.io/managed-by': 'mcpctl',
    'app.kubernetes.io/name': sanitizeName(spec.name),
    ...spec.labels,
  };
}

function buildContainerSpec(spec: ContainerSpec) {
  const memoryLimit = spec.memoryLimit ?? DEFAULT_MEMORY_LIMIT;
  const nanoCpus = spec.nanoCpus ?? DEFAULT_NANO_CPUS;
  const memStr = formatMemory(memoryLimit);
  const cpuStr = formatCpu(nanoCpus);

  const container: K8sPodManifest['spec']['containers'][0] = {
    name: sanitizeName(spec.name),
    image: spec.image,
    resources: {
      limits: { memory: memStr, cpu: cpuStr },
      requests: { memory: memStr, cpu: cpuStr },
    },
    securityContext: {
      // MCP server images (runner images, third-party) may run as root
      // Restrict privilege escalation and capabilities but allow root
      runAsNonRoot: false,
      readOnlyRootFilesystem: false,
      allowPrivilegeEscalation: false,
      capabilities: { drop: ['ALL'] },
      seccompProfile: { type: 'RuntimeDefault' },
    },
    // Keep stdin open for STDIO MCP servers (matches Docker's OpenStdin)
    stdin: true,
  };

  // In Docker, spec.command maps to Cmd (args to entrypoint).
  // In k8s, we use `args` to pass arguments to the image's entrypoint,
  // preserving the runner image's entrypoint (uvx, npx -y, etc.)
  if (spec.command && spec.command.length > 0) {
    container.args = spec.command;
  }

  if (spec.env && Object.keys(spec.env).length > 0) {
    container.env = Object.entries(spec.env).map(([name, value]) => ({ name, value }));
  }

  if (spec.containerPort) {
    container.ports = [{ containerPort: spec.containerPort }];
  }

  return container;
}

export function generatePodSpec(spec: ContainerSpec, namespace: string): K8sPodManifest {
  const labels = buildLabels(spec);
  return {
    apiVersion: 'v1',
    kind: 'Pod',
    metadata: {
      name: sanitizeName(spec.name),
      namespace,
      labels,
    },
    spec: {
      containers: [buildContainerSpec(spec)],
      restartPolicy: 'Always',
      // MCP server pods don't need k8s API access
      automountServiceAccountToken: false,
      // On mixed-arch clusters, constrain to the same arch as mcpd
      // (runner images are typically single-arch)
      ...(process.env['MCPD_NODE_SELECTOR']
        ? { nodeSelector: JSON.parse(process.env['MCPD_NODE_SELECTOR']) as Record<string, string> }
        : {}),
    },
  };
}

export function generateDeploymentSpec(spec: ContainerSpec, namespace: string, replicas = 1): K8sDeploymentManifest {
  const labels = buildLabels(spec);
  const selectorLabels = {
    'app.kubernetes.io/name': sanitizeName(spec.name),
    [MCPCTL_LABEL]: 'true',
  };

  return {
    apiVersion: 'apps/v1',
    kind: 'Deployment',
    metadata: {
      name: sanitizeName(spec.name),
      namespace,
      labels,
    },
    spec: {
      replicas,
      selector: { matchLabels: selectorLabels },
      template: {
        metadata: { labels },
        spec: {
          containers: [buildContainerSpec(spec)],
          restartPolicy: 'Always',
          automountServiceAccountToken: false,
        },
      },
    },
  };
}

export function generateNamespaceSpec(name: string): K8sNamespaceManifest {
  return {
    apiVersion: 'v1',
    kind: 'Namespace',
    metadata: { name },
  };
}

export { sanitizeName, formatMemory, formatCpu };
