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

"""Ansible connector — fleet management via Ansible playbooks.

The Ansible connector is an OrchestratorConnector: it plans a
sequence of steps (playbook runs, ad-hoc commands, fact collection)
and executes them via ansible-runner.

Credential model:
  Ansible does NOT receive credentials from the broker.  Instead,
  the connector passes a credential callback that ansible-runner's
  credential plugin calls at runtime.  This callback resolves
  credentials via the broker's CredentialResolver for each target
  host at the moment the task runs — not at planning time.

  This means:
  - The Ansible inventory file contains NO passwords or keys.
  - Each host's credentials are resolved just-in-time from the
    secrets backend (Entra, Vault, etc).
  - If a credential expires mid-playbook, the next task for that
    host will resolve a fresh credential.

Real integration:
  Library: ``ansible-runner`` (execution library)
  Auth: Per-host credential resolution via custom credential plugin
  Inventory: Dynamic inventory from Intune device cache or Bascule
    fleet registry

Stubbed in this sprint — steps return placeholder results.
"""

from __future__ import annotations

from typing import Any

from gsap_broker.connectors.base import ConnectorContext
from gsap_broker.connectors.orchestrator import (
    OrchestratorConnector,
    WorkflowPlan,
    WorkflowStep,
)
from gsap_broker.credentials.resolver import CredentialResolver


class AnsibleConnector(OrchestratorConnector):
    """Fleet management via Ansible playbooks."""

    connector_id = "ansible"
    corpus_entry_cid = "sha256:ansible-connector-v1"
    capability_mask = 0x7  # READ | PROPOSE | MUTATE
    declared_endpoints = ["ansible://*"]
    accord_template = "fleet-management"
    gsap_required = True
    chronicle_enabled = True

    def __init__(self, credential_resolver: CredentialResolver):
        super().__init__(credential_resolver)

    async def plan(
        self, operation: str, parameters: dict[str, Any], context: ConnectorContext
    ) -> WorkflowPlan:
        """Map operations to workflow plans.

        Operations:
          "playbook" → single step running the named playbook
          "adhoc"    → single step running a module on targets
          "collect"  → single step gathering facts
          "role"     → single step applying a role
        """
        targets = parameters.get("targets", [])
        if isinstance(targets, str):
            targets = [targets]

        if operation == "playbook":
            return WorkflowPlan(steps=[
                WorkflowStep(
                    name=f"playbook:{parameters.get('playbook', 'site.yml')}",
                    command=parameters.get("playbook", "site.yml"),
                    targets=targets,
                    extra_vars=parameters.get("extra_vars", {}),
                )
            ])

        if operation == "adhoc":
            return WorkflowPlan(steps=[
                WorkflowStep(
                    name=f"adhoc:{parameters.get('module', 'ping')}",
                    command=parameters.get("module", "ping"),
                    targets=targets,
                    extra_vars=parameters.get("args", {}),
                )
            ])

        if operation == "collect":
            return WorkflowPlan(steps=[
                WorkflowStep(
                    name="collect:facts",
                    command="setup",
                    targets=targets,
                    required=False,  # fact collection is best-effort
                )
            ])

        if operation == "role":
            return WorkflowPlan(steps=[
                WorkflowStep(
                    name=f"role:{parameters.get('role', '')}",
                    command=parameters.get("role", ""),
                    targets=targets,
                    extra_vars=parameters.get("extra_vars", {}),
                )
            ])

        return WorkflowPlan(steps=[
            WorkflowStep(name=f"unknown:{operation}", command=operation, targets=targets)
        ])

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

        Stubbed — actual ansible-runner integration in a future sprint.
        """
        # TODO: use ansible_runner.run() with:
        #   - playbook=step.command (for playbook operation)
        #   - module=step.command, module_args=step.extra_vars (for adhoc)
        #   - inventory from dynamic source (Intune cache, Bascule fleet)
        #   - credential plugin that calls self._resolver.resolve()
        #     per-host at runtime
        return {
            "success": True,
            "stub": True,
            "step": step.name,
            "targets": step.targets,
        }
