import type {
  McpOrchestrator,
  ContainerSpec,
  ContainerInfo,
  ContainerLogs,
  ExecResult,
} from '../orchestrator.js';
import { K8sClient } from './k8s-client.js';
import type { K8sClientConfig } from './k8s-client.js';
import { generatePodSpec, generateNamespaceSpec } from './manifest-generator.js';

interface K8sPodStatus {
  metadata: {
    name: string;
    namespace: string;
    creationTimestamp: string;
    labels?: Record<string, string>;
  };
  status: {
    phase: string;
    containerStatuses?: Array<{
      state: {
        running?: Record<string, unknown>;
        waiting?: { reason?: string };
        terminated?: { reason?: string; exitCode?: number };
      };
    }>;
  };
  spec?: {
    containers: Array<{
      ports?: Array<{ containerPort: number }>;
    }>;
  };
}

interface K8sPodList {
  items: K8sPodStatus[];
}

function mapPhase(phase: string, containerStatuses?: K8sPodStatus['status']['containerStatuses']): ContainerInfo['state'] {
  // Check container-level status first for more granularity
  if (containerStatuses && containerStatuses.length > 0) {
    const cs = containerStatuses[0];
    if (cs) {
      if (cs.state.running) return 'running';
      if (cs.state.waiting) return 'starting';
      if (cs.state.terminated) return 'stopped';
    }
  }

  switch (phase) {
    case 'Running':
      return 'running';
    case 'Pending':
      return 'starting';
    case 'Succeeded':
    case 'Failed':
      return 'stopped';
    default:
      return 'unknown';
  }
}

export class KubernetesOrchestrator implements McpOrchestrator {
  private client: K8sClient;
  private namespace: string;

  constructor(config: K8sClientConfig) {
    this.client = new K8sClient(config);
    this.namespace = config.namespace ?? 'default';
  }

  async ping(): Promise<boolean> {
    try {
      const res = await this.client.get('/api/v1');
      return res.statusCode === 200;
    } catch {
      return false;
    }
  }

  async pullImage(_image: string): Promise<void> {
    // K8s pulls images on pod scheduling - no pre-pull needed
  }

  async createContainer(spec: ContainerSpec): Promise<ContainerInfo> {
    await this.ensureNamespace(this.namespace);

    const manifest = generatePodSpec(spec, this.namespace);
    const res = await this.client.post<K8sPodStatus>(
      `/api/v1/namespaces/${this.namespace}/pods`,
      manifest,
    );

    if (res.statusCode >= 400) {
      const err = res.body as unknown as { message?: string };
      throw new Error(`Failed to create pod: ${err.message ?? `HTTP ${res.statusCode}`}`);
    }

    // Wait briefly for pod to start scheduling
    await new Promise((resolve) => setTimeout(resolve, 500));

    return this.inspectContainer(res.body.metadata.name);
  }

  async stopContainer(containerId: string): Promise<void> {
    // In K8s, "stopping" a pod means deleting it
    await this.removeContainer(containerId);
  }

  async removeContainer(containerId: string, _force?: boolean): Promise<void> {
    const res = await this.client.delete(
      `/api/v1/namespaces/${this.namespace}/pods/${containerId}`,
    );
    if (res.statusCode >= 400 && res.statusCode !== 404) {
      const err = res.body as { message?: string };
      throw new Error(`Failed to delete pod: ${err.message ?? `HTTP ${res.statusCode}`}`);
    }
  }

  async inspectContainer(containerId: string): Promise<ContainerInfo> {
    const res = await this.client.get<K8sPodStatus>(
      `/api/v1/namespaces/${this.namespace}/pods/${containerId}`,
    );

    if (res.statusCode === 404) {
      throw new Error(`Pod "${containerId}" not found in namespace "${this.namespace}"`);
    }
    if (res.statusCode >= 400) {
      const err = res.body as unknown as { message?: string };
      throw new Error(`Failed to inspect pod: ${err.message ?? `HTTP ${res.statusCode}`}`);
    }

    const pod = res.body;
    const result: ContainerInfo = {
      containerId: pod.metadata.name,
      name: pod.metadata.name,
      state: mapPhase(pod.status.phase, pod.status.containerStatuses),
      createdAt: new Date(pod.metadata.creationTimestamp),
    };

    // Extract port from first container spec if available
    const containers = pod.spec?.containers;
    if (containers && containers.length > 0) {
      const ports = containers[0]?.ports;
      if (ports && ports.length > 0 && ports[0]) {
        result.port = ports[0].containerPort;
      }
    }

    return result;
  }

  async getContainerLogs(
    containerId: string,
    opts?: { tail?: number; since?: number },
  ): Promise<ContainerLogs> {
    const logOpts: { tail?: number; since?: number } = {
      tail: opts?.tail ?? 100,
    };
    if (opts?.since !== undefined) {
      logOpts.since = opts.since;
    }
    const stdout = await this.client.getLogs(this.namespace, containerId, logOpts);
    return { stdout, stderr: '' };
  }

  async execInContainer(
    _containerId: string,
    _cmd: string[],
    _opts?: { stdin?: string; timeoutMs?: number },
  ): Promise<ExecResult> {
    // K8s exec via API — future implementation
    throw new Error('execInContainer not yet implemented for Kubernetes');
  }

  async listContainers(namespace?: string): Promise<ContainerInfo[]> {
    const ns = namespace ?? this.namespace;
    const res = await this.client.get<K8sPodList>(
      `/api/v1/namespaces/${ns}/pods?labelSelector=mcpctl.managed%3Dtrue`,
    );
    if (res.statusCode >= 400) return [];

    return res.body.items.map((pod) => {
      const info: ContainerInfo = {
        containerId: pod.metadata.name,
        name: pod.metadata.name,
        state: mapPhase(pod.status.phase, pod.status.containerStatuses),
        createdAt: new Date(pod.metadata.creationTimestamp),
      };
      return info;
    });
  }

  async ensureNamespace(name: string): Promise<void> {
    const res = await this.client.get(`/api/v1/namespaces/${name}`);
    if (res.statusCode === 200) return;

    const nsManifest = generateNamespaceSpec(name);
    const createRes = await this.client.post('/api/v1/namespaces', nsManifest);
    if (createRes.statusCode >= 400 && createRes.statusCode !== 409) {
      const err = createRes.body as { message?: string };
      throw new Error(`Failed to create namespace "${name}": ${err.message ?? `HTTP ${createRes.statusCode}`}`);
    }
  }

  getNamespace(): string {
    return this.namespace;
  }
}
