import https from 'node:https';
import http from 'node:http';
import fs from 'node:fs';
import path from 'node:path';

export interface K8sClientConfig {
  apiServer: string;
  token?: string;
  caCert?: string;
  namespace?: string;
  /** Skip TLS verification (for dev clusters) */
  insecure?: boolean;
}

export interface K8sResponse<T = unknown> {
  statusCode: number;
  body: T;
}

export interface K8sError {
  kind: 'Status';
  apiVersion: 'v1';
  status: 'Failure';
  message: string;
  reason: string;
  code: number;
}

/**
 * Load config from in-cluster service account or KUBECONFIG env.
 */
export function loadDefaultConfig(): K8sClientConfig {
  // In-cluster detection
  const tokenPath = '/var/run/secrets/kubernetes.io/serviceaccount/token';
  const caPath = '/var/run/secrets/kubernetes.io/serviceaccount/ca.crt';
  const nsPath = '/var/run/secrets/kubernetes.io/serviceaccount/namespace';

  if (fs.existsSync(tokenPath)) {
    const token = fs.readFileSync(tokenPath, 'utf-8').trim();
    const namespace = fs.existsSync(nsPath) ? fs.readFileSync(nsPath, 'utf-8').trim() : 'default';
    const config: K8sClientConfig = {
      apiServer: `https://${process.env['KUBERNETES_SERVICE_HOST'] ?? 'kubernetes.default.svc'}:${process.env['KUBERNETES_SERVICE_PORT'] ?? '443'}`,
      token,
      namespace,
    };
    if (fs.existsSync(caPath)) {
      config.caCert = fs.readFileSync(caPath, 'utf-8');
    }
    return config;
  }

  // Fallback: parse KUBECONFIG or ~/.kube/config
  const kubeconfigPath = process.env['KUBECONFIG'] ?? path.join(process.env['HOME'] ?? '', '.kube', 'config');
  if (fs.existsSync(kubeconfigPath)) {
    return parseKubeconfig(fs.readFileSync(kubeconfigPath, 'utf-8'));
  }

  throw new Error('No Kubernetes configuration found (no in-cluster config or kubeconfig)');
}

/**
 * Minimal kubeconfig parser - extracts current-context server and auth.
 */
export function parseKubeconfig(raw: string): K8sClientConfig {
  // Simple YAML-like parser for kubeconfig (avoids yaml dependency)
  const lines = raw.split('\n');
  let currentContext = '';
  let contextCluster = '';
  let contextUser = '';
  let serverUrl = '';
  let token = '';
  let caCert: string | undefined;
  let namespace = 'default';

  // Pass 1: find current-context
  for (const line of lines) {
    const match = line.match(/^current-context:\s*(.+)/);
    if (match?.[1]) {
      currentContext = match[1].trim();
      break;
    }
  }

  // Pass 2: find context details
  let inContexts = false;
  let inTargetContext = false;
  for (const line of lines) {
    if (line.match(/^contexts:/)) { inContexts = true; continue; }
    if (inContexts && line.match(/^\S/) && !line.match(/^contexts:/)) { inContexts = false; }
    if (inContexts && line.includes(`name: ${currentContext}`)) { inTargetContext = true; continue; }
    if (inTargetContext) {
      const clusterMatch = line.match(/cluster:\s*(.+)/);
      if (clusterMatch?.[1]) contextCluster = clusterMatch[1].trim();
      const userMatch = line.match(/user:\s*(.+)/);
      if (userMatch?.[1]) contextUser = userMatch[1].trim();
      const nsMatch = line.match(/namespace:\s*(.+)/);
      if (nsMatch?.[1]) namespace = nsMatch[1].trim();
      if (contextCluster && contextUser) { inTargetContext = false; }
    }
  }

  // Pass 3: find cluster server
  let inClusters = false;
  let inTargetCluster = false;
  for (const line of lines) {
    if (line.match(/^clusters:/)) { inClusters = true; continue; }
    if (inClusters && line.match(/^\S/) && !line.match(/^clusters:/)) { inClusters = false; }
    if (inClusters && line.includes(`name: ${contextCluster}`)) { inTargetCluster = true; continue; }
    if (inTargetCluster) {
      const serverMatch = line.match(/server:\s*(.+)/);
      if (serverMatch?.[1]) { serverUrl = serverMatch[1].trim(); inTargetCluster = false; }
      const caMatch = line.match(/certificate-authority-data:\s*(.+)/);
      if (caMatch?.[1]) caCert = Buffer.from(caMatch[1].trim(), 'base64').toString('utf-8');
    }
  }

  // Pass 4: find user token
  let inUsers = false;
  let inTargetUser = false;
  for (const line of lines) {
    if (line.match(/^users:/)) { inUsers = true; continue; }
    if (inUsers && line.match(/^\S/) && !line.match(/^users:/)) { inUsers = false; }
    if (inUsers && line.includes(`name: ${contextUser}`)) { inTargetUser = true; continue; }
    if (inTargetUser) {
      const tokenMatch = line.match(/token:\s*(.+)/);
      if (tokenMatch?.[1]) { token = tokenMatch[1].trim(); inTargetUser = false; }
    }
  }

  if (!serverUrl) {
    throw new Error(`Could not parse kubeconfig: no server found for context "${currentContext}"`);
  }

  const config: K8sClientConfig = {
    apiServer: serverUrl,
    namespace,
  };
  if (token) config.token = token;
  if (caCert) config.caCert = caCert;
  return config;
}

