# Copyright 2026 Guildhouse Dev
# SPDX-License-Identifier: Apache-2.0

"""Orchestrator connector framework — multi-step workflow execution.

Orchestrator connectors manage workflows that span multiple steps
and potentially multiple targets.  Each step may acquire its own
credentials via the CredentialResolver.

Unlike ``SessionConnector`` (single target, single credential,
single command), an ``OrchestratorConnector``:
  - Plans a sequence of steps before execution
  - Executes steps in order, stopping on required-step failure
  - Reports partial results (which steps completed before failure)
  - Can target different hosts per step

Rust port note:
  ``WorkflowStep`` and ``WorkflowPlan`` map to plain structs.
  ``OrchestratorConnector`` maps to an async trait with
  ``plan()`` and ``execute_step()`` methods.
"""

from __future__ import annotations

import logging
from abc import abstractmethod
from dataclasses import dataclass, field
from typing import Any

from gsap_broker.connectors.base import ConnectorContext, ConnectorPlugin, ConnectorResult
from gsap_broker.credentials.resolver import CredentialResolver

logger = logging.getLogger(__name__)


@dataclass
class WorkflowStep:
    """A single step in a workflow plan."""

    name: str
    command: str
    targets: list[str] = field(default_factory=list)
    required: bool = True
    extra_vars: dict[str, Any] = field(default_factory=dict)


@dataclass
class WorkflowPlan:
    """Ordered sequence of steps for a workflow."""

    steps: list[WorkflowStep] = field(default_factory=list)


class OrchestratorConnector(ConnectorPlugin):
    """Base for multi-step workflow connectors (Ansible, Terraform, etc).

    Subclasses implement ``plan()`` to convert an operation + parameters
    into a ``WorkflowPlan``, and ``execute_step()`` to run each step.

    The base ``invoke()`` handles:
      - Planning the workflow
      - Executing steps in order
      - Stopping on required-step failure
      - Aggregating results with partial-completion reporting
    """

    def __init__(self, credential_resolver: CredentialResolver):
        self._resolver = credential_resolver

    async def invoke(
        self, operation: str, parameters: dict[str, Any], context: ConnectorContext
    ) -> ConnectorResult:
        try:
            plan = await self.plan(operation, parameters, context)
        except Exception as e:
            return ConnectorResult(success=False, error=f"Planning failed: {e}")

        if not plan.steps:
            return ConnectorResult(success=True, data={"steps": []})

        results: list[dict[str, Any]] = []
        for step in plan.steps:
            result = await self.execute_step(step, context)
            results.append({"step": step.name, **result})

            if not result.get("success") and step.required:
                return ConnectorResult(
                    success=False,
                    data={"completed": results, "failed_at": step.name},
                )

        return ConnectorResult(success=True, data={"steps": results})

    @abstractmethod
    async def plan(
        self, operation: str, parameters: dict[str, Any], context: ConnectorContext
    ) -> WorkflowPlan:
        """Convert an operation into a step-by-step execution plan."""
        ...

    @abstractmethod
    async def execute_step(
        self, step: WorkflowStep, context: ConnectorContext
    ) -> dict[str, Any]:
        """Execute a single workflow step.

        Returns a dict with at minimum a ``success: bool`` key.
        """
        ...

    def health_check(self) -> bool:
        return True
