"""OCLAWMA Skill: Terraform

Official Terraform skill for OCLAWMA providing comprehensive infrastructure
as code management capabilities including init, plan, apply, destroy, and
state management.
"""

from __future__ import annotations

import json
import os
import subprocess
from typing import Any

from oclawma.skills import LazySkill, SkillMetadata

__all__ = ["TerraformSkill"]


class TerraformSkill(LazySkill):
    """Terraform infrastructure management skill.

    This skill provides tools for managing infrastructure using Terraform.
    It wraps the Terraform CLI into simple, safe tool calls with proper
    error handling and output formatting.

    Features:
    - Lifecycle management (init, plan, apply, destroy)
    - Configuration validation and formatting
    - State management (list, show, mv, rm)
    - Workspace management
    - Resource import and tainting
    - Output management

    Example:
        >>> from oclawma.skills import SkillRegistry
        >>> registry = SkillRegistry()
        >>>
        >>> # Initialize Terraform
        >>> result = await registry.execute_tool("terraform", "init")
        >>>
        >>> # Generate plan
        >>> result = await registry.execute_tool("terraform", "plan")
        >>>
        >>> # Apply with auto-approval
        >>> result = await registry.execute_tool(
        ...     "terraform", "apply",
        ...     auto_approve=True
        ... )
    """

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

        Args:
            metadata: Skill metadata from the entry point.
        """
        super().__init__(metadata)
        self._terraform_path: str | None = None

    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_terraform(self) -> str:
        """Get terraform binary path, caching the result.

        Returns:
            Path to terraform binary.

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

    def _run_terraform(
        self,
        args: list[str],
        cwd: str | None = None,
        timeout: int = 300,
        capture_output: bool = True,
        env_vars: dict[str, str] | None = None
    ) -> dict[str, Any]:
        """Execute a terraform command.

        Args:
            args: Command arguments.
            cwd: Working directory for command.
            timeout: Command timeout in seconds.
            capture_output: Whether to capture output.
            env_vars: Additional environment variables.

        Returns:
            Standardized result dictionary.
        """
        try:
            cmd = [self._get_terraform()] + args

            # Prepare environment
            env = os.environ.copy()
            if env_vars:
                env.update(env_vars)

            result = subprocess.run(
                cmd,
                capture_output=capture_output,
                text=True,
                check=False,
                timeout=timeout,
                cwd=cwd,
                env=env
            )

            # Combine stdout and stderr for output
            output = result.stdout
            if result.stderr:
                if output:
                    output += "\n" + result.stderr
                else:
                    output = result.stderr

            # Determine success
            success = result.returncode == 0

            # Handle special exit codes for plan
            if args[0] == "plan" and result.returncode in [0, 2]:
                success = True

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

            return {
                "success": success,
                "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 terraform: {str(e)}"
            }

    def _build_var_args(self, vars: dict[str, Any]) -> list[str]:
        """Build variable arguments from dictionary.

        Args:
            vars: Variables dictionary.

        Returns:
            List of -var arguments.
        """
        args = []
        for key, value in vars.items():
            if isinstance(value, (dict, list)):
                value = json.dumps(value)
            args.extend(["-var", f"{key}={value}"])
        return args

    def _load(self) -> None:
        """Load the skill's tools."""
        self._tools = {
            # Lifecycle commands
            "init": self._init,
            "validate": self._validate,
            "plan": self._plan,
            "apply": self._apply,
            "destroy": self._destroy,

            # Configuration commands
            "fmt": self._fmt,
            "show": self._show,

            # State commands
            "state_list": self._state_list,
            "state_show": self._state_show,
            "state_mv": self._state_mv,
            "state_rm": self._state_rm,

            # Resource commands
            "import": self._import,
            "taint": self._taint,
            "untaint": self._untaint,

            # Output commands
            "output": self._output,
            "refresh": self._refresh,

            # Workspace commands
            "workspace_list": self._workspace_list,
            "workspace_select": self._workspace_select,
            "workspace_new": self._workspace_new,
            "workspace_delete": self._workspace_delete,

            # Utility commands
            "providers": self._providers,
            "version": self._version,
        }
        self._loaded = True

    # -------------------------------------------------------------------------
    # Lifecycle Commands
    # -------------------------------------------------------------------------

    async def _init(
        self,
        path: str = ".",
        backend: bool = True,
        upgrade: bool = False
    ) -> dict[str, Any]:
        """Initialize Terraform working directory."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["init", "-input=false"]

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

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

        return self._run_terraform(args, cwd=path, timeout=120)

    async def _validate(self, path: str = ".") -> dict[str, Any]:
        """Validate Terraform configuration."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["validate"]
        return self._run_terraform(args, cwd=path, timeout=60)

    async def _plan(
        self,
        path: str = ".",
        var_file: str | None = None,
        vars: dict[str, Any] | None = None,
        destroy: bool = False,
        target: str | None = None,
        detailed_exitcode: bool = True
    ) -> dict[str, Any]:
        """Generate and show execution plan."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["plan", "-input=false"]

        if detailed_exitcode:
            args.append("-detailed-exitcode")

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

        if var_file:
            args.extend(["-var-file", var_file])

        if vars:
            args.extend(self._build_var_args(vars))

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

        return self._run_terraform(args, cwd=path, timeout=300)

    async def _apply(
        self,
        path: str = ".",
        var_file: str | None = None,
        vars: dict[str, Any] | None = None,
        auto_approve: bool = False,
        target: str | None = None,
        refresh: bool = True
    ) -> dict[str, Any]:
        """Build or change infrastructure."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["apply", "-input=false"]

        if auto_approve:
            args.append("-auto-approve")

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

        if var_file:
            args.extend(["-var-file", var_file])

        if vars:
            args.extend(self._build_var_args(vars))

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

        return self._run_terraform(args, cwd=path, timeout=600)

    async def _destroy(
        self,
        path: str = ".",
        var_file: str | None = None,
        vars: dict[str, Any] | None = None,
        auto_approve: bool = False,
        target: str | None = None
    ) -> dict[str, Any]:
        """Destroy Terraform-managed infrastructure."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["destroy", "-input=false"]

        if auto_approve:
            args.append("-auto-approve")

        if var_file:
            args.extend(["-var-file", var_file])

        if vars:
            args.extend(self._build_var_args(vars))

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

        return self._run_terraform(args, cwd=path, timeout=600)

    # -------------------------------------------------------------------------
    # Configuration Commands
    # -------------------------------------------------------------------------

    async def _fmt(
        self,
        path: str = ".",
        recursive: bool = False,
        check: bool = False
    ) -> dict[str, Any]:
        """Rewrite configuration files to canonical format."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["fmt"]

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

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

        return self._run_terraform(args, cwd=path, timeout=60)

    async def _show(
        self,
        path: str = ".",
        json: bool = False
    ) -> dict[str, Any]:
        """Show Terraform state or plan."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["show"]

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

        return self._run_terraform(args, cwd=path, timeout=60)

    # -------------------------------------------------------------------------
    # State Commands
    # -------------------------------------------------------------------------

    async def _state_list(self, path: str = ".") -> dict[str, Any]:
        """List resources in state."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["state", "list"]
        return self._run_terraform(args, cwd=path, timeout=60)

    async def _state_show(
        self,
        address: str,
        path: str = "."
    ) -> dict[str, Any]:
        """Show a resource in state."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["state", "show", address]
        return self._run_terraform(args, cwd=path, timeout=60)

    async def _state_mv(
        self,
        source: str,
        destination: str,
        path: str = "."
    ) -> dict[str, Any]:
        """Move item in state."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["state", "mv", source, destination]
        return self._run_terraform(args, cwd=path, timeout=60)

    async def _state_rm(
        self,
        address: str,
        path: str = "."
    ) -> dict[str, Any]:
        """Remove items from state."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["state", "rm", address]
        return self._run_terraform(args, cwd=path, timeout=60)

    # -------------------------------------------------------------------------
    # Resource Commands
    # -------------------------------------------------------------------------

    async def _import(
        self,
        address: str,
        id: str,
        path: str = "."
    ) -> dict[str, Any]:
        """Import existing infrastructure into Terraform."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["import", address, id]
        return self._run_terraform(args, cwd=path, timeout=120)

    async def _taint(
        self,
        address: str,
        path: str = "."
    ) -> dict[str, Any]:
        """Mark a resource as tainted."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["taint", address]
        return self._run_terraform(args, cwd=path, timeout=60)

    async def _untaint(
        self,
        address: str,
        path: str = "."
    ) -> dict[str, Any]:
        """Remove taint from a resource."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["untaint", address]
        return self._run_terraform(args, cwd=path, timeout=60)

    # -------------------------------------------------------------------------
    # Output Commands
    # -------------------------------------------------------------------------

    async def _output(
        self,
        path: str = ".",
        name: str | None = None,
        json: bool = True
    ) -> dict[str, Any]:
        """Show output values from state."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["output"]

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

        if name:
            args.append(name)

        return self._run_terraform(args, cwd=path, timeout=60)

    async def _refresh(
        self,
        path: str = ".",
        var_file: str | None = None,
        vars: dict[str, Any] | None = None
    ) -> dict[str, Any]:
        """Update state file against real resources."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["refresh", "-input=false"]

        if var_file:
            args.extend(["-var-file", var_file])

        if vars:
            args.extend(self._build_var_args(vars))

        return self._run_terraform(args, cwd=path, timeout=300)

    # -------------------------------------------------------------------------
    # Workspace Commands
    # -------------------------------------------------------------------------

    async def _workspace_list(self, path: str = ".") -> dict[str, Any]:
        """List workspaces."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["workspace", "list"]
        return self._run_terraform(args, cwd=path, timeout=60)

    async def _workspace_select(
        self,
        name: str,
        path: str = "."
    ) -> dict[str, Any]:
        """Select a workspace."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["workspace", "select", name]
        return self._run_terraform(args, cwd=path, timeout=60)

    async def _workspace_new(
        self,
        name: str,
        path: str = "."
    ) -> dict[str, Any]:
        """Create a new workspace."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["workspace", "new", name]
        return self._run_terraform(args, cwd=path, timeout=60)

    async def _workspace_delete(
        self,
        name: str,
        path: str = "."
    ) -> dict[str, Any]:
        """Delete a workspace."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["workspace", "delete", name]
        return self._run_terraform(args, cwd=path, timeout=60)

    # -------------------------------------------------------------------------
    # Utility Commands
    # -------------------------------------------------------------------------

    async def _providers(self, path: str = ".") -> dict[str, Any]:
        """Show provider requirements."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["providers"]
        return self._run_terraform(args, cwd=path, timeout=60)

    async def _version(self) -> dict[str, Any]:
        """Show Terraform version."""
        try:
            self._get_terraform()
        except Exception as e:
            return {"success": False, "error": str(e)}

        args = ["version"]
        return self._run_terraform(args, timeout=60)