export class K8sClient {
  private apiServer: string;
  private token: string | undefined;
  private caCert: string | undefined;
  private insecure: boolean;
  readonly defaultNamespace: string;

  constructor(config: K8sClientConfig) {
    this.apiServer = config.apiServer.replace(/\/$/, '');
    this.token = config.token;
    this.caCert = config.caCert;
    this.insecure = config.insecure ?? false;
    this.defaultNamespace = config.namespace ?? 'default';
  }

  async get<T = unknown>(path: string): Promise<K8sResponse<T>> {
    return this.request<T>('GET', path);
  }

  async post<T = unknown>(path: string, body: unknown): Promise<K8sResponse<T>> {
    return this.request<T>('POST', path, body);
  }

  async delete<T = unknown>(path: string): Promise<K8sResponse<T>> {
    return this.request<T>('DELETE', path);
  }

  async patch<T = unknown>(path: string, body: unknown): Promise<K8sResponse<T>> {
    return this.request<T>('PATCH', path, body, 'application/strategic-merge-patch+json');
  }

  private request<T>(method: string, urlPath: string, body?: unknown, contentType = 'application/json'): Promise<K8sResponse<T>> {
    return new Promise((resolve, reject) => {
      const url = new URL(urlPath, this.apiServer);
      const isHttps = url.protocol === 'https:';
      const transport = isHttps ? https : http;

      const headers: Record<string, string> = {
        Accept: 'application/json',
      };
      if (this.token) {
        headers['Authorization'] = `Bearer ${this.token}`;
      }

      let payload: string | undefined;
      if (body !== undefined) {
        payload = JSON.stringify(body);
        headers['Content-Type'] = contentType;
        headers['Content-Length'] = String(Buffer.byteLength(payload));
      }

      const opts: https.RequestOptions = {
        hostname: url.hostname,
        port: url.port || (isHttps ? 443 : 80),
        path: url.pathname + url.search,
        method,
        headers,
        timeout: 30000,
      };

      if (isHttps) {
        if (this.caCert) {
          opts.ca = this.caCert;
        }
        if (this.insecure) {
          opts.rejectUnauthorized = false;
        }
      }

      const req = transport.request(opts, (res) => {
        const chunks: Buffer[] = [];
        res.on('data', (chunk: Buffer) => chunks.push(chunk));
        res.on('end', () => {
          const raw = Buffer.concat(chunks).toString('utf-8');
          try {
            const parsed = JSON.parse(raw) as T;
            resolve({ statusCode: res.statusCode ?? 0, body: parsed });
          } catch {
            reject(new Error(`Invalid JSON from K8s API: ${raw.slice(0, 200)}`));
          }
        });
      });

      req.on('error', reject);
      req.on('timeout', () => {
        req.destroy();
        reject(new Error('K8s API request timed out'));
      });
      if (payload) req.write(payload);
      req.end();
    });
  }

  async getLogs(namespace: string, podName: string, opts?: { tail?: number; since?: number }): Promise<string> {
    return new Promise((resolve, reject) => {
      const params = new URLSearchParams();
      if (opts?.tail !== undefined) params.set('tailLines', String(opts.tail));
      if (opts?.since !== undefined) params.set('sinceSeconds', String(opts.since));
      const qs = params.toString();
      const urlPath = `/api/v1/namespaces/${namespace}/pods/${podName}/log${qs ? `?${qs}` : ''}`;

      const url = new URL(urlPath, this.apiServer);
      const isHttps = url.protocol === 'https:';
      const transport = isHttps ? https : http;

      const headers: Record<string, string> = {};
      if (this.token) {
        headers['Authorization'] = `Bearer ${this.token}`;
      }

      const reqOpts: https.RequestOptions = {
        hostname: url.hostname,
        port: url.port || (isHttps ? 443 : 80),
        path: url.pathname + url.search,
        method: 'GET',
        headers,
        timeout: 30000,
      };

      if (isHttps) {
        if (this.caCert) reqOpts.ca = this.caCert;
        if (this.insecure) reqOpts.rejectUnauthorized = false;
      }

      const req = transport.request(reqOpts, (res) => {
        const chunks: Buffer[] = [];
        res.on('data', (chunk: Buffer) => chunks.push(chunk));
        res.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
      });

      req.on('error', reject);
      req.on('timeout', () => {
        req.destroy();
        reject(new Error('K8s log request timed out'));
      });
      req.end();
    });
  }
}
