"""Release Preparation Workflow

Pre-release quality gate combining health checks, security scan,
and changelog generation.

Stages:
1. health (CHEAP) - Run health checks (lint, types, tests)
2. security (CAPABLE) - Security scan summary
3. changelog (CAPABLE) - Generate changelog from commits
4. approve (PREMIUM) - Final release readiness assessment (conditional)

Copyright 2025 Smart-AI-Memory
Licensed under the Apache License, Version 2.0
"""

import json
import subprocess
from datetime import datetime
from typing import Any

from .base import BaseWorkflow, ModelTier
from .context import WorkflowContext
from .services import ParsingService, PromptService
from .step_config import WorkflowStepConfig

# Define step configurations for executor-based execution
RELEASE_PREP_STEPS = {
    "approve": WorkflowStepConfig(
        name="approve",
        task_type="final_review",  # Premium tier task
        tier_hint="premium",
        description="Assess release readiness and provide go/no-go recommendation",
        max_tokens=2000,
    ),
}


class ReleasePreparationWorkflow(BaseWorkflow):
    """Pre-release quality gate workflow.

    Combines multiple checks to determine if the codebase
    is ready for release.

    When use_security_crew=True, adds an additional crew_security stage
    that runs SecurityAuditCrew for comprehensive security analysis.

    Supports composition via ``WorkflowContext`` -- use ``default_context()``
    to get a pre-configured context with prompt and parsing services.
    """

    name = "release-prep"
    description = "Pre-release quality gate with health, security, and changelog"

    # Default stages (can be modified in __init__)
    stages = ["health", "security", "changelog", "approve"]
    tier_map = {
        "health": ModelTier.CHEAP,
        "security": ModelTier.CAPABLE,
        "changelog": ModelTier.CAPABLE,
        "approve": ModelTier.PREMIUM,
    }

    def __init__(
        self,
        skip_approve_if_clean: bool = True,
        use_security_crew: bool = False,
        crew_config: dict | None = None,
        enable_auth_strategy: bool = True,
        **kwargs: Any,
    ):
        """Initialize release preparation workflow.

        Args:
            skip_approve_if_clean: Skip premium approval if all checks pass
            use_security_crew: Enable SecurityAuditCrew for comprehensive security audit
            crew_config: Configuration dict for SecurityAuditCrew
            enable_auth_strategy: Enable intelligent auth routing (default: True)
            **kwargs: Additional arguments passed to BaseWorkflow

        """
        super().__init__(**kwargs)
        self.skip_approve_if_clean = skip_approve_if_clean
        self.use_security_crew = use_security_crew
        self.crew_config = crew_config or {}
        self.enable_auth_strategy = enable_auth_strategy
        self._has_blockers: bool = False
        self._auth_mode_used: str | None = None

        # Dynamically configure stages based on security crew setting
        if use_security_crew:
            self.stages = ["health", "security", "crew_security", "changelog", "approve"]
            self.tier_map = {
                "health": ModelTier.CHEAP,
                "security": ModelTier.CAPABLE,
                "crew_security": ModelTier.PREMIUM,
                "changelog": ModelTier.CAPABLE,
                "approve": ModelTier.PREMIUM,
            }
        else:
            self.stages = ["health", "security", "changelog", "approve"]
            self.tier_map = {
                "health": ModelTier.CHEAP,
                "security": ModelTier.CAPABLE,
                "changelog": ModelTier.CAPABLE,
                "approve": ModelTier.PREMIUM,
            }

    @classmethod
    def default_context(cls, xml_config: dict | None = None) -> WorkflowContext:
        """Create a WorkflowContext pre-configured for release preparation.

        Args:
            xml_config: Optional XML prompt configuration dict.

        Returns:
            WorkflowContext with prompt and parsing services.

        """
        return WorkflowContext(
            prompt=PromptService("release-prep", xml_config=xml_config),
            parsing=ParsingService(xml_config=xml_config),
        )

    def should_skip_stage(self, stage_name: str, input_data: Any) -> tuple[bool, str | None]:
        """Skip approval if all checks pass cleanly.

        Args:
            stage_name: Name of the stage to check
            input_data: Current workflow data

        Returns:
            Tuple of (should_skip, reason)

        """
        if stage_name == "approve" and self.skip_approve_if_clean:
            if not self._has_blockers:
                return True, "All checks passed - auto-approved"
        return False, None

    async def run_stage(
        self,
        stage_name: str,
        tier: ModelTier,
        input_data: Any,
    ) -> tuple[Any, int, int]:
        """Route to specific stage implementation."""
        if stage_name == "health":
            return await self._health(input_data, tier)
        if stage_name == "security":
            return await self._security(input_data, tier)
        if stage_name == "crew_security":
            return await self._crew_security(input_data, tier)
        if stage_name == "changelog":
            return await self._changelog(input_data, tier)
        if stage_name == "approve":
            return await self._approve(input_data, tier)
        raise ValueError(f"Unknown stage: {stage_name}")

    async def _health(self, input_data: dict, tier: ModelTier) -> tuple[dict, int, int]:
        """Run health checks.

        Executes lint, type checking, and tests.
        """
        target_path = input_data.get("path", ".")

        # === AUTH STRATEGY INTEGRATION ===
        if self.enable_auth_strategy:
            try:
                import logging
                from pathlib import Path

                from attune.models import (
                    count_lines_of_code,
                    get_auth_strategy,
                    get_module_size_category,
                )

                logger = logging.getLogger(__name__)

                # Calculate total LOC for project/directory
                target = Path(target_path)
                total_lines = 0
                if target.is_file():
                    total_lines = count_lines_of_code(target)
                elif target.is_dir():
                    for py_file in target.rglob("*.py"):
                        try:
                            total_lines += count_lines_of_code(py_file)
                        except Exception:
                            pass

                if total_lines > 0:
                    strategy = get_auth_strategy()
                    recommended_mode = strategy.get_recommended_mode(total_lines)
                    self._auth_mode_used = recommended_mode.value

                    size_category = get_module_size_category(total_lines)
                    logger.info(
                        f"Release prep target: {target_path} ({total_lines:,} LOC, {size_category})"
                    )
                    logger.info(f"Recommended auth mode: {recommended_mode.value}")

                    cost_estimate = strategy.estimate_cost(total_lines, recommended_mode)
                    if recommended_mode.value == "subscription":
                        logger.info(f"Cost: {cost_estimate['quota_cost']}")
                    else:
                        logger.info(f"Cost: ~${cost_estimate['monetary_cost']:.4f}")

            except Exception as e:
                import logging

                logger = logging.getLogger(__name__)
                logger.warning(f"Auth strategy detection failed: {e}")

        checks: dict[str, dict] = {}

        # Lint check (ruff)
        try:
            result = subprocess.run(
                ["python", "-m", "ruff", "check", target_path],
                check=False,
                capture_output=True,
                text=True,
                timeout=60,
            )
            lint_errors = result.stdout.count("error") + result.stderr.count("error")
            checks["lint"] = {
                "passed": result.returncode == 0,
                "errors": lint_errors,
                "tool": "ruff",
            }
        except (subprocess.TimeoutExpired, FileNotFoundError):
            checks["lint"] = {"passed": True, "errors": 0, "tool": "ruff", "skipped": True}

        # Type check (mypy)
        try:
            result = subprocess.run(
                ["python", "-m", "mypy", target_path, "--ignore-missing-imports"],
                check=False,
                capture_output=True,
                text=True,
                timeout=120,
            )
            type_errors = result.stdout.count("error:")
            checks["types"] = {
                "passed": result.returncode == 0,
                "errors": type_errors,
                "tool": "mypy",
            }
        except (subprocess.TimeoutExpired, FileNotFoundError):
            checks["types"] = {"passed": True, "errors": 0, "tool": "mypy", "skipped": True}

        # Test check (pytest)
        try:
            result = subprocess.run(
                ["python", "-m", "pytest", "--co", "-q"],
                check=False,
                capture_output=True,
                text=True,
                timeout=60,
                cwd=target_path,
            )
            # Count collected tests
            test_count = 0
            for line in result.stdout.splitlines():
                if "test" in line.lower():
                    test_count += 1

            checks["tests"] = {
                "passed": True,
                "test_count": test_count,
                "tool": "pytest",
            }
        except (subprocess.TimeoutExpired, FileNotFoundError):
            checks["tests"] = {"passed": True, "test_count": 0, "tool": "pytest", "skipped": True}

        # Calculate health score
        failed_checks = [k for k, v in checks.items() if not v.get("passed", True)]
        health_score = 100 - (len(failed_checks) * 20)

        if failed_checks:
            self._has_blockers = True

        health_result = {
            "checks": checks,
            "health_score": max(0, health_score),
            "failed_checks": failed_checks,
            "passed": len(failed_checks) == 0,
        }

        input_tokens = len(str(input_data)) // 4
        output_tokens = len(str(health_result)) // 4

        return (
            {
                "health": health_result,
                **input_data,
            },
            input_tokens,
            output_tokens,
        )

    async def _security(self, input_data: dict, tier: ModelTier) -> tuple[dict, int, int]:
        """Run security scan using Bandit.

        Uses industry-standard Bandit tool for security analysis.
        """
        target_path = input_data.get("path", ".")

        issues: list[dict] = []
        high_count = 0
        medium_count = 0

        # Run Bandit security scanner
        try:
            result = subprocess.run(
                [
                    "python",
                    "-m",
                    "bandit",
                    "-r",
                    target_path,
                    "--severity-level",
                    "medium",
                    "--format",
                    "json",
                ],
                check=False,
                capture_output=True,
                text=True,
                timeout=120,
            )

            # Parse Bandit JSON output
            if result.stdout:
                try:
                    bandit_data = json.loads(result.stdout)
                    bandit_results = bandit_data.get("results", [])

                    for finding in bandit_results:
                        severity = finding.get("issue_severity", "LOW").lower()
                        issues.append(
                            {
                                "type": finding.get("test_id", "unknown"),
                                "file": finding.get("filename", "unknown"),
                                "line": finding.get("line_number", 0),
                                "severity": severity,
                                "message": finding.get("issue_text", ""),
                                "confidence": finding.get("issue_confidence", ""),
                            }
                        )

                        if severity == "high":
                            high_count += 1
                        elif severity == "medium":
                            medium_count += 1

                except json.JSONDecodeError:
                    # If JSON parsing fails, fall back to error count from stderr
                    pass

        except (subprocess.TimeoutExpired, FileNotFoundError):
            # Bandit not available or timed out - skip security scan
            pass

        if high_count > 0:
            self._has_blockers = True

        security_result = {
            "issues": issues[:20],  # Top 20
            "total_issues": len(issues),
            "high_severity": high_count,
            "medium_severity": medium_count,
            "passed": high_count == 0,
            "tool": "bandit",
        }

        input_tokens = len(str(input_data)) // 4
        output_tokens = len(str(security_result)) // 4

        return (
            {
                "security": security_result,
                **input_data,
            },
            input_tokens,
            output_tokens,
        )

    async def _crew_security(self, input_data: dict, tier: ModelTier) -> tuple[dict, int, int]:
        """Run SecurityAuditCrew for comprehensive security analysis.

        This stage uses the 5-agent SecurityAuditCrew for deep security
        analysis including vulnerability hunting, risk assessment,
        remediation planning, and compliance mapping.

        Falls back gracefully if SecurityAuditCrew is not available.
        """
        try:
            from .security_adapters import (
                _check_crew_available,
                _get_crew_audit,
                crew_report_to_workflow_format,
                merge_security_results,
            )
        except ImportError:
            # Security adapters removed - return fallback
            return (
                {
                    "crew_security": {
                        "available": False,
                        "fallback": True,
                        "reason": "Security adapters not installed",
                    },
                    **input_data,
                },
                0,
                0,
            )

        target_path = input_data.get("path", ".")
        existing_security = input_data.get("security", {})

        # Check if crew is available
        if not _check_crew_available():
            return (
                {
                    "crew_security": {
                        "available": False,
                        "fallback": True,
                        "reason": "SecurityAuditCrew not installed",
                    },
                    **input_data,
                },
                0,
                0,
            )

        # Run SecurityAuditCrew
        report = await _get_crew_audit(target_path, self.crew_config)

        if report is None:
            return (
                {
                    "crew_security": {
                        "available": True,
                        "fallback": True,
                        "reason": "SecurityAuditCrew audit failed or timed out",
                    },
                    **input_data,
                },
                0,
                0,
            )

        # Convert crew report to workflow format
        crew_results = crew_report_to_workflow_format(report)

        # Merge with existing security stage results
        existing_issues = existing_security.get("issues", [])
        merged = merge_security_results(crew_results, {"findings": existing_issues})

        # Update blockers based on crew findings
        critical_count = len(crew_results.get("assessment", {}).get("critical_findings", []))
        high_count = len(crew_results.get("assessment", {}).get("high_findings", []))

        if critical_count > 0 or high_count > 0:
            self._has_blockers = True

        crew_security_result = {
            "available": True,
            "fallback": False,
            "findings": crew_results.get("findings", []),
            "finding_count": crew_results.get("finding_count", 0),
            "risk_score": crew_results.get("risk_score", 0),
            "risk_level": crew_results.get("risk_level", "none"),
            "critical_count": critical_count,
            "high_count": high_count,
            "summary": crew_results.get("summary", ""),
            "agents_used": crew_results.get("agents_used", []),
            "merged_results": merged,
        }

        # Estimate tokens (crew uses internal LLM calls)
        input_tokens = len(str(input_data)) // 4
        output_tokens = len(str(crew_security_result)) // 4

        return (
            {
                "crew_security": crew_security_result,
                **input_data,
            },
            input_tokens,
            output_tokens,
        )

    async def _changelog(self, input_data: dict, tier: ModelTier) -> tuple[dict, int, int]:
        """Generate changelog from recent commits.

        Extracts commit messages and organizes by type.
        """
        target_path = input_data.get("path", ".")
        since = input_data.get("since", "1 week ago")

        commits: list[dict] = []

        try:
            result = subprocess.run(
                ["git", "log", f"--since={since}", "--oneline", "--no-merges"],
                check=False,
                capture_output=True,
                text=True,
                timeout=30,
                cwd=target_path,
            )

            for line in result.stdout.splitlines():
                if not line.strip():
                    continue

                parts = line.split(" ", 1)
                if len(parts) >= 2:
                    sha = parts[0]
                    message = parts[1]

                    # Categorize by conventional commit prefix
                    category = "other"
                    if message.startswith("feat"):
                        category = "features"
                    elif message.startswith("fix"):
                        category = "fixes"
                    elif message.startswith("docs"):
                        category = "docs"
                    elif message.startswith("refactor"):
                        category = "refactor"
                    elif message.startswith("test"):
                        category = "tests"
                    elif message.startswith("chore"):
                        category = "chores"

                    commits.append(
                        {
                            "sha": sha,
                            "message": message,
                            "category": category,
                        },
                    )
        except (subprocess.TimeoutExpired, FileNotFoundError):
            pass

        # Group by category
        by_category: dict[str, list] = {}
        for commit in commits:
            cat = commit["category"]
            if cat not in by_category:
                by_category[cat] = []
            by_category[cat].append(commit)

        changelog = {
            "commits": commits,
            "total_commits": len(commits),
            "by_category": {k: len(v) for k, v in by_category.items()},
            "generated_at": datetime.now().isoformat(),
            "period": since,
        }

        input_tokens = len(str(input_data)) // 4
        output_tokens = len(str(changelog)) // 4

        return (
            {
                "changelog": changelog,
                **input_data,
            },
            input_tokens,
            output_tokens,
        )

    async def _approve(self, input_data: dict, tier: ModelTier) -> tuple[dict, int, int]:
        """Final release readiness assessment using LLM.

        Synthesizes all checks into go/no-go recommendation.

        Supports XML-enhanced prompts when enabled in workflow config.
        """
        health = input_data.get("health", {})
        security = input_data.get("security", {})
        changelog = input_data.get("changelog", {})
        target = input_data.get("path", "")

        # Gather blockers
        blockers: list[str] = []

        if not health.get("passed", False):
            for check in health.get("failed_checks", []):
                blockers.append(f"Health check failed: {check}")

        if not security.get("passed", False):
            blockers.append(f"Security issues: {security.get('high_severity', 0)} high severity")

        if changelog.get("total_commits", 0) == 0:
            blockers.append("No commits in release period")

        # Gather warnings
        warnings: list[str] = []

        if security.get("medium_severity", 0) > 0:
            warnings.append(f"{security.get('medium_severity')} medium security issues")

        test_count = health.get("checks", {}).get("tests", {}).get("test_count", 0)
        if test_count < 10:
            warnings.append(f"Low test count: {test_count}")

        # Build input payload for LLM
        input_payload = f"""Target: {target or "codebase"}

Health Score: {health.get("health_score", 0)}/100
Health Checks: {json.dumps(health.get("checks", {}), indent=2)}

Security Issues: {security.get("total_issues", 0)}
High Severity: {security.get("high_severity", 0)}
Medium Severity: {security.get("medium_severity", 0)}

Commit Count: {changelog.get("total_commits", 0)}
Changes by Category: {json.dumps(changelog.get("by_category", {}), indent=2)}

Blockers: {json.dumps(blockers, indent=2)}
Warnings: {json.dumps(warnings, indent=2)}"""

        # Check if XML prompts are enabled
        if self._is_xml_enabled():
            # Use XML-enhanced prompt
            from attune.prompts.examples import RELEASE_PREP_EXAMPLES

            user_message = self._render_xml_prompt(
                role="release manager assessing release readiness",
                goal="Provide a comprehensive release readiness assessment",
                instructions=[
                    "Evaluate all health checks and their implications",
                    "Assess security findings and their risk level",
                    "Review the changelog for completeness",
                    "Identify any blockers that must be resolved",
                    "Provide a clear go/no-go recommendation",
                    "Suggest remediation steps for any issues",
                ],
                constraints=[
                    "Be conservative - flag potential issues",
                    "Provide clear, actionable feedback",
                    "Include confidence level in recommendation",
                ],
                input_type="release_checks",
                input_payload=input_payload,
                examples=RELEASE_PREP_EXAMPLES,
                extra={
                    "blocker_count": len(blockers),
                    "warning_count": len(warnings),
                },
            )
            system = None  # XML prompt includes all context
        else:
            # Use legacy plain text prompts
            system = """You are a release manager assessing release readiness.
Analyze the health checks, security findings, and changelog to provide
a clear go/no-go recommendation.

Be thorough and flag any potential issues."""

            user_message = f"""Assess release readiness:

{input_payload}

Provide a comprehensive release readiness assessment."""

        # Try executor-based execution first (Phase 3 pattern)
        if self._executor is not None or self._api_key:
            try:
                step = RELEASE_PREP_STEPS["approve"]
                response, input_tokens, output_tokens, cost = await self.run_step_with_executor(
                    step=step,
                    prompt=user_message,
                    system=system,
                )
            except Exception:
                # Fall back to legacy _call_llm if executor fails
                response, input_tokens, output_tokens = await self._call_llm(
                    tier,
                    system or "",
                    user_message,
                    max_tokens=2000,
                )
        else:
            # Legacy path for backward compatibility
            response, input_tokens, output_tokens = await self._call_llm(
                tier,
                system or "",
                user_message,
                max_tokens=2000,
            )

        # Parse XML response if enforcement is enabled
        parsed_data = self._parse_xml_response(response)

        # Make decision
        approved = len(blockers) == 0
        confidence = "high" if approved and len(warnings) == 0 else "medium" if approved else "low"

        result = {
            "approved": approved,
            "confidence": confidence,
            "blockers": blockers,
            "warnings": warnings,
            "health_score": health.get("health_score", 0),
            "commit_count": changelog.get("total_commits", 0),
            "assessment": response,
            "recommendation": (
                "Ready for release" if approved else "Address blockers before release"
            ),
            "model_tier_used": tier.value,
        }

        # Include auth mode used for telemetry
        if self._auth_mode_used:
            result["auth_mode_used"] = self._auth_mode_used

        # Merge parsed XML data if available
        if parsed_data.get("xml_parsed"):
            result.update(
                {
                    "xml_parsed": True,
                    "summary": parsed_data.get("summary"),
                    "findings": parsed_data.get("findings", []),
                    "checklist": parsed_data.get("checklist", []),
                },
            )

        # Add formatted report for human readability
        result["formatted_report"] = format_release_prep_report(result, input_data)

        return (result, input_tokens, output_tokens)


