"""OCLAWMA Skill: Kubernetes

Official Kubernetes skill for OCLAWMA providing comprehensive cluster
management capabilities including kubectl operations, pod log retrieval,
deployment management, and Helm chart support.
"""

from __future__ import annotations

import json
import os
import subprocess
import tempfile
from pathlib import Path
from typing import Any

import yaml
from oclawma.skills import LazySkill, SkillMetadata

__all__ = ["KubernetesSkill"]


class KubernetesSkill(LazySkill):
    """Kubernetes cluster management skill.

    This skill provides tools for managing Kubernetes clusters using kubectl
    and Helm. It wraps common operations into simple, safe tool calls with
    proper error handling and output formatting.

    Features:
    - kubectl wrapper for any command
    - Pod management and log retrieval
    - Deployment scaling and rollout control
    - Helm chart lifecycle management
    - Context switching and multi-cluster support
    - Resource metrics (top)
    - Event monitoring

    Example:
        >>> from oclawma.skills import SkillRegistry
        >>> registry = SkillRegistry()
        >>>
        >>> # List pods
        >>> result = await registry.execute_tool("kubernetes", "get_pods")
        >>>
        >>> # Get pod logs
        >>> result = await registry.execute_tool(
        ...     "kubernetes", "get_logs",
        ...     pod="my-app-xxx", namespace="default", tail=50
        ... )
        >>>
        >>> # Scale deployment
        >>> result = await registry.execute_tool(
        ...     "kubernetes", "scale_deployment",
        ...     name="my-app", replicas=3, namespace="production"
        ... )
    """

    def __init__(self, metadata: SkillMetadata) -> None:
        """Initialize the Kubernetes skill.

        Args:
            metadata: Skill metadata from the entry point.
        """
        super().__init__(metadata)
        self._kubectl_path: str | None = None
        self._helm_path: str | None = None
        self._default_namespace = os.environ.get("KUBECTL_NAMESPACE", "default")
        self._default_context = os.environ.get("KUBECTL_CONTEXT", "")

    def _find_binary(self, name: str) -> str | None:
        """Find the path to a binary.

        Args:
            name: Binary name to find.

        Returns:
            Full path to binary or None if not found.
        """
        try:
            result = subprocess.run(
                ["which", name],
                capture_output=True,
                text=True,
                check=False
            )
            if result.returncode == 0:
                return result.stdout.strip()
        except Exception:
            pass
        return None

    def _get_kubectl(self) -> str:
        """Get kubectl binary path, caching the result.

        Returns:
            Path to kubectl binary.

        Raises:
            SkillLoadError: If kubectl is not found.
        """
        if self._kubectl_path is None:
            self._kubectl_path = self._find_binary("kubectl")
            if self._kubectl_path is None:
                from oclawma.skills import SkillLoadError
                raise SkillLoadError(
                    "kubectl not found. Please install kubectl and ensure it's in PATH."
                )
        return self._kubectl_path

    def _get_helm(self) -> str | None:
        """Get helm binary path, caching the result.

        Returns:
            Path to helm binary or None if not installed.
        """
        if self._helm_path is None:
            self._helm_path = self._find_binary("helm")
        return self._helm_path

    def _build_kubectl_args(
        self,
        command: list[str],
        namespace: str | None = None,
        context: str | None = None
    ) -> list[str]:
        """Build kubectl command arguments.

        Args:
            command: Base command arguments.
            namespace: Optional namespace override.
            context: Optional context override.

        Returns:
            Complete argument list for kubectl.
        """
        args = [self._get_kubectl()]

        # Add context if specified
        if context:
            args.extend(["--context", context])
        elif self._default_context:
            args.extend(["--context", self._default_context])

        # Add namespace if specified
        if namespace:
            args.extend(["--namespace", namespace])

        args.extend(command)
        return args

    def _run_kubectl(
        self,
        command: list[str],
        namespace: str | None = None,
        context: str | None = None,
        timeout: int = 60,
        capture_output: bool = True
    ) -> dict[str, Any]:
        """Execute a kubectl command.

        Args:
            command: Command arguments.
            namespace: Optional namespace.
            context: Optional context.
            timeout: Command timeout in seconds.
            capture_output: Whether to capture output.

        Returns:
            Standardized result dictionary.
        """
        try:
            args = self._build_kubectl_args(command, namespace, context)

            result = subprocess.run(
                args,
                capture_output=capture_output,
                text=True,
                check=False,
                timeout=timeout
            )

            output = result.stdout if result.returncode == 0 else result.stderr

            # Truncate very long outputs
            truncated = False
            if len(output) > 50000:
                output = output[:50000] + "\n... [output truncated]"
                truncated = True

            return {
                "success": result.returncode == 0,
                "output": output,
                "exit_code": result.returncode,
                "truncated": truncated
            }

        except subprocess.TimeoutExpired:
            return {
                "success": False,
                "output": "",
                "error": f"Command timed out after {timeout} seconds"
            }
        except Exception as e:
            return {
                "success": False,
                "output": "",
                "error": f"Failed to execute kubectl: {str(e)}"
            }

    def _run_helm(
        self,
        command: list[str],
        namespace: str | None = None,
        timeout: int = 120
    ) -> dict[str, Any]:
        """Execute a helm command.

        Args:
            command: Command arguments.
            namespace: Optional namespace.
            timeout: Command timeout in seconds.

        Returns:
            Standardized result dictionary.
        """
        helm_path = self._get_helm()
        if not helm_path:
            return {
                "success": False,
                "output": "",
                "error": "Helm not found. Please install Helm."
            }

        try:
            args = [helm_path]

            # Add namespace if specified
            if namespace:
                args.extend(["--namespace", namespace])

            args.extend(command)

            result = subprocess.run(
                args,
                capture_output=True,
                text=True,
                check=False,
                timeout=timeout
            )

            output = result.stdout if result.returncode == 0 else result.stderr

            # Truncate very long outputs
            truncated = False
            if len(output) > 50000:
                output = output[:50000] + "\n... [output truncated]"
                truncated = True

            return {
                "success": result.returncode == 0,
                "output": output,
                "exit_code": result.returncode,
                "truncated": truncated
            }

        except subprocess.TimeoutExpired:
            return {
                "success": False,
                "output": "",
                "error": f"Command timed out after {timeout} seconds"
            }
        except Exception as e:
            return {
                "success": False,
                "output": "",
                "error": f"Failed to execute helm: {str(e)}"
            }

    def _load(self) -> None:
        """Load the skill's tools.

        This is called automatically when tools are first accessed.
        """
        self._tools = {
            # kubectl wrapper
            "kubectl": self._kubectl,

            # Pod management
            "get_pods": self._get_pods,
            "get_logs": self._get_logs,
            "describe_pod": self._describe_pod,
            "exec_command": self._exec_command,

            # Deployment management
            "get_deployments": self._get_deployments,
            "scale_deployment": self._scale_deployment,
            "rollout_deployment": self._rollout_deployment,

            # Resource management
            "apply_manifest": self._apply_manifest,
            "delete_resource": self._delete_resource,

            # Helm support
            "helm_list": self._helm_list,
            "helm_install": self._helm_install,
            "helm_upgrade": self._helm_upgrade,
            "helm_uninstall": self._helm_uninstall,
            "helm_status": self._helm_status,
            "helm_get_values": self._helm_get_values,

            # Context management
            "get_contexts": self._get_contexts,
            "set_context": self._set_context,

            # Monitoring
            "get_events": self._get_events,
            "top_pods": self._top_pods,
            "top_nodes": self._top_nodes,

            # Utilities
            "port_forward": self._port_forward,
        }
        self._loaded = True

    # -------------------------------------------------------------------------
    # kubectl Wrapper
    # -------------------------------------------------------------------------

    async def _kubectl(
        self,
        command: str,
        namespace: str | None = None,
        context: str | None = None,
        timeout: int = 60
    ) -> dict[str, Any]:
        """Execute a kubectl command.

        Args:
            command: kubectl command arguments.
            namespace: Kubernetes namespace.
            context: Kubernetes context.
            timeout: Command timeout in seconds.

        Returns:
            Standardized result dictionary.
        """
        try:
            # Pre-validate kubectl exists
            self._get_kubectl()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = command.split()
        return self._run_kubectl(args, namespace, context, timeout)

    # -------------------------------------------------------------------------
    # Pod Management
    # -------------------------------------------------------------------------

    async def _get_pods(
        self,
        namespace: str | None = None,
        selector: str | None = None,
        all_namespaces: bool = False,
        output: str = "json"
    ) -> dict[str, Any]:
        """List pods with optional filtering.

        Args:
            namespace: Namespace to list pods from.
            selector: Label selector (e.g., "app=nginx").
            all_namespaces: List pods from all namespaces.
            output: Output format (json, yaml, wide, name).

        Returns:
            Standardized result dictionary with pod list.
        """
        args = ["get", "pods"]

        if all_namespaces:
            args.append("--all-namespaces")

        if selector:
            args.extend(["--selector", selector])

        if output:
            args.extend(["-o", output])

        return self._run_kubectl(args, namespace=namespace)

    async def _get_logs(
        self,
        pod: str,
        namespace: str = "default",
        container: str | None = None,
        follow: bool = False,
        tail: int = 100,
        previous: bool = False,
        timestamps: bool = True
    ) -> dict[str, Any]:
        """Get pod logs.

        Args:
            pod: Pod name or selector.
            namespace: Pod namespace.
            container: Container name (for multi-container pods).
            follow: Follow log output (streaming).
            tail: Number of lines from end.
            previous: Show logs from previous container instance.
            timestamps: Include timestamps.

        Returns:
            Standardized result dictionary with logs.
        """
        args = ["logs", pod]

        if container:
            args.extend(["-c", container])

        if follow:
            args.append("--follow")

        if tail > 0 and not follow:
            args.extend(["--tail", str(tail)])

        if previous:
            args.append("--previous")

        if timestamps:
            args.append("--timestamps")

        result = self._run_kubectl(args, namespace=namespace, timeout=30 if not follow else 10)

        # Don't truncate if following (streaming)
        if follow:
            return result

        # For non-follow mode, truncate long logs
        if result["success"] and len(result["output"]) > 10000:
            lines = result["output"].split("\n")
            result["output"] = "\n".join(lines[-tail:])
            result["truncated"] = True

        return result

    async def _describe_pod(
        self,
        pod: str,
        namespace: str = "default",
        events: bool = True
    ) -> dict[str, Any]:
        """Get detailed pod information.

        Args:
            pod: Pod name.
            namespace: Pod namespace.
            events: Include recent events.

        Returns:
            Standardized result dictionary with pod description.
        """
        args = ["describe", "pod", pod]

        if not events:
            args.append("--show-events=false")

        return self._run_kubectl(args, namespace=namespace)

    async def _exec_command(
        self,
        pod: str,
        command: str,
        namespace: str = "default",
        container: str | None = None
    ) -> dict[str, Any]:
        """Execute a command in a pod container.

        Args:
            pod: Pod name.
            command: Command to execute.
            namespace: Pod namespace.
            container: Container name.

        Returns:
            Standardized result dictionary with command output.
        """
        args = ["exec", pod, "--"]
        args.extend(command.split())

        if container:
            args.insert(2, container)
            args.insert(2, "-c")

        return self._run_kubectl(args, namespace=namespace, timeout=30)

    # -------------------------------------------------------------------------
    # Deployment Management
    # -------------------------------------------------------------------------

    async def _get_deployments(
        self,
        namespace: str | None = None,
        all_namespaces: bool = False,
        selector: str | None = None,
        output: str = "json"
    ) -> dict[str, Any]:
        """List deployments.

        Args:
            namespace: Namespace to list from.
            all_namespaces: List from all namespaces.
            selector: Label selector.
            output: Output format.

        Returns:
            Standardized result dictionary.
        """
        args = ["get", "deployments"]

        if all_namespaces:
            args.append("--all-namespaces")

        if selector:
            args.extend(["--selector", selector])

        if output:
            args.extend(["-o", output])

        return self._run_kubectl(args, namespace=namespace)

    async def _scale_deployment(
        self,
        name: str,
        replicas: int,
        namespace: str = "default",
        wait: bool = False
    ) -> dict[str, Any]:
        """Scale a deployment to specified replicas.

        Args:
            name: Deployment name.
            replicas: Number of replicas.
            namespace: Deployment namespace.
            wait: Wait for rollout to complete.

        Returns:
            Standardized result dictionary.
        """
        args = ["scale", "deployment", name, f"--replicas={replicas}"]

        result = self._run_kubectl(args, namespace=namespace)

        if result["success"] and wait:
            # Wait for rollout
            wait_args = ["rollout", "status", f"deployment/{name}"]
            wait_result = self._run_kubectl(wait_args, namespace=namespace, timeout=120)
            result["rollout_status"] = wait_result

        return result

    async def _rollout_deployment(
        self,
        name: str,
        action: str,
        namespace: str = "default",
        revision: int | None = None
    ) -> dict[str, Any]:
        """Manage deployment rollouts.

        Args:
            name: Deployment name.
            action: Action (status, history, pause, resume, restart, undo).
            namespace: Deployment namespace.
            revision: Revision for undo action.

        Returns:
            Standardized result dictionary.
        """
        valid_actions = ["status", "history", "pause", "resume", "restart", "undo"]

        if action not in valid_actions:
            return {
                "success": False,
                "error": f"Invalid action: {action}. Valid actions: {', '.join(valid_actions)}"
            }

        args = ["rollout", action, f"deployment/{name}"]

        if action == "undo" and revision:
            args.extend([f"--to-revision={revision}"])

        return self._run_kubectl(args, namespace=namespace, timeout=120)

    # -------------------------------------------------------------------------
    # Resource Management
    # -------------------------------------------------------------------------

    async def _apply_manifest(
        self,
        manifest: str,
        namespace: str | None = None,
        dry_run: str | None = None
    ) -> dict[str, Any]:
        """Apply a Kubernetes manifest.

        Args:
            manifest: YAML manifest content or file path.
            namespace: Target namespace.
            dry_run: Dry run mode (client, server).

        Returns:
            Standardized result dictionary.
        """
        # Determine if manifest is a file path or raw YAML
        manifest_path = Path(manifest)
        is_file = manifest_path.exists() and manifest_path.is_file()

        if not is_file:
            # Validate YAML and write to temp file
            try:
                yaml_content = yaml.safe_load(manifest)
                if yaml_content is None:
                    return {
                        "success": False,
                        "error": "Empty or invalid YAML manifest"
                    }
            except yaml.YAMLError as e:
                return {
                    "success": False,
                    "error": f"Invalid YAML: {str(e)}"
                }

            # Write to temp file
            with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
                f.write(manifest)
                temp_path = f.name
        else:
            temp_path = str(manifest_path)

        try:
            args = ["apply", "-f", temp_path]

            if dry_run:
                args.append(f"--dry-run={dry_run}")

            return self._run_kubectl(args, namespace=namespace)

        finally:
            if not is_file:
                os.unlink(temp_path)

    async def _delete_resource(
        self,
        kind: str,
        name: str,
        namespace: str = "default",
        force: bool = False
    ) -> dict[str, Any]:
        """Delete a Kubernetes resource.

        Args:
            kind: Resource kind (pod, deployment, service, etc.).
            name: Resource name.
            namespace: Resource namespace.
            force: Force delete without grace period.

        Returns:
            Standardized result dictionary.
        """
        args = ["delete", kind, name]

        if force:
            args.append("--force")
            args.append("--grace-period=0")

        return self._run_kubectl(args, namespace=namespace)

    # -------------------------------------------------------------------------
    # Helm Support
    # -------------------------------------------------------------------------

    async def _helm_list(
        self,
        namespace: str | None = None,
        all_namespaces: bool = False,
        filter: str | None = None,
        output: str = "json"
    ) -> dict[str, Any]:
        """List Helm releases.

        Args:
            namespace: Filter by namespace.
            all_namespaces: List from all namespaces.
            filter: Filter releases by name.
            output: Output format.

        Returns:
            Standardized result dictionary.
        """
        args = ["list"]

        if all_namespaces:
            args.append("--all-namespaces")

        if filter:
            args.extend(["--filter", filter])

        if output:
            args.extend(["-o", output])

        return self._run_helm(args, namespace=namespace)

    async def _helm_install(
        self,
        name: str,
        chart: str,
        namespace: str = "default",
        version: str | None = None,
        values: dict[str, Any] | None = None,
        values_file: str | None = None,
        create_namespace: bool = False,
        dry_run: bool = False,
        wait: bool = False
    ) -> dict[str, Any]:
        """Install a Helm chart.

        Args:
            name: Release name.
            chart: Chart reference.
            namespace: Target namespace.
            version: Chart version.
            values: Values to override.
            values_file: Path to values.yaml.
            create_namespace: Create namespace if it doesn't exist.
            dry_run: Simulate installation.
            wait: Wait for resources to be ready.

        Returns:
            Standardized result dictionary.
        """
        args = ["install", name, chart]

        if version:
            args.extend(["--version", version])

        if create_namespace:
            args.append("--create-namespace")

        if dry_run:
            args.append("--dry-run")

        if wait:
            args.append("--wait")

        # Handle values
        temp_values_file = None
        if values:
            with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
                yaml.dump(values, f)
                temp_values_file = f.name
            args.extend(["-f", temp_values_file])

        if values_file:
            args.extend(["-f", values_file])

        try:
            return self._run_helm(args, namespace=namespace, timeout=300 if wait else 120)
        finally:
            if temp_values_file:
                os.unlink(temp_values_file)

    async def _helm_upgrade(
        self,
        name: str,
        chart: str,
        namespace: str = "default",
        version: str | None = None,
        values: dict[str, Any] | None = None,
        values_file: str | None = None,
        install: bool = False,
        dry_run: bool = False,
        wait: bool = False
    ) -> dict[str, Any]:
        """Upgrade a Helm release.

        Args:
            name: Release name.
            chart: Chart reference.
            namespace: Release namespace.
            version: Chart version.
            values: Values to override.
            values_file: Path to values.yaml.
            install: Install if release doesn't exist.
            dry_run: Simulate upgrade.
            wait: Wait for resources to be ready.

        Returns:
            Standardized result dictionary.
        """
        args = ["upgrade", name, chart]

        if install:
            args.append("--install")

        if version:
            args.extend(["--version", version])

        if dry_run:
            args.append("--dry-run")

        if wait:
            args.append("--wait")

        # Handle values
        temp_values_file = None
        if values:
            with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
                yaml.dump(values, f)
                temp_values_file = f.name
            args.extend(["-f", temp_values_file])

        if values_file:
            args.extend(["-f", values_file])

        try:
            return self._run_helm(args, namespace=namespace, timeout=300 if wait else 120)
        finally:
            if temp_values_file:
                os.unlink(temp_values_file)

    async def _helm_uninstall(
        self,
        name: str,
        namespace: str = "default",
        keep_history: bool = False,
        wait: bool = False
    ) -> dict[str, Any]:
        """Uninstall a Helm release.

        Args:
            name: Release name.
            namespace: Release namespace.
            keep_history: Keep release history.
            wait: Wait for resources to be deleted.

        Returns:
            Standardized result dictionary.
        """
        args = ["uninstall", name]

        if keep_history:
            args.append("--keep-history")

        if wait:
            args.append("--wait")

        return self._run_helm(args, namespace=namespace, timeout=120)

    async def _helm_status(
        self,
        name: str,
        namespace: str = "default",
        revision: int | None = None,
        show_resources: bool = False
    ) -> dict[str, Any]:
        """Get Helm release status.

        Args:
            name: Release name.
            namespace: Release namespace.
            revision: Specific revision to show.
            show_resources: Show resources created by the release.

        Returns:
            Standardized result dictionary.
        """
        args = ["status", name]

        if revision:
            args.extend(["--revision", str(revision)])

        if show_resources:
            args.append("--show-resources")

        args.extend(["-o", "json"])

        return self._run_helm(args, namespace=namespace)

    async def _helm_get_values(
        self,
        name: str,
        namespace: str = "default",
        all: bool = False
    ) -> dict[str, Any]:
        """Get Helm release values.

        Args:
            name: Release name.
            namespace: Release namespace.
            all: Show all computed values.

        Returns:
            Standardized result dictionary.
        """
        args = ["get", "values", name]

        if all:
            args.append("--all")

        args.extend(["-o", "yaml"])

        return self._run_helm(args, namespace=namespace)

    # -------------------------------------------------------------------------
    # Context Management
    # -------------------------------------------------------------------------

    async def _get_contexts(self, current: bool = False) -> dict[str, Any]:
        """List available Kubernetes contexts.

        Args:
            current: Show only current context.

        Returns:
            Standardized result dictionary.
        """
        args = ["config", "get-contexts"]

        if current:
            args.append("--current")
        else:
            args.extend(["-o", "json"])

        return self._run_kubectl(args)

    async def _set_context(self, context: str) -> dict[str, Any]:
        """Switch to a different Kubernetes context.

        Args:
            context: Context name to switch to.

        Returns:
            Standardized result dictionary.
        """
        args = ["config", "use-context", context]
        return self._run_kubectl(args)

    # -------------------------------------------------------------------------
    # Monitoring
    # -------------------------------------------------------------------------

    async def _get_events(
        self,
        namespace: str | None = None,
        all_namespaces: bool = False,
        field_selector: str | None = None,
        sort_by: str = ".lastTimestamp",
        limit: int = 50
    ) -> dict[str, Any]:
        """Get cluster events.

        Args:
            namespace: Filter by namespace.
            all_namespaces: Show events from all namespaces.
            field_selector: Field selector.
            sort_by: Sort by field.
            limit: Limit number of events.

        Returns:
            Standardized result dictionary.
        """
        args = ["get", "events"]

        if all_namespaces:
            args.append("--all-namespaces")

        if field_selector:
            args.extend(["--field-selector", field_selector])

        if sort_by:
            args.extend(["--sort-by", sort_by])

        args.extend(["-o", "json"])

        result = self._run_kubectl(args, namespace=namespace)

        # Limit events if we got JSON
        if result["success"] and limit > 0:
            try:
                data = json.loads(result["output"])
                if isinstance(data, dict) and "items" in data:
                    data["items"] = data["items"][-limit:]
                    result["output"] = json.dumps(data, indent=2)
                    result["limited"] = True
            except json.JSONDecodeError:
                pass

        return result

    async def _top_pods(
        self,
        namespace: str | None = None,
        all_namespaces: bool = False,
        selector: str | None = None,
        containers: bool = False
    ) -> dict[str, Any]:
        """Show pod resource usage.

        Args:
            namespace: Filter by namespace.
            all_namespaces: Show pods from all namespaces.
            selector: Label selector.
            containers: Show container-level metrics.

        Returns:
            Standardized result dictionary.
        """
        args = ["top", "pods"]

        if all_namespaces:
            args.append("--all-namespaces")

        if selector:
            args.extend(["--selector", selector])

        if containers:
            args.append("--containers")

        return self._run_kubectl(args, namespace=namespace)

    async def _top_nodes(self, selector: str | None = None) -> dict[str, Any]:
        """Show node resource usage.

        Args:
            selector: Label selector.

        Returns:
            Standardized result dictionary.
        """
        args = ["top", "nodes"]

        if selector:
            args.extend(["--selector", selector])

        return self._run_kubectl(args)

    # -------------------------------------------------------------------------
    # Utilities
    # -------------------------------------------------------------------------

    async def _port_forward(
        self,
        resource: str,
        local_port: int,
        remote_port: int,
        namespace: str = "default",
        background: bool = False
    ) -> dict[str, Any]:
        """Forward local port to a pod or service.

        Args:
            resource: Resource (pod/name or service/name).
            local_port: Local port number.
            remote_port: Remote port number.
            namespace: Resource namespace.
            background: Run in background.

        Returns:
            Standardized result dictionary.
        """
        args = ["port-forward", resource, f"{local_port}:{remote_port}"]

        if background:
            try:
                # Start in background
                process = subprocess.Popen(
                    self._build_kubectl_args(args, namespace),
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE
                )

                # Give it a moment to fail if there's an immediate error
                import time
                time.sleep(0.5)

                if process.poll() is not None:
                    # Process already exited
                    stdout, stderr = process.communicate()
                    return {
                        "success": False,
                        "error": stderr.decode() if stderr else "Port forwarding failed",
                        "exit_code": process.returncode
                    }

                return {
                    "success": True,
                    "output": f"Port forwarding started: localhost:{local_port} -> {resource}:{remote_port} (PID: {process.pid})",
                    "pid": process.pid
                }

            except Exception as e:
                return {
                    "success": False,
                    "error": f"Failed to start port forwarding: {str(e)}"
                }
        else:
            # Run with timeout (port-forward blocks until interrupted)
            result = self._run_kubectl(args, namespace=namespace, timeout=5)

            # If it times out, that's actually success (it's running)
            if result.get("error", "").lower().startswith("command timed out"):
                return {
                    "success": True,
                    "output": f"Port forwarding active: localhost:{local_port} -> {resource}:{remote_port}"
                }

            return result
