import { promisify } from 'node:util';
import { execFile } from 'node:child_process';
import type { Tool, ToolResult } from '../types.js';
import type { K8sConfig } from '../../config/index.js';

const execFileAsync = promisify(execFile);

type Runner = (
  file: string,
  args: string[],
  options?: { env?: NodeJS.ProcessEnv; maxBuffer?: number },
) => Promise<{ stdout: string; stderr: string }>;

interface PodItem {
  metadata?: { name?: string; namespace?: string };
  status?: { phase?: string; containerStatuses?: Array<{ ready?: boolean; restartCount?: number }> };
}

interface DeploymentItem {
  metadata?: { name?: string; namespace?: string };
  spec?: { replicas?: number };
  status?: { readyReplicas?: number; availableReplicas?: number };
}

function resolveNamespace(argsNamespace: string | undefined, cfg: NonNullable<K8sConfig>): string | undefined {
  return argsNamespace ?? cfg.default_namespace;
}

function assertAllowedNamespace(namespace: string | undefined, cfg: NonNullable<K8sConfig>): string | null {
  if (!namespace) {return null;}
  if (!cfg.allowed_namespaces || cfg.allowed_namespaces.length === 0) {
    return null;
  }
  return cfg.allowed_namespaces.includes(namespace)
    ? null
    : `Namespace "${namespace}" is not allowed by k8s.allowed_namespaces`;
}

function parseJson<T>(value: string): T {
  return JSON.parse(value) as T;
}

function formatPodReady(statuses?: Array<{ ready?: boolean }>): string {
  const total = statuses?.length ?? 0;
  const ready = statuses?.filter((s) => s.ready).length ?? 0;
  return `${ready}/${total}`;
}

function formatPodRestarts(statuses?: Array<{ restartCount?: number }>): number {
  return (statuses ?? []).reduce((sum, s) => sum + (s.restartCount ?? 0), 0);
}

export interface K8sToolDeps {
  runner?: Runner;
}