def format_release_prep_report(result: dict, input_data: dict) -> str:
    """Format release preparation output as a human-readable report.

    Args:
        result: The approve stage result
        input_data: Input data from previous stages

    Returns:
        Formatted report string

    """
    lines = []

    # Header with approval status
    approved = result.get("approved", False)
    confidence = result.get("confidence", "unknown").upper()

    if approved:
        status_icon = "✅"
        status_text = "READY FOR RELEASE"
    else:
        status_icon = "❌"
        status_text = "NOT READY"

    lines.append("=" * 60)
    lines.append("RELEASE PREPARATION REPORT")
    lines.append("=" * 60)
    lines.append("")
    lines.append(f"Status: {status_icon} {status_text}")
    lines.append(f"Confidence: {confidence}")
    lines.append(f"Recommendation: {result.get('recommendation', 'N/A')}")
    lines.append("")

    # Health checks
    health = input_data.get("health", {})
    health_score = health.get("health_score", 0)
    checks = health.get("checks", {})

    lines.append("-" * 60)
    lines.append("HEALTH CHECKS")
    lines.append("-" * 60)
    lines.append(f"Health Score: {health_score}/100")
    lines.append("")

    for check_name, check_data in checks.items():
        passed = check_data.get("passed", False)
        skipped = check_data.get("skipped", False)
        tool = check_data.get("tool", "unknown")

        if skipped:
            icon = "⏭️"
            status = "SKIPPED"
        elif passed:
            icon = "✅"
            status = "PASSED"
        else:
            icon = "❌"
            status = "FAILED"

        errors = check_data.get("errors", 0)
        extra = f" ({errors} errors)" if errors else ""
        lines.append(f"  {icon} {check_name.upper()} ({tool}): {status}{extra}")
    lines.append("")

    # Security summary
    security = input_data.get("security", {})
    if security:
        lines.append("-" * 60)
        lines.append("SECURITY SCAN")
        lines.append("-" * 60)
        total_issues = security.get("total_issues", 0)
        high = security.get("high_severity", 0)
        medium = security.get("medium_severity", 0)
        passed = security.get("passed", True)

        if passed:
            lines.append("✅ No high severity issues found")
        else:
            lines.append(f"❌ {high} high severity issues found")

        lines.append(f"Total Issues: {total_issues}")
        lines.append(f"  🔴 High: {high}")
        lines.append(f"  🟡 Medium: {medium}")
        lines.append("")

    # Changelog summary
    changelog = input_data.get("changelog", {})
    if changelog:
        lines.append("-" * 60)
        lines.append("CHANGELOG")
        lines.append("-" * 60)
        commit_count = changelog.get("total_commits", 0)
        by_category = changelog.get("by_category", {})
        period = changelog.get("period", "unknown")

        lines.append(f"Period: {period}")
        lines.append(f"Total Commits: {commit_count}")
        if by_category:
            lines.append("By Category:")
            for cat, count in by_category.items():
                lines.append(f"  • {cat}: {count}")
        lines.append("")

    # Blockers
    blockers = result.get("blockers", [])
    if blockers:
        lines.append("-" * 60)
        lines.append("🚫 BLOCKERS")
        lines.append("-" * 60)
        for blocker in blockers:
            lines.append(f"  • {blocker}")
        lines.append("")

    # Warnings
    warnings = result.get("warnings", [])
    if warnings:
        lines.append("-" * 60)
        lines.append("⚠️  WARNINGS")
        lines.append("-" * 60)
        for warning in warnings:
            lines.append(f"  • {warning}")
        lines.append("")

    # LLM Assessment
    assessment = result.get("assessment", "")
    if assessment and not assessment.startswith("[Simulated"):
        lines.append("-" * 60)
        lines.append("DETAILED ASSESSMENT")
        lines.append("-" * 60)
        if len(assessment) > 1500:
            lines.append(assessment[:1500] + "...")
        else:
            lines.append(assessment)
        lines.append("")

    # Footer
    lines.append("=" * 60)
    model_tier = result.get("model_tier_used", "unknown")
    lines.append(f"Assessed using {model_tier} tier model")
    lines.append("=" * 60)

    return "\n".join(lines)


def main():
    """CLI entry point for release preparation workflow."""
    import asyncio

    async def run():
        workflow = ReleasePreparationWorkflow()
        result = await workflow.execute(path=".")

        print("\nRelease Preparation Results")
        print("=" * 50)
        print(f"Provider: {result.provider}")
        print(f"Success: {result.success}")

        output = result.final_output
        print(f"Approved: {output.get('approved', False)}")
        print(f"Confidence: {output.get('confidence', 'N/A')}")

        if output.get("blockers"):
            print("\nBlockers:")
            for b in output["blockers"]:
                print(f"  - {b}")

        if output.get("warnings"):
            print("\nWarnings:")
            for w in output["warnings"]:
                print(f"  - {w}")

        print("\nCost Report:")
        print(f"  Total Cost: ${result.cost_report.total_cost:.4f}")
        savings = result.cost_report.savings
        pct = result.cost_report.savings_percent
        print(f"  Savings: ${savings:.4f} ({pct:.1f}%)")

    asyncio.run(run())


if __name__ == "__main__":
    main()