export function createK8sTools(config: NonNullable<K8sConfig>, deps?: K8sToolDeps): Tool[] {
  const runner = deps?.runner ?? (async (file: string, args: string[], options?: { env?: NodeJS.ProcessEnv; maxBuffer?: number }) => {
    return execFileAsync(file, args, options);
  });
  const kubectl = config.kubectl_path;

  const podsTool: Tool = {
    name: 'k8s.pods',
    description: 'List Kubernetes pods in a namespace or across all namespaces.',
    inputSchema: {
      type: 'object',
      properties: {
        namespace: { type: 'string', description: 'Namespace to query (defaults to k8s.default_namespace)' },
        all_namespaces: { type: 'boolean', description: 'Query all namespaces' },
        selector: { type: 'string', description: 'Label selector (e.g. "app=api")' },
        limit: { type: 'number', description: 'Maximum pods to display (default 25)' },
      },
      required: [],
    },
    execute: async (rawArgs: unknown): Promise<ToolResult> => {
      const args = rawArgs as { namespace?: string; all_namespaces?: boolean; selector?: string; limit?: number };
      try {
        if (args.all_namespaces && config.allowed_namespaces.length > 0) {
          return {
            success: false,
            output: '',
            error: 'all_namespaces=true is not allowed when k8s.allowed_namespaces is configured',
          };
        }
        const namespace = resolveNamespace(args.namespace, config);
        const nsError = assertAllowedNamespace(namespace, config);
        if (nsError) {
          return { success: false, output: '', error: nsError };
        }

        const cmdArgs = ['get', 'pods', '-o', 'json'];
        if (args.all_namespaces) {
          cmdArgs.push('-A');
        } else if (namespace) {
          cmdArgs.push('-n', namespace);
        }
        if (args.selector) {
          cmdArgs.push('-l', args.selector);
        }

        const { stdout } = await runner(kubectl, cmdArgs, { maxBuffer: 10 * 1024 * 1024 });
        const parsed = parseJson<{ items: PodItem[] }>(typeof stdout === 'string' ? stdout : stdout.toString('utf-8'));
        const limit = Math.max(1, Math.floor(args.limit ?? 25));
        const items = (parsed.items ?? []).slice(0, limit);

        if (items.length === 0) {
          return { success: true, output: 'No pods found.' };
        }

        const lines = items.map((item) => {
          const ns = item.metadata?.namespace ?? '-';
          const name = item.metadata?.name ?? '-';
          const phase = item.status?.phase ?? 'Unknown';
          const ready = formatPodReady(item.status?.containerStatuses);
          const restarts = formatPodRestarts(item.status?.containerStatuses);
          return `- ${ns}/${name} phase=${phase} ready=${ready} restarts=${restarts}`;
        });
        return {
          success: true,
          output: `Pods (${items.length} shown):\n${lines.join('\n')}`,
        };
      } catch (error) {
        return { success: false, output: '', error: error instanceof Error ? error.message : String(error) };
      }
    },
  };

  const deploymentsTool: Tool = {
    name: 'k8s.deployments',
    description: 'List Kubernetes deployments in a namespace or across all namespaces.',
    inputSchema: {
      type: 'object',
      properties: {
        namespace: { type: 'string', description: 'Namespace to query (defaults to k8s.default_namespace)' },
        all_namespaces: { type: 'boolean', description: 'Query all namespaces' },
        selector: { type: 'string', description: 'Label selector (e.g. "app=api")' },
        limit: { type: 'number', description: 'Maximum deployments to display (default 25)' },
      },
      required: [],
    },
    execute: async (rawArgs: unknown): Promise<ToolResult> => {
      const args = rawArgs as { namespace?: string; all_namespaces?: boolean; selector?: string; limit?: number };
      try {
        if (args.all_namespaces && config.allowed_namespaces.length > 0) {
          return {
            success: false,
            output: '',
            error: 'all_namespaces=true is not allowed when k8s.allowed_namespaces is configured',
          };
        }
        const namespace = resolveNamespace(args.namespace, config);
        const nsError = assertAllowedNamespace(namespace, config);
        if (nsError) {
          return { success: false, output: '', error: nsError };
        }

        const cmdArgs = ['get', 'deployments', '-o', 'json'];
        if (args.all_namespaces) {
          cmdArgs.push('-A');
        } else if (namespace) {
          cmdArgs.push('-n', namespace);
        }
        if (args.selector) {
          cmdArgs.push('-l', args.selector);
        }

        const { stdout } = await runner(kubectl, cmdArgs, { maxBuffer: 10 * 1024 * 1024 });
        const parsed = parseJson<{ items: DeploymentItem[] }>(typeof stdout === 'string' ? stdout : stdout.toString('utf-8'));
        const limit = Math.max(1, Math.floor(args.limit ?? 25));
        const items = (parsed.items ?? []).slice(0, limit);

        if (items.length === 0) {
          return { success: true, output: 'No deployments found.' };
        }

        const lines = items.map((item) => {
          const ns = item.metadata?.namespace ?? '-';
          const name = item.metadata?.name ?? '-';
          const desired = item.spec?.replicas ?? 0;
          const ready = item.status?.readyReplicas ?? 0;
          const available = item.status?.availableReplicas ?? 0;
          return `- ${ns}/${name} replicas=${ready}/${desired} available=${available}`;
        });
        return {
          success: true,
          output: `Deployments (${items.length} shown):\n${lines.join('\n')}`,
        };
      } catch (error) {
        return { success: false, output: '', error: error instanceof Error ? error.message : String(error) };
      }
    },
  };

  const logsTool: Tool = {
    name: 'k8s.logs',
    description: 'Fetch recent logs for a Kubernetes pod.',
    inputSchema: {
      type: 'object',
      properties: {
        pod: { type: 'string', description: 'Pod name' },
        namespace: { type: 'string', description: 'Namespace (defaults to k8s.default_namespace)' },
        container: { type: 'string', description: 'Container name (if pod has multiple containers)' },
        lines: { type: 'number', description: 'Tail lines (default 200, max 2000)' },
        since: { type: 'string', description: 'Only return logs newer than duration (e.g. "10m", "1h")' },
      },
      required: ['pod'],
    },
    execute: async (rawArgs: unknown): Promise<ToolResult> => {
      const args = rawArgs as { pod: string; namespace?: string; container?: string; lines?: number; since?: string };
      try {
        const namespace = resolveNamespace(args.namespace, config);
        const nsError = assertAllowedNamespace(namespace, config);
        if (nsError) {
          return { success: false, output: '', error: nsError };
        }

        const tail = Math.max(1, Math.min(2000, Math.floor(args.lines ?? 200)));
        const cmdArgs = ['logs', args.pod, '--tail', String(tail)];
        if (namespace) {
          cmdArgs.push('-n', namespace);
        }
        if (args.container) {
          cmdArgs.push('-c', args.container);
        }
        if (args.since) {
          cmdArgs.push('--since', args.since);
        }

        const { stdout } = await runner(kubectl, cmdArgs, { maxBuffer: 10 * 1024 * 1024 });
        const text = typeof stdout === 'string' ? stdout : stdout.toString('utf-8');
        const body = text.trim();
        return {
          success: true,
          output: body.length > 0 ? body : '(no log output)',
        };
      } catch (error) {
        return { success: false, output: '', error: error instanceof Error ? error.message : String(error) };
      }
    },
  };

  return [podsTool, deploymentsTool, logsTool];
}
